stackercpp.cpp 4.8 KB

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