squares.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #include "opencv2/core.hpp"
  2. #include "opencv2/core/ocl.hpp"
  3. #include "opencv2/core/utility.hpp"
  4. #include "opencv2/imgproc.hpp"
  5. #include "opencv2/imgcodecs.hpp"
  6. #include "opencv2/highgui.hpp"
  7. #include <iostream>
  8. using namespace cv;
  9. using namespace std;
  10. int thresh = 50, N = 11;
  11. const char* wndname = "Square Detection Demo";
  12. // helper function:
  13. // finds a cosine of angle between vectors
  14. // from pt0->pt1 and from pt0->pt2
  15. static double angle( Point pt1, Point pt2, Point pt0 )
  16. {
  17. double dx1 = pt1.x - pt0.x;
  18. double dy1 = pt1.y - pt0.y;
  19. double dx2 = pt2.x - pt0.x;
  20. double dy2 = pt2.y - pt0.y;
  21. return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
  22. }
  23. // returns sequence of squares detected on the image.
  24. static void findSquares( const UMat& image, vector<vector<Point> >& squares )
  25. {
  26. squares.clear();
  27. UMat pyr, timg, gray0(image.size(), CV_8U), gray;
  28. // down-scale and upscale the image to filter out the noise
  29. pyrDown(image, pyr, Size(image.cols/2, image.rows/2));
  30. pyrUp(pyr, timg, image.size());
  31. vector<vector<Point> > contours;
  32. // find squares in every color plane of the image
  33. for( int c = 0; c < 3; c++ )
  34. {
  35. int ch[] = {c, 0};
  36. mixChannels(timg, gray0, ch, 1);
  37. // try several threshold levels
  38. for( int l = 0; l < N; l++ )
  39. {
  40. // hack: use Canny instead of zero threshold level.
  41. // Canny helps to catch squares with gradient shading
  42. if( l == 0 )
  43. {
  44. // apply Canny. Take the upper threshold from slider
  45. // and set the lower to 0 (which forces edges merging)
  46. Canny(gray0, gray, 0, thresh, 5);
  47. // dilate canny output to remove potential
  48. // holes between edge segments
  49. dilate(gray, gray, UMat(), Point(-1,-1));
  50. }
  51. else
  52. {
  53. // apply threshold if l!=0:
  54. // tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
  55. threshold(gray0, gray, (l+1)*255/N, 255, THRESH_BINARY);
  56. }
  57. // find contours and store them all as a list
  58. findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
  59. vector<Point> approx;
  60. // test each contour
  61. for( size_t i = 0; i < contours.size(); i++ )
  62. {
  63. // approximate contour with accuracy proportional
  64. // to the contour perimeter
  65. approxPolyDP(contours[i], approx, arcLength(contours[i], true)*0.02, true);
  66. // square contours should have 4 vertices after approximation
  67. // relatively large area (to filter out noisy contours)
  68. // and be convex.
  69. // Note: absolute value of an area is used because
  70. // area may be positive or negative - in accordance with the
  71. // contour orientation
  72. if( approx.size() == 4 &&
  73. fabs(contourArea(approx)) > 1000 &&
  74. isContourConvex(approx) )
  75. {
  76. double maxCosine = 0;
  77. for( int j = 2; j < 5; j++ )
  78. {
  79. // find the maximum cosine of the angle between joint edges
  80. double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
  81. maxCosine = MAX(maxCosine, cosine);
  82. }
  83. // if cosines of all angles are small
  84. // (all angles are ~90 degree) then write quandrange
  85. // vertices to resultant sequence
  86. if( maxCosine < 0.3 )
  87. squares.push_back(approx);
  88. }
  89. }
  90. }
  91. }
  92. }
  93. // the function draws all the squares in the image
  94. static void drawSquares( UMat& _image, const vector<vector<Point> >& squares )
  95. {
  96. Mat image = _image.getMat(ACCESS_WRITE);
  97. for( size_t i = 0; i < squares.size(); i++ )
  98. {
  99. const Point* p = &squares[i][0];
  100. int n = (int)squares[i].size();
  101. polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, LINE_AA);
  102. }
  103. }
  104. // draw both pure-C++ and ocl square results onto a single image
  105. static UMat drawSquaresBoth( const UMat& image,
  106. const vector<vector<Point> >& sqs)
  107. {
  108. UMat imgToShow(Size(image.cols, image.rows), image.type());
  109. image.copyTo(imgToShow);
  110. drawSquares(imgToShow, sqs);
  111. return imgToShow;
  112. }
  113. int main(int argc, char** argv)
  114. {
  115. const char* keys =
  116. "{ i input | ../data/pic1.png | specify input image }"
  117. "{ o output | squares_output.jpg | specify output save path}"
  118. "{ h help | | print help message }"
  119. "{ m cpu_mode | | run without OpenCL }";
  120. CommandLineParser cmd(argc, argv, keys);
  121. if(cmd.has("help"))
  122. {
  123. cout << "Usage : " << argv[0] << " [options]" << endl;
  124. cout << "Available options:" << endl;
  125. cmd.printMessage();
  126. return EXIT_SUCCESS;
  127. }
  128. if (cmd.has("cpu_mode"))
  129. {
  130. ocl::setUseOpenCL(false);
  131. cout << "OpenCL was disabled" << endl;
  132. }
  133. string inputName = samples::findFile(cmd.get<string>("i"));
  134. string outfile = cmd.get<string>("o");
  135. int iterations = 10;
  136. namedWindow( wndname, WINDOW_AUTOSIZE );
  137. vector<vector<Point> > squares;
  138. UMat image;
  139. imread(inputName, IMREAD_COLOR).copyTo(image);
  140. if( image.empty() )
  141. {
  142. cout << "Couldn't load " << inputName << endl;
  143. cmd.printMessage();
  144. return EXIT_FAILURE;
  145. }
  146. int j = iterations;
  147. int64 t_cpp = 0;
  148. //warm-ups
  149. cout << "warming up ..." << endl;
  150. findSquares(image, squares);
  151. do
  152. {
  153. int64 t_start = getTickCount();
  154. findSquares(image, squares);
  155. t_cpp += cv::getTickCount() - t_start;
  156. t_start = getTickCount();
  157. cout << "run loop: " << j << endl;
  158. }
  159. while(--j);
  160. cout << "average time: " << 1000.0f * (double)t_cpp / getTickFrequency() / iterations << "ms" << endl;
  161. UMat result = drawSquaresBoth(image, squares);
  162. imshow(wndname, result);
  163. imwrite(outfile, result);
  164. waitKey(0);
  165. return EXIT_SUCCESS;
  166. }