stackercpp.cpp 5.8 KB

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