intelligent_scissors.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include <iostream>
  2. #include <cmath>
  3. #include <string>
  4. #include <vector>
  5. #include <queue>
  6. #include <opencv2/core/core.hpp>
  7. #include <opencv2/highgui/highgui.hpp>
  8. #include <opencv2/imgproc/imgproc.hpp>
  9. using namespace cv;
  10. struct Pix
  11. {
  12. Point next_point;
  13. double cost;
  14. bool operator > (const Pix &b) const
  15. {
  16. return cost > b.cost;
  17. }
  18. };
  19. struct Parameters
  20. {
  21. Mat img, img_pre_render, img_render;
  22. Point end;
  23. std::vector<std::vector<Point> > contours;
  24. std::vector<Point> tmp_contour;
  25. Mat zero_crossing, gradient_magnitude, Ix, Iy, hit_map_x, hit_map_y;
  26. };
  27. static float local_cost(const Point& p, const Point& q, const Mat& gradient_magnitude, const Mat& Iy, const Mat& Ix, const Mat& zero_crossing)
  28. {
  29. float fG = gradient_magnitude.at<float>(q.y, q.x);
  30. float dp;
  31. float dq;
  32. const float WEIGHT_LAP_ZERO_CROSS = 0.43f;
  33. const float WEIGHT_GRADIENT_MAGNITUDE = 0.14f;
  34. const float WEIGHT_GRADIENT_DIRECTION = 0.43f;
  35. bool isDiag = (p.x != q.x) && (p.y != q.y);
  36. if ((Iy.at<float>(p) * (q.x - p.x) - Ix.at<float>(p) * (q.y - p.y)) >= 0)
  37. {
  38. dp = Iy.at<float>(p) * (q.x - p.x) - Ix.at<float>(p) * (q.y - p.y);
  39. dq = Iy.at<float>(q) * (q.x - p.x) - Ix.at<float>(q) * (q.y - p.y);
  40. }
  41. else
  42. {
  43. dp = Iy.at<float>(p) * (p.x - q.x) + (-Ix.at<float>(p)) * (p.y - q.y);
  44. dq = Iy.at<float>(q) * (p.x - q.x) + (-Ix.at<float>(q)) * (p.y - q.y);
  45. }
  46. if (isDiag)
  47. {
  48. dp /= sqrtf(2);
  49. dq /= sqrtf(2);
  50. }
  51. else
  52. {
  53. fG /= sqrtf(2);
  54. }
  55. return WEIGHT_LAP_ZERO_CROSS * zero_crossing.at<uchar>(q) +
  56. WEIGHT_GRADIENT_DIRECTION * (acosf(dp) + acosf(dq)) / static_cast<float>(CV_PI) +
  57. WEIGHT_GRADIENT_MAGNITUDE * fG;
  58. }
  59. static void find_min_path(const Point& start, Parameters* param)
  60. {
  61. Pix begin;
  62. Mat &img = param->img;
  63. Mat cost_map(img.size(), CV_32F, Scalar(FLT_MAX));
  64. Mat expand(img.size(), CV_8UC1, Scalar(0));
  65. Mat processed(img.size(), CV_8UC1, Scalar(0));
  66. Mat removed(img.size(), CV_8UC1, Scalar(0));
  67. std::priority_queue < Pix, std::vector<Pix>, std::greater<Pix> > L;
  68. cost_map.at<float>(start) = 0;
  69. processed.at<uchar>(start) = 1;
  70. begin.cost = 0;
  71. begin.next_point = start;
  72. L.push(begin);
  73. while (!L.empty())
  74. {
  75. Pix P = L.top();
  76. L.pop();
  77. Point p = P.next_point;
  78. processed.at<uchar>(p) = 0;
  79. if (removed.at<uchar>(p) == 0)
  80. {
  81. expand.at<uchar>(p) = 1;
  82. for (int i = -1; i <= 1; i++)
  83. {
  84. for(int j = -1; j <= 1; j++)
  85. {
  86. int tx = p.x + i;
  87. int ty = p.y + j;
  88. if (tx < 0 || tx >= img.cols || ty < 0 || ty >= img.rows)
  89. continue;
  90. if (expand.at<uchar>(ty, tx) == 0)
  91. {
  92. Point q = Point(tx, ty);
  93. float cost = cost_map.at<float>(p) + local_cost(p, q, param->gradient_magnitude, param->Iy, param->Ix, param->zero_crossing);
  94. if (processed.at<uchar>(q) == 1 && cost < cost_map.at<float>(q))
  95. {
  96. removed.at<uchar>(q) = 1;
  97. }
  98. if (processed.at<uchar>(q) == 0)
  99. {
  100. cost_map.at<float>(q) = cost;
  101. param->hit_map_x.at<int>(q)= p.x;
  102. param->hit_map_y.at<int>(q) = p.y;
  103. processed.at<uchar>(q) = 1;
  104. Pix val;
  105. val.cost = cost_map.at<float>(q);
  106. val.next_point = q;
  107. L.push(val);
  108. }
  109. }
  110. }
  111. }
  112. }
  113. }
  114. }
  115. static void onMouse(int event, int x, int y, int , void* userdata)
  116. {
  117. Parameters* param = reinterpret_cast<Parameters*>(userdata);
  118. Point &end = param->end;
  119. std::vector<std::vector<Point> > &contours = param->contours;
  120. std::vector<Point> &tmp_contour = param->tmp_contour;
  121. Mat &img_render = param->img_render;
  122. Mat &img_pre_render = param->img_pre_render;
  123. if (event == EVENT_LBUTTONDOWN)
  124. {
  125. end = Point(x, y);
  126. if (!contours.back().empty())
  127. {
  128. for (int i = static_cast<int>(tmp_contour.size()) - 1; i >= 0; i--)
  129. {
  130. contours.back().push_back(tmp_contour[i]);
  131. }
  132. tmp_contour.clear();
  133. }
  134. else
  135. {
  136. contours.back().push_back(end);
  137. }
  138. find_min_path(end, param);
  139. img_render.copyTo(img_pre_render);
  140. imshow("lasso", img_render);
  141. }
  142. else if (event == EVENT_RBUTTONDOWN)
  143. {
  144. img_pre_render.copyTo(img_render);
  145. drawContours(img_pre_render, contours, static_cast<int>(contours.size()) - 1, Scalar(0,255,0), FILLED);
  146. addWeighted(img_pre_render, 0.3, img_render, 0.7, 0, img_render);
  147. contours.resize(contours.size() + 1);
  148. imshow("lasso", img_render);
  149. }
  150. else if (event == EVENT_MOUSEMOVE && !contours.back().empty())
  151. {
  152. tmp_contour.clear();
  153. img_pre_render.copyTo(img_render);
  154. Point val_point = Point(x, y);
  155. while (val_point != end)
  156. {
  157. tmp_contour.push_back(val_point);
  158. Point cur = Point(param->hit_map_x.at<int>(val_point), param->hit_map_y.at<int>(val_point));
  159. line(img_render, val_point, cur, Scalar(255, 0, 0), 2);
  160. val_point = cur;
  161. }
  162. imshow("lasso", img_render);
  163. }
  164. }
  165. const char* keys =
  166. {
  167. "{help h | |}"
  168. "{@image | fruits.jpg| Path to image to process}"
  169. };
  170. int main( int argc, const char** argv )
  171. {
  172. Parameters param;
  173. const int EDGE_THRESHOLD_LOW = 50;
  174. const int EDGE_THRESHOLD_HIGH = 100;
  175. CommandLineParser parser(argc, argv, keys);
  176. parser.about("\nThis program demonstrates implementation of 'Intelligent Scissors' algorithm designed\n"
  177. "by Eric N. Mortensen and William A. Barrett, and described in article\n"
  178. "'Intelligent Scissors for Image Composition':\n"
  179. "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.138.3811&rep=rep1&type=pdf\n"
  180. "To start drawing a new contour select a pixel, click LEFT mouse button.\n"
  181. "To fix a path click LEFT mouse button again.\n"
  182. "To finish drawing a contour click RIGHT mouse button.\n");
  183. if (parser.has("help"))
  184. {
  185. parser.printMessage();
  186. return 1;
  187. }
  188. std::vector<std::vector<Point> > c(1);
  189. param.contours = c;
  190. std::string filename = parser.get<std::string>(0);
  191. Mat grayscale, img_canny;
  192. param.img = imread(samples::findFile(filename));
  193. param.hit_map_x.create(param.img.rows, param.img.cols, CV_32SC1);
  194. param.hit_map_y.create(param.img.rows, param.img.cols, CV_32SC1);
  195. cvtColor(param.img, grayscale, COLOR_BGR2GRAY);
  196. Canny(grayscale, img_canny, EDGE_THRESHOLD_LOW, EDGE_THRESHOLD_HIGH);
  197. threshold(img_canny, param.zero_crossing, 254, 1, THRESH_BINARY_INV);
  198. Sobel(grayscale, param.Ix, CV_32FC1, 1, 0, 1);
  199. Sobel(grayscale, param.Iy, CV_32FC1, 0, 1, 1);
  200. param.Ix.convertTo(param.Ix, CV_32F, 1.0/255);
  201. param.Iy.convertTo(param.Iy, CV_32F, 1.0/255);
  202. // Compute gradients magnitude.
  203. double max_val = 0.0;
  204. magnitude(param.Iy, param.Ix, param.gradient_magnitude);
  205. minMaxLoc(param.gradient_magnitude, 0, &max_val);
  206. param.gradient_magnitude.convertTo(param.gradient_magnitude, CV_32F, -1/max_val, 1.0);
  207. param.img.copyTo(param.img_pre_render);
  208. param.img.copyTo(param.img_render);
  209. namedWindow("lasso");
  210. setMouseCallback("lasso", onMouse, &param);
  211. imshow("lasso", param.img);
  212. waitKey(0);
  213. }