C++ port: update test binary with more flags and add barcode format names next to...
[zxing.git] / cpp / magick / src / main.cpp
index ce5f877..ceb9cfc 100644 (file)
@@ -2,8 +2,7 @@
  *  main.cpp
  *  zxing
  *
- *  Created by Ralf Kistner on 16/10/2009.
- *  Copyright 2008 ZXing authors All rights reserved.
+ *  Copyright 2010 ZXing authors All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <Magick++.h>
 #include "MagickBitmapSource.h"
 #include <zxing/common/Counted.h>
-#include <zxing/qrcode/QRCodeReader.h>
+//#include <zxing/qrcode/QRCodeReader.h>
+#include <zxing/Binarizer.h>
+#include <zxing/MultiFormatReader.h>
 #include <zxing/Result.h>
 #include <zxing/ReaderException.h>
 #include <zxing/common/GlobalHistogramBinarizer.h>
-#include <zxing/common/LocalBlockBinarizer.h>
+#include <zxing/common/HybridBinarizer.h>
 #include <exception>
 #include <zxing/Exception.h>
 #include <zxing/common/IllegalArgumentException.h>
 #include <zxing/BinaryBitmap.h>
+#include <zxing/DecodeHints.h>
 
-#include <zxing/qrcode/detector/Detector.h>
-#include <zxing/qrcode/decoder/Decoder.h>
+//#include <zxing/qrcode/detector/Detector.h>
+//#include <zxing/qrcode/detector/QREdgeDetector.h>
+//#include <zxing/qrcode/decoder/Decoder.h>
 
 using namespace Magick;
 using namespace std;
 using namespace zxing;
-using namespace zxing::qrcode;
-
-void draw_matrix(Image& image, Ref<BitMatrix> matrix) {
-  int width = matrix->getWidth();
-  int height = matrix->getHeight();
-//     image.modifyImage();
-//     image.type(TrueColorType);
-
-  PixelPacket* pixels = image.getPixels(0, 0, width, height);
-
-  PixelPacket* pixel = pixels;
-  ColorMono color;
-  for (int y = 0; y < height; y++) {
-    for (int x = 0; x < width; x++) {
-      color.mono(!matrix->get(x, y));
-      *pixel = color;
-      pixel++;
-    }
-  }
-  image.syncPixels();
-}
-
-void save_matrix(Ref<BitMatrix> matrix, string filename, float scale = 1.0) {
-  Image image(Geometry(matrix->getWidth(), matrix->getHeight()), Color(MaxRGB, MaxRGB, MaxRGB, 0));
-  int width = matrix->getWidth();
-  int height = matrix->getHeight();
-  draw_matrix(image, matrix);
-  image.scale(Geometry(width*scale, height*scale));
-  image.write(filename);
-}
-
-void save_grid(Ref<BitMatrix> matrix, string filename, Ref<PerspectiveTransform> transform, int dimension) {
-  Image image(Geometry(matrix->getWidth(), matrix->getHeight()), Color(MaxRGB, MaxRGB, MaxRGB, 0));
-
-  draw_matrix(image, matrix);
+//using namespace zxing::qrcode;
 
-  image.strokeColor(Color(MaxRGB, 0, 0, MaxRGB / 3));
-  image.fillColor(Color(0, 0, 0, MaxRGB));
-  image.strokeWidth(1);
+static bool raw_dump = false;
+static bool show_format = false;
+static bool tryHarder = false;
+static bool show_filename = false;
 
-  for (int i = 0; i <= dimension; i++) {
-    valarray<float> tpoints(0.0, 4);
+static const int MAX_EXPECTED = 1024;
 
-    tpoints[0] = 0;
-    tpoints[1] = i;
-    tpoints[2] = dimension;
-    tpoints[3] = i;
-    transform->transformPoints(tpoints);
-
-    DrawableLine line1(tpoints[0], tpoints[1], tpoints[2], tpoints[3]);
-    image.draw(line1);
-
-    tpoints[0] = i;
-    tpoints[1] = 0;
-    tpoints[2] = i;
-    tpoints[3] = dimension;
-    transform->transformPoints(tpoints);
-
-    DrawableLine line2(tpoints[0], tpoints[1], tpoints[2], tpoints[3]);
-    image.draw(line2);
-  }
-
-  image.write(filename);
+Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints) {
+  Ref<Reader> reader(new MultiFormatReader);
+  return Ref<Result> (new Result(*reader->decode(image, hints)));
 }
 
-Ref<Result> decode(string out_prefix, Ref<BinaryBitmap> image, string& cell_grid, string& cell_transformed) {
-  Decoder decoder;
-
-  Detector detector(image->getBlackMatrix());
-
-  Ref<DetectorResult> detectorResult(detector.detect());
 
-  if (out_prefix.size()) {
-    // Grid image
-    string gridfile = out_prefix + ".grid.gif";
-    Ref<PerspectiveTransform> transform = detectorResult->getTransform();
-    int dimension = detectorResult->getBits()->getDimension();
-    save_grid(image->getBlackMatrix(), gridfile, transform, dimension);
-    cell_grid = "<img src=\"" + gridfile + "\" />";
+int test_image(Image& image, bool hybrid, string expected = "") {
 
-    // Transformed image
-    string tfile = out_prefix + ".transformed.png";
-    save_matrix(detectorResult->getBits(), tfile, 5);
-    cell_transformed = "<img src=\"" + tfile + "\" />";
-  }
-
-
-  vector<Ref<ResultPoint> > points(detectorResult->getPoints());
-
-  Ref<DecoderResult> decoderResult(decoder.decode(detectorResult->getBits()));
-
-  Ref<Result> result(new Result(decoderResult->getText(),
-                                decoderResult->getRawBytes(),
-                                points,
-                                BarcodeFormat_QR_CODE));
-
-  return result;
-}
-
-
-
-
-int test_image(Image& image, string out_prefix, bool localized) {
-  string cell_mono;
-  string cell_transformed;
   string cell_result;
-  string cell_grid;
   int res = -1;
 
   Ref<BitMatrix> matrix(NULL);
   Ref<Binarizer> binarizer(NULL);
-
+  const char* result_format = "";
 
   try {
     Ref<MagickBitmapSource> source(new MagickBitmapSource(image));
 
-    if (localized) {
-      binarizer = new LocalBlockBinarizer(source);
+    if (hybrid) {
+      binarizer = new HybridBinarizer(source);
     } else {
       binarizer = new GlobalHistogramBinarizer(source);
     }
 
-    if (out_prefix.size()) {
-      string monofile = out_prefix + ".mono.png";
-      matrix = binarizer->getBlackMatrix();
-      save_matrix(matrix, monofile);
-      cell_mono = "<img src=\"" + monofile + "\" />";
-    }
-
+    DecodeHints hints(hints.DEFAULT_HINTS);
+    hints.setTryHarder(tryHarder);
     Ref<BinaryBitmap> binary(new BinaryBitmap(binarizer));
-    Ref<Result> result(decode(out_prefix, binary, cell_grid, cell_transformed));
-    cell_result = "<font color=\"blue\"><b>" + result->getText()->getText() + "</b></font>";
+    Ref<Result> result(decode(binary, hints));
+    cell_result = result->getText()->getText();
+    result_format = barcodeFormatNames[result->getBarcodeFormat()];
     res = 0;
   } catch (ReaderException e) {
     cell_result = "zxing::ReaderException: " + string(e.what());
@@ -187,39 +97,107 @@ int test_image(Image& image, string out_prefix, bool localized) {
     res = -5;
   }
 
-  cout << "<td>" << cell_mono << "</td>" << endl;
-  cout << "<td>" << cell_grid << "</td>" << endl;
-  cout << "<td>" << cell_transformed << "</td>" << endl;
-  cout << "<td>" << cell_result << "</td>" << endl;
+  if (cell_result.compare(expected)) {
+    res = -6;
+    if (!raw_dump) {
+        cout << (hybrid ? "Hybrid" : "Global") << " binarizer failed:\n";
+        if (expected.length() >= 0) {
+          cout << "  Expected: " << expected << "\n";
+        }
+        cout << "  Detected: " << cell_result << endl;
+    }
+  }
+
+
+  if (raw_dump && !hybrid) {/* don't print twice, and global is a bit better */
+    cout << cell_result;
+    if (show_format) {
+      cout << " " << result_format;
+    }
+    cout << endl;
+
+  }
   return res;
 }
 
-int test_image_local(Image& image, string out_prefix) {
-  return test_image(image, out_prefix, true);
+int test_image_hybrid(Image& image, string expected = "") {
+  return test_image(image, true, expected);
 }
 
-int test_image_global(Image& image, string out_prefix) {
-  return test_image(image, out_prefix, false);
+int test_image_global(Image& image, string expected = "") {
+  return test_image(image, false, expected);
 }
 
+string get_expected(string imagefilename) {
+  string textfilename = imagefilename;
+  int dotpos = textfilename.rfind(".");
+  textfilename.replace(dotpos+1, textfilename.length() - dotpos - 1, "txt");
+  char data[MAX_EXPECTED];
+  FILE *fp = fopen(textfilename.data(), "rb");
+    
+  // get file size
+  fseek(fp, 0, SEEK_END);
+  int toread = ftell(fp);
+  rewind(fp);
+  
+  if (toread > MAX_EXPECTED) {
+       cerr << "MAX_EXPECTED = " << MAX_EXPECTED << " but file '" << textfilename << "' has " << toread
+            << " bytes! Skipping..." << endl;
+    fclose(fp);
+    return "";
+  }
+  
+  int nread = fread(data, sizeof(char), toread, fp);
+  if (nread != toread) {
+    cerr << "Could not read entire contents of file '" << textfilename << "'! Skipping..." << endl;
+    fclose(fp);
+    return "";
+  }
+  fclose(fp);
+  data[nread] = '\0';
+  string expected(data);
+  return expected;
+}
 
 int main(int argc, char** argv) {
-  if (argc <= 2) {
-    cout << "Usage: " << argv[0] << " [<outfolder> | \"-\"] <filename1> [<filename2> ...]" << endl;
+  if (argc <= 1) {
+    cout << "Usage: " << argv[0] << " [--dump-raw] [--show-format] [--try-harder] [--show-filename] <filename1> [<filename2> ...]" << endl;
     return 1;
   }
-  string outfolder = argv[1];
 
-  int total = argc - 2;
+  int total = 0;
   int gonly = 0;
-  int lonly = 0;
+  int honly = 0;
   int both = 0;
   int neither = 0;
 
-  cout << "<html><body><table>" << endl;
-  for (int i = 2; i < argc; i++) {
+  if (argc == 2) raw_dump = true;
+
+  for (int i = 1; i < argc; i++) {
     string infilename = argv[i];
-    cerr << "Processing: " << infilename << endl;
+    if (infilename.substr(infilename.length()-3,3).compare("txt") == 0) {
+      continue;
+    }
+    if (infilename.compare("--dump-raw") == 0) {
+      raw_dump = true;
+      continue;
+    }
+    if (infilename.compare("--show-format") == 0) {
+      show_format = true;
+      continue;
+    }
+    if (infilename.compare("--try-harder") == 0) {
+      tryHarder = true;
+      continue;
+    }
+    if (infilename.compare("--show-filename") == 0) {
+      show_filename = true;
+      continue;
+    }
+    if (!raw_dump)
+      cerr << "Processing: " << infilename << endl;
+    if (show_filename)
+      cout << infilename << " ";
     Image image;
     try {
       image.read(infilename);
@@ -227,46 +205,31 @@ int main(int argc, char** argv) {
       cerr << "Unable to open image, ignoring" << endl;
       continue;
     }
-    cout << "<tr><td colspan=\"3\">" << infilename << "</td></tr>" << endl;
-    cout << "<tr>" << endl;
-
-    cout << "<td><img src=\"" << infilename << "\" /></td>" << endl;
 
+    string expected;
+    expected = get_expected(infilename);
 
     int gresult = 1;
-    int lresult = 1;
+    int hresult = 1;
 
-    if (outfolder == string("-")) {
-      gresult = test_image_global(image, "");
-      lresult = test_image_local(image, "");
-    } else {
-      replace(infilename.begin(), infilename.end(), '/', '_');
-      string prefix = string(outfolder) + string("/") + infilename;
-      gresult = test_image_global(image, prefix + ".g");
-      lresult = test_image_local(image, prefix + ".l");
-    }
+    hresult = test_image_hybrid(image, expected);
+    gresult = test_image_global(image, expected);
 
     gresult = gresult == 0;
-    lresult = lresult == 0;
-
-    gonly += gresult && !lresult;
-    lonly += lresult && !gresult;
-    both += gresult && lresult;
-    neither += !gresult && !lresult;
+    hresult = hresult == 0;
 
-    cout << "</tr>" << endl;
+    gonly += gresult && !hresult;
+    honly += hresult && !gresult;
+    both += gresult && hresult;
+    neither += !gresult && !hresult;
+    total = total + 1;
   }
-  cout << "</table>" << endl;
-
-  cout << "<table>" << endl;
-  cout << "<tr><td>Total</td><td>" << total << "</td></tr>" << endl;
-  cout << "<tr><td>Both correct</td><td>" << both << "</td></tr>" << endl;
-  cout << "<tr><td>Neither correct</td><td>" << neither << "</td></tr>" << endl;
-  cout << "<tr><td>Global only</td><td>" << gonly << "</td></tr>" << endl;
-  cout << "<tr><td>Local only</td><td>" << lonly << "</td></tr>" << endl;
 
-  cout << "</table>" << endl;
-  cout << "</body></html>" << endl;
+  if (!raw_dump)
+    cout << (honly+both)  << " passed hybrid, " << (gonly+both) << " passed global, "
+      << both << " pass both, " << neither << " pass neither, " << honly
+      << " passed only hybrid, " << gonly << " passed only global, of " << total
+      << " total." << endl;
 
   return 0;
 }