Commits

Ruslan Osmanov  committed 603c743

Refact

  • Participants
  • Parent commits 87039e9
  • Tags 1.0.3-r1

Comments (0)

Files changed (5)

File CMake/ImToolsCompiler.cmake

 # Initialize CXXFLAGS.
-set(CMAKE_CXX_FLAGS         "${CMAKE_CXX_FLAGS} -Wall -std=c++11")
+set(CMAKE_CXX_FLAGS         "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++11")
 set(CMAKE_CXX_FLAGS_DEBUG   "${CMAKE_CXX_FLAGS_DEBUG} -DIMTOOLS_DEBUG -O0 -g -pedantic")
 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG")
 

File src/immerge.cxx

 
 
 static void
-cleanup(bool is_error)
+init()
 {
-  verbose_log2("Cleanup...\n");
+  debug_log0("init()\n");
+
+  // Setup compression parameters
+
+  g_compression_params.push_back(CV_IMWRITE_PNG_STRATEGY);
+  g_compression_params.push_back(cv::IMWRITE_PNG_STRATEGY_FILTERED);
+  g_compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
+  g_compression_params.push_back(9/* 0 -none,  9 - full */);
+#if 0
+  // We don't force specific JPEG quality because the format is lossy.
+  g_compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
+  g_compression_params.push_back(90);
+#endif
+
+  // Initialize threading
+
+  IT_ATTR_INIT(g_pta);
+  IT_MUTEX_CREATE(g_work_mutex);
+  IT_MUTEX_CREATE(g_process_images_mutex);
+}
+
+
+static void
+cleanup()
+{
+  debug_log0("cleanup()\n");
 
   // Release resources allocated for threading
 
 }
 
 
-/// Loads images into memory
+/// Loads images specified by the CLI arguments into memory
 
 static void
 load_images(const int argc, char** argv)
   // Load target images
 
   if (optind < argc) {
+    if (g_pairs) {
+      // ARGV is a list of input and output files: infile outfile infile2 outfile2 ...
 
-    if (g_pairs) { // IMAGES is a list of input and output files
       for (int i = 0; optind < argc;) {
         const char* filename = argv[optind++];
 
         if (i++ % 2 == 0) {
           if (i > MAX_MERGE_TARGETS) {
-            strict_log(g_strict, "max. number of targets exceeded: %d. Skipping the rest.\n", MAX_MERGE_TARGETS);
+            strict_log(g_strict, "max. number of targets exceeded: %d. "
+                "Skipping the rest.\n", MAX_MERGE_TARGETS);
             break;
           }
 
-          // input file
+          // Input file
           if (!file_exists(filename)) {
             strict_log(g_strict, "image %s doesn't exist.\n", filename);
             break;
           }
           g_dst_images.push_back(filename);
         } else {
-          // output file
+          // Output file
           g_out_images.push_back(filename);
         }
       }
         strict_log(g_strict, "%s file have no pair! Skipping.\n", g_dst_images.back().c_str());
         g_dst_images.pop_back();
       }
-      assert(g_dst_images.size() == g_out_images.size());
-    } else { // IMAGES is a list of output files
+    } else { // ARGV is a list of output files
       for (int i = 0; optind < argc; ++i) {
         if (i >= MAX_MERGE_TARGETS) {
-          strict_log(g_strict, "max. number of targets exceeded: %d. Skipping the rest.\n", MAX_MERGE_TARGETS);
+          strict_log(g_strict, "max. number of targets exceeded: %d. "
+              "Skipping the rest.\n", MAX_MERGE_TARGETS);
           break;
         }
 
   g_new_img = cv::imread(g_new_image_filename, 1);
 
   if (g_old_img.size() != g_new_img.size()) {
-    strict_log(g_strict, "Input images have different dimensions.\n");
-    exit(1);
+    throw ErrorException("Input images have different dimensions");
   }
   if (g_old_img.type() != g_new_img.type()) {
-    strict_log(g_strict, "Input images have different types.\n");
-    exit(1);
+    throw ErrorException("Input images have different types");
   }
 }
 
 
-/// Thread routine) applying a bounding box to the output matrix.
+/// Thread routine a patch indicated by bounding box to the output matrix.
 /// @note Is called directly, if there is no threads support.
 
 static void*
     // at location (match_loc.x, match_loc.y).
     // The result will be stored in OUT_IMG.
 
-    patch(*pbarg->out_img, g_new_img, new_tpl_img, roi);
-
+    patch(*pbarg->out_img, new_tpl_img, roi);
   } catch (ErrorException& e) {
     success = false;
     log::push_error(e.what());
   } catch (...) {
-    log::push_error("Caught unknown exception!\n");
     success = false;
+    log::push_error("Caught unknown exception!\n");
   }
 
   return (void*)(success);
 }
 
 
+/// Applies all patches to the target image
+/// FILENAME - target filename.
+/// DIFF_IMG - binary image with information about differences between
+/// original and modified images, where modified spots have high values.
+/// OUT_FILENAME - optional output filename. If is NULL, result is
+/// written to the output directory.
+
 static bool
 process_image(string& filename, cv::Mat& diff_img, string* out_filename)
 {
       pthread_join(pbarg[i].thread_id, &thread_result);
       if (!thread_result) {
         success = false;
-        // No break. We have to wait for all the threads
+        // No break. We have to wait for the rest of threads.
       }
     }
 
 
 
 #ifdef IMTOOLS_THREADS
+/// Thread routine calling PROCESS_IMAGE function
+
 static void*
 process_image_thread_func(void* arg)
 {
 
 
 static bool
-merge()
+run()
 {
   bool success = true;
 
 #if defined(IMTOOLS_THREADS)
   int n_images = g_dst_images.size();
   auto img_proc_args = new image_process_arg_t[n_images];
+  void* thread_result;
   int i = 0;
-  void *thread_result;
 
   for (images_vector_t::iterator it = g_dst_images.begin(); it != g_dst_images.end(); ++it, ++i) {
     IT_LOCK(g_process_images_mutex);
   }
 #endif // IMTOOLS_THREADS
 
-  debug_timer_end(t1, t2, merge);
+  debug_timer_end(t1, t2, run);
 
   return (success);
 }
     exit(2);
   }
 
-
   debug_log("out-dir: %s\n", g_out_dir.c_str());
   debug_log("mod-threshold: %d\n", g_mod_threshold);
   debug_log("min-threshold: %d\n", g_min_threshold);
   debug_log("min-boxes-threshold: %d\n", g_min_boxes_threshold);
   debug_log("max-boxes-threshold: %d\n", g_max_boxes_threshold);
 
-  // Setup compression parameters
-
-  g_compression_params.push_back(CV_IMWRITE_PNG_STRATEGY);
-  g_compression_params.push_back(cv::IMWRITE_PNG_STRATEGY_FILTERED);
-  g_compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
-  g_compression_params.push_back(9/* 0 -none,  9 - full */);
-#if 0
-  g_compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
-  g_compression_params.push_back(90);
-#endif
-
-  // Initialize threading
-
-  IT_ATTR_INIT(g_pta);
-  IT_MUTEX_CREATE(g_work_mutex);
-  IT_MUTEX_CREATE(g_process_images_mutex);
+  init();
 
   try {
     // Load images into memory
     load_images(argc, argv);
     debug_timer_end(t1, t2, load_images());
 
-    // Merge the difference into the target images
-
-    if (!merge()) {
+    if (!run()) {
       exit_code = 1;
     }
-
   } catch (ErrorException& e) {
     error_log("%s\n", e.what());
     exit_code = 1;
     exit_code = 1;
   }
 
-  cleanup(exit_code);
+  cleanup();
 
   return exit_code;
 }

File src/impatch.cxx

     cv::Mat img_mat = cv::imread(*it);
     cv::Rect roi = cv::Rect(g_x, g_y, tpl_mat.cols, tpl_mat.rows);
 
-    imtools::patch(out_mat, img_mat, tpl_mat, roi);
+    // Prepare matrix for current target image
+
+    out_mat = img_mat.clone();
+
+    imtools::patch(out_mat, tpl_mat, roi);
 
     std::string out_filename = "out_" + g_tpl_filename;
     verbose_log("Writing to %s", out_filename.c_str());

File src/imtools.cxx

 
 #include "imtools.hxx"
 
+using std::string;
+using std::vector;
+using cv::Mat;
+using cv::Rect;
+using cv::Point;
+using cv::Size;
+
 namespace imtools {
 
 int verbose = 0;
 
 err:
   if (format) {
-    std::string error;
+    string error;
 
     va_list args;
     va_start(args, format);
     char message[1024];
     int message_len = vsnprintf(message, sizeof(message), format, args);
-    error = std::string(message, message_len);
+    error = string(message, message_len);
     va_end(args);
 
     throw InvalidCliArgException(error);
 
 
 void
-diff(cv::Mat& out_img, const cv::Mat& old_img, const cv::Mat& new_img, const int mod_threshold)
+diff(Mat& out_img, const Mat& old_img, const Mat& new_img, const int mod_threshold)
 {
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
 
 
 void
-blur(cv::Mat& target, const blur_type type)
+blur(Mat& target, const blur_type type)
 {
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
       break;
 
     case BLUR:
-      cv::blur(target, target, cv::Size(3, 3));
+      cv::blur(target, target, Size(3, 3));
       break;
 
     case BLUR_GAUSS:
-      cv::GaussianBlur(target, target, cv::Size(3, 3), 10);
+      cv::GaussianBlur(target, target, Size(3, 3), 10);
       break;
 
     case BLUR_MEDIAN:
       break;
 
     default:
-      throw std::runtime_error(std::string("Invalid blur type"));
+      throw ErrorException("Invalid blur type");
   }
 
   debug_timer_end(t1, t2, imtools::blur);
 
 
 void
-threshold(cv::Mat& target, const int threshold, const int max_threshold)
+threshold(Mat& target, const int threshold, const int max_threshold)
 {
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
 
 
 void
-match_template(cv::Point& match_loc, const cv::Mat& img, const cv::Mat& tpl)
+match_template(Point& match_loc, const Mat& img, const Mat& tpl)
 {
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
 
-  cv::Mat result;
+  Mat result;
   int match_method = CV_TM_SQDIFF;
 
   int result_cols = img.cols - tpl.cols + 1;
   result.create(result_cols, result_rows, CV_32FC1);
 
   cv::matchTemplate(img, tpl, result, match_method);
-  cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
+  cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, Mat());
 
   // Localize the best match with minMaxLoc
 
   double min_val, max_val;
-  cv::Point min_loc, max_loc;
-  cv::minMaxLoc(result, &min_val, &max_val, &min_loc, &max_loc, cv::Mat());
+  Point min_loc, max_loc;
+  cv::minMaxLoc(result, &min_val, &max_val, &min_loc, &max_loc, Mat());
 
   // For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all
   // the other methods, the higher the better
 
 
 void
-patch(cv::Mat& out_mat, const cv::Mat& img_mat, const cv::Mat& tpl_mat, const cv::Rect& roi)
+patch(Mat& out_mat, const Mat& tpl_mat, const Rect& roi)
 {
   debug_log("imtools::patch(), x: %d, y: %d\n", roi.x, roi.y);
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
 
   if (out_mat.empty()) {
-    throw std::runtime_error(std::string("Output matrix is empty"));
+    throw ErrorException("Output matrix is empty");
   }
   if (tpl_mat.empty()) {
-    throw std::runtime_error(std::string("Input template matrix is empty"));
+    throw ErrorException("Input template matrix is empty");
   }
 
-  assert(!img_mat.empty());
-  //assert(out_mat.rows >= img_mat.rows && out_mat.cols >= img_mat.cols);
-  assert(out_mat.type() == img_mat.type());
-  assert(img_mat.rows >= tpl_mat.rows && img_mat.cols >= tpl_mat.cols);
-
   if ((roi.x + tpl_mat.cols) > out_mat.cols
       || (roi.y + tpl_mat.rows) > out_mat.rows)
   {
   }
 
   // Region of interest
-  //cv::Rect roi = cv::Rect(pt.x, pt.y, tpl_mat.cols, tpl_mat.rows);
-
-  // Output matrix should be prepared already.
-  // out_mat = img_mat.clone();
+  //Rect roi = Rect(pt.x, pt.y, tpl_mat.cols, tpl_mat.rows);
 
   // Extract ROI from the output image as a reference
-  cv::Mat out_mat_roi = out_mat(roi);
+  Mat out_mat_roi = out_mat(roi);
 
   // Overwrite the ROI
   tpl_mat.copyTo(out_mat_roi);
 
 
 void
-bound_boxes(bound_box_vector_t& boxes, const cv::Mat& mask, int min_threshold, int max_threshold)
+bound_boxes(bound_box_vector_t& boxes, const Mat& mask, int min_threshold, int max_threshold)
 {
   debug_timer_init(t1, t2);
   debug_timer_start(t1);
 
-  cv::Mat mask_gray, threshold_output;
-  std::vector<std::vector<cv::Point> > contours;
-  std::vector<cv::Vec4i> hierarchy;
+  Mat mask_gray, threshold_output;
+  vector<vector<Point> > contours;
+  vector<cv::Vec4i> hierarchy;
 
   assert(min_threshold >= 0 && min_threshold <= max_threshold);
 
     mask_gray = mask;
   }
   debug_log0("bound_boxes: blur() w/ 15x15 kernel\n");
-  cv::blur(mask_gray, mask_gray, cv::Size(15, 15));
+  cv::blur(mask_gray, mask_gray, Size(15, 15));
 
   // Detect edges
   debug_log("bound_boxes: threshold(%d, %d)\n", min_threshold, max_threshold);
   cv::threshold(mask_gray, threshold_output, min_threshold, max_threshold, cv::THRESH_BINARY);
 
-  cv::findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
+  cv::findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
 
   // Approximate contours to polygons, get bounding rects
-  std::vector<std::vector<cv::Point> > contours_poly(contours.size());
+  vector<vector<Point> > contours_poly(contours.size());
   boxes.reserve(contours.size());
   debug_log("bound_boxes - reserved %ld\n", contours.size());
   for (size_t i = 0; i < contours.size(); ++i) {
-    cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true);
-    boxes.push_back(cv::boundingRect(cv::Mat(contours_poly[i])));
+    cv::approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
+    boxes.push_back(cv::boundingRect(Mat(contours_poly[i])));
     debug_log("bound_boxes - boxes[i] = %dx%d\n", boxes[i].x, boxes[i].y);
   }
   debug_log("bound_boxes - size = %ld\n", boxes.size());
 /// Computes structural similarity coefficient.
 /// The code is borrowed from
 /// http://docs.opencv.org/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html#image-similarity-psnr-and-ssim
-cv::Scalar get_MSSIM(const cv::Mat& i1, const cv::Mat& i2)
+cv::Scalar get_MSSIM(const Mat& i1, const Mat& i2)
 {
   const double C1 = 6.5025, C2 = 58.5225;
   int d           = CV_32F;
 
-  cv::Mat I1, I2;
+  Mat I1, I2;
   i1.convertTo(I1, d); // cannot calculate on one byte large values
   i2.convertTo(I2, d);
 
-  cv::Mat I2_2  = I2.mul(I2); // I2^2
-  cv::Mat I1_2  = I1.mul(I1); // I1^2
-  cv::Mat I1_I2 = I1.mul(I2); // I1 * I2
+  Mat I2_2  = I2.mul(I2); // I2^2
+  Mat I1_2  = I1.mul(I1); // I1^2
+  Mat I1_I2 = I1.mul(I2); // I1 * I2
 
 
-  cv::Mat mu1, mu2;
-  cv::GaussianBlur(I1, mu1, cv::Size(11, 11), 1.5);
-  cv::GaussianBlur(I2, mu2, cv::Size(11, 11), 1.5);
+  Mat mu1, mu2;
+  cv::GaussianBlur(I1, mu1, Size(11, 11), 1.5);
+  cv::GaussianBlur(I2, mu2, Size(11, 11), 1.5);
 
-  cv::Mat mu1_2   = mu1.mul(mu1);
-  cv::Mat mu2_2   = mu2.mul(mu2);
-  cv::Mat mu1_mu2 = mu1.mul(mu2);
+  Mat mu1_2   = mu1.mul(mu1);
+  Mat mu2_2   = mu2.mul(mu2);
+  Mat mu1_mu2 = mu1.mul(mu2);
 
-  cv::Mat sigma1_2, sigma2_2, sigma12;
+  Mat sigma1_2, sigma2_2, sigma12;
 
-  cv::GaussianBlur(I1_2, sigma1_2, cv::Size(11, 11), 1.5);
+  cv::GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
   sigma1_2 -= mu1_2;
 
-  cv::GaussianBlur(I2_2, sigma2_2, cv::Size(11, 11), 1.5);
+  cv::GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
   sigma2_2 -= mu2_2;
 
-  cv::GaussianBlur(I1_I2, sigma12, cv::Size(11, 11), 1.5);
+  cv::GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
   sigma12 -= mu1_mu2;
 
-  cv::Mat t1, t2, t3;
+  Mat t1, t2, t3;
 
   t1 = 2 * mu1_mu2 + C1;
   t2 = 2 * sigma12 + C2;
   t2 = sigma1_2 + sigma2_2 + C2;
   t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
 
-  cv::Mat ssim_map;
+  Mat ssim_map;
   cv::divide(t3, t1, ssim_map); // ssim_map =  t3./t1;
 
   cv::Scalar mssim = cv::mean(ssim_map); // mssim = average of ssim map

File src/imtools.hxx

 #include "log.hxx"
 #include "exceptions.hxx"
 
-#define IMTOOLS_VERSION "1.0.3-dev"
+using cv::Mat;
+using cv::Rect;
+using cv::Scalar;
+using cv::Point;
+
+
+#define IMTOOLS_VERSION "1.0.3-r1"
 
 #ifdef IMTOOLS_DEBUG
 # define IMTOOLS_BUILD_TYPE "debug"
 #define IMTOOLS_FULL_NAME "ImTools " IMTOOLS_VERSION " (" IMTOOLS_BUILD_TYPE ") (" IMTOOLS_SUFFIX ")"
 #define IMTOOLS_COPYRIGHT "Copyright (C) 2014 - Ruslan Osmanov <rrosmanov@gmail.com>"
 
-#if 0
-
-#define save_int_opt_arg(__arg, ...)           \
-{                                              \
-  if (optarg) {                                \
-    char *optarg_end;                          \
-    (__arg) = strtol(optarg, &optarg_end, 10); \
-    if (*optarg_end != '\0' || (__arg) < 0) {  \
-      error_log(__VA_ARGS__);                  \
-      usage(true);                             \
-    }                                          \
-  } else {                                     \
-    error_log(__VA_ARGS__);                    \
-    usage(true);                               \
-  }                                            \
-}
-#else
 #define save_int_opt_arg(__arg, ...)              \
 {                                                 \
   (__arg) = get_int_opt_arg(optarg, __VA_ARGS__); \
 }
 
-#endif
-
 
 namespace imtools {
 
 extern int verbose;
 
 typedef unsigned int uint_t;
-typedef cv::Rect bound_box_t;
+typedef Rect bound_box_t;
 typedef std::vector<bound_box_t> bound_box_vector_t;
 typedef std::vector<std::string> images_vector_t;
 
 struct box_arg_t {
   pthread_t    thread_id;
   bound_box_t *box;
-  cv::Mat     *old_img;
-  cv::Mat     *out_img;
+  Mat     *old_img;
+  Mat     *out_img;
   std::string *filename;
 };
 
   pthread_t    thread_id;
   std::string *filename;
   std::string *out_filename;
-  cv::Mat     *diff_img;
+  Mat     *diff_img;
 };
 
 inline bool
 
 int get_int_opt_arg(const char* optarg, const char* format, ...);
 
-
 // Computes difference between old_img and new_img. The matrix values lower than mod_threshold are
 // cut down to zeros. Result (1-channel binary image) is stored in out_img.
-void diff(cv::Mat& out_img, const cv::Mat& old_img, const cv::Mat& new_img,
+void diff(Mat& out_img, const Mat& old_img, const Mat& new_img,
     const int mod_threshold = THRESHOLD_MOD);
 
 /// Can be used to reduce noise
-void blur(cv::Mat& target, const blur_type type);
+void blur(Mat& target, const blur_type type);
 
 /// Reduces noise
-void threshold(cv::Mat& target, const int threshold = THRESHOLD_MIN,
+void threshold(Mat& target, const int threshold = THRESHOLD_MIN,
     const int max_threshold = THRESHOLD_MAX);
 
-void match_template(cv::Point& match_loc, const cv::Mat& img, const cv::Mat& tpl);
+void match_template(Point& match_loc, const Mat& img, const Mat& tpl);
 
-/// Patch IMG_MAT at position (X, Y) with contents of TPL_MAT.
-/// Result is stored in OUT_MAT. OUT_MAT must be of the same size and type as IMG_MAT.
-void patch(cv::Mat& out_mat, const cv::Mat& img_mat, const cv::Mat& tpl_mat, const cv::Rect& roi);
+/// Patch OUT_MAT at position (X, Y) with contents of TPL_MAT.
+void patch(Mat& out_mat, const Mat& tpl_mat, const Rect& roi);
 
 /// Find bounding boxes in mask (can be obtained with diff() + threshold())
-void bound_boxes(bound_box_vector_t& boxes, const cv::Mat& mask,
+void bound_boxes(bound_box_vector_t& boxes, const Mat& mask,
     int min_threshold = THRESHOLD_BOXES_MIN, int max_threshold = THRESHOLD_BOXES_MAX);
 
 /// Computes structural similarity coefficient, i.e. similarity between i1 and i2 matrices.
 /// Each item of the return value is a number between 0 and 1, where 1 is the perfect match.
-cv::Scalar get_MSSIM(const cv::Mat& i1, const cv::Mat& i2);
+Scalar get_MSSIM(const Mat& i1, const Mat& i2);
 
 } // namespace imtools