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