stackercpp.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #include "stackercpp.h"
  2. Stacker::Stacker(bool verbose)
  3. {
  4. Stacker::verbose = verbose;
  5. Stacker::export_width = 0;
  6. Stacker::export_height = 0;
  7. Stacker::layers = 0;
  8. Stacker::trimratio = 0;
  9. cv::setNumThreads(0);
  10. Stacker::stopwatch = clock();
  11. }
  12. void
  13. Stacker::add_frame(unsigned char *data, int width, int height)
  14. {
  15. stopwatch_start();
  16. Mat warp_matrix = Mat::eye(3, 3, CV_32F);
  17. Mat mat = Mat(height, width, CV_8UC3, data);
  18. Mat grayscale = Mat(height, width, CV_8UC1);
  19. cv::cvtColor(mat, grayscale, cv::COLOR_BGR2GRAY);
  20. stopwatch_mark("grayscale input");
  21. stopwatch_start();
  22. Scalar mean, stddev;
  23. meanStdDev(grayscale, mean, stddev);
  24. printf("mean: %f, dev: %f\n", mean[0], stddev[0]);
  25. if (mean[0] < 10) {
  26. return;
  27. }
  28. stopwatch_mark("filter");
  29. int number_of_iterations = 5;
  30. double termination_eps = 1e-10;
  31. TermCriteria criteria(TermCriteria::COUNT + TermCriteria::EPS, number_of_iterations, termination_eps);
  32. if (layers == 0) {
  33. // First image in the stack is used as the reference to align the next frames to
  34. Stacker::reference = grayscale;
  35. // Create black image to accumulate the average
  36. Stacker::stacked = Mat(height, width, CV_64FC3);
  37. Stacker::stacked.setTo(Scalar(0, 0, 0, 0));
  38. // Add first frame to the stack
  39. Stacker::stacked += mat;
  40. Stacker::layers += 1;
  41. } else {
  42. // All frames after the initial one are stacked
  43. Mat warped = Mat(Stacker::stacked.rows, Stacker::stacked.cols, CV_32FC1);
  44. stopwatch_start();
  45. cv::findTransformECC(grayscale, Stacker::reference, warp_matrix, MOTION_HOMOGRAPHY, criteria);
  46. stopwatch_mark("find alignment");
  47. stopwatch_start();
  48. warpPerspective(mat, warped, warp_matrix, warped.size(), INTER_LINEAR);
  49. stopwatch_mark("warp image");
  50. // Check how much the image should be cropped to hide the warped edges
  51. float current_trimratio = cv::videostab::estimateOptimalTrimRatio(warp_matrix, mat.size());
  52. Stacker::trimratio = std::max(Stacker::trimratio, current_trimratio);
  53. // Add the warped image to the stack
  54. Stacker::stacked += warped;
  55. Stacker::layers += 1;
  56. }
  57. }
  58. Mat
  59. Stacker::postprocess_mat(Mat input)
  60. {
  61. stopwatch_start();
  62. int h_crop = (int) ((float) input.cols * Stacker::trimratio);
  63. int v_crop = (int) ((float) input.rows * Stacker::trimratio);
  64. Mat cropped;
  65. input(Rect(h_crop, v_crop, input.cols - h_crop - h_crop, input.rows - v_crop - v_crop)).copyTo(input);
  66. stopwatch_mark("trim");
  67. stopwatch_start();
  68. Mat blur;
  69. GaussianBlur(input, blur, Size(0, 0), 1.8);
  70. std::vector<cv::Mat> rgb_planes(3);
  71. cv::split(blur, rgb_planes);
  72. double min_r, max_r, min_g, max_g, min_b, max_b;
  73. minMaxIdx(rgb_planes[0], &min_b, &max_b);
  74. minMaxIdx(rgb_planes[1], &min_g, &max_g);
  75. minMaxIdx(rgb_planes[2], &min_r, &max_r);
  76. input = input - Scalar(min_b + 5, min_g + 5, min_r + 5);
  77. double scale_r, scale_g, scale_b;
  78. scale_r = 255 / (max_r - min_r + 5);
  79. scale_g = (255 / (max_g - min_g + 5));
  80. scale_b = 255 / (max_b - min_b + 5);
  81. multiply(input, Scalar(scale_b, scale_g, scale_r), input);
  82. stopwatch_mark("levels");
  83. stopwatch_start();
  84. float gamma = 1 / 0.9;
  85. Mat table(1, 256, CV_8U);
  86. uchar *p = table.ptr();
  87. for (int i = 0; i < 256; ++i) {
  88. p[i] = (uchar) (pow(i / 255.0, gamma) * 255);
  89. }
  90. LUT(input, table, input);
  91. stopwatch_mark("gamma");
  92. stopwatch_start();
  93. Mat sharpened;
  94. GaussianBlur(input, sharpened, Size(0, 0), 1.7);
  95. addWeighted(input, 2.5, sharpened, -1.5, 0, sharpened);
  96. stopwatch_mark("sharpen");
  97. /* Disabled CLAHE local contrast, it's a bit to overpronounced and doesn't
  98. * seem really necessary at this point
  99. stopwatch_start();
  100. Mat lab;
  101. cvtColor(sharpened, lab, COLOR_BGR2Lab);
  102. std::vector<cv::Mat> lab_planes(3);
  103. cv::split(lab, lab_planes);
  104. stopwatch_mark("to Lab");
  105. stopwatch_start();
  106. cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
  107. clahe->setClipLimit(1);
  108. clahe->setTilesGridSize(Size(8, 8));
  109. cv::Mat dst;
  110. clahe->apply(lab_planes[0], dst);
  111. dst.copyTo(lab_planes[0]);
  112. stopwatch_mark("clahe");
  113. stopwatch_start();
  114. Mat result;
  115. cv::merge(lab_planes, lab);
  116. cvtColor(lab, result, COLOR_Lab2BGR);
  117. stopwatch_mark("to RGB");
  118. */
  119. return sharpened;
  120. }
  121. char *
  122. Stacker::get_result()
  123. {
  124. // Complete the averaging and go back to an 8-bit image
  125. stopwatch_start();
  126. Stacker::stacked.convertTo(Stacker::stacked, CV_8U, 1. / Stacker::layers);
  127. stopwatch_mark("average");
  128. // Run the final-frame postprocessing
  129. Mat result = postprocess_mat(Stacker::stacked);
  130. Stacker::export_width = result.cols;
  131. Stacker::export_height = result.rows;
  132. // Convert mat to bytes
  133. size_t size = result.total() * result.elemSize();
  134. char *data = (char *) malloc(size);
  135. std::memcpy(data, result.data, size * sizeof(char));
  136. return data;
  137. }
  138. char *
  139. Stacker::postprocess(unsigned char *data, int width, int height)
  140. {
  141. // Convert bytes to mat
  142. Mat mat = Mat(height, width, CV_8UC3, data);
  143. // Run the final-frame postprocessing
  144. Mat result = postprocess_mat(mat);
  145. Stacker::export_width = result.cols;
  146. Stacker::export_height = result.rows;
  147. // Convert mat to bytes
  148. size_t size = result.total() * result.elemSize();
  149. char *outdata = (char *) malloc(size);
  150. std::memcpy(outdata, result.data, size * sizeof(char));
  151. return outdata;
  152. }
  153. void
  154. Stacker::stopwatch_start()
  155. {
  156. Stacker::stopwatch = clock();
  157. }
  158. void
  159. Stacker::stopwatch_mark(const char *name)
  160. {
  161. if (Stacker::verbose) {
  162. printf("[%.1fms] %s\n", float(clock() - Stacker::stopwatch) / CLOCKS_PER_SEC * 1000, name);
  163. }
  164. }
  165. int
  166. Stacker::get_width()
  167. {
  168. return Stacker::export_width;
  169. }
  170. int
  171. Stacker::get_height()
  172. {
  173. return Stacker::export_height;
  174. }