1. 程式人生 > >opencv 檢測直線、線段、圓、矩形

opencv 檢測直線、線段、圓、矩形

檢測直線:cvHoughLines,cvHoughLines2

檢測圓:cvHoughCircles

檢測矩形:opencv中沒有對應的函式,下面有段程式碼可以檢測矩形,是通過先找直線,然後找到直線平行與垂直的四根線。

檢測直線程式碼:

/* This is a standalone program. Pass an image name as a first parameter of the program.

   Switch between standard and probabilistic Hough transform by changing "#if 1" to "#if 0" and back */

#include <cv.h>

#include <highgui.h>

#include <math.h>

int main(int argc, char** argv)

{

    const char* filename = argc >= 2 ? argv[1] : "pic1.png";

    IplImage* src = cvLoadImage( filename, 0 );

    IplImage* dst;

    IplImage* color_dst;

    CvMemStorage* storage = cvCreateMemStorage(0);

    CvSeq* lines = 0;

    int i;

    if( !src )

        return -1;

    dst = cvCreateImage( cvGetSize(src), 8, 1 );

    color_dst = cvCreateImage( cvGetSize(src), 8, 3 );

    cvCanny( src, dst, 50, 200, 3 );

    cvCvtColor( dst, color_dst, CV_GRAY2BGR );

#if 0

    lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 100, 0, 0 );

    for( i = 0; i < MIN(lines->total,100); i++ )

    {

        float* line = (float*)cvGetSeqElem(lines,i);

        float rho = line[0];

        float theta = line[1];

        CvPoint pt1, pt2;

        double a = cos(theta), b = sin(theta);

        double x0 = a*rho, y0 = b*rho;

        pt1.x = cvRound(x0 + 1000*(-b));

        pt1.y = cvRound(y0 + 1000*(a));

        pt2.x = cvRound(x0 - 1000*(-b));

        pt2.y = cvRound(y0 - 1000*(a));

        cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, CV_AA, 0 );

    }

#else

    lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );

    for( i = 0; i < lines->total; i++ )

    {

        CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);

        cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );

    }

#endif

    cvNamedWindow( "Source", 1 );

    cvShowImage( "Source", src );

    cvNamedWindow( "Hough", 1 );

    cvShowImage( "Hough", color_dst );

    cvWaitKey(0);

    return 0;

}

檢測圓程式碼:

#include <cv.h>

#include <highgui.h>

#include <math.h>

int main(int argc, char** argv)

{

    IplImage* img;

    if( argc == 2 && (img=cvLoadImage(argv[1], 1))!= 0)

    {

        IplImage* gray = cvCreateImage( cvGetSize(img), 8, 1 );

        CvMemStorage* storage = cvCreateMemStorage(0);

        cvCvtColor( img, gray, CV_BGR2GRAY );

        cvSmooth( gray, gray, CV_GAUSSIAN, 9, 9 ); // smooth it, otherwise a lot of false circles may be detected

        CvSeq* circles = cvHoughCircles( gray, storage, CV_HOUGH_GRADIENT, 2, gray->height/4, 200, 100 );

        int i;

        for( i = 0; i < circles->total; i++ )

        {

             float* p = (float*)cvGetSeqElem( circles, i );

             cvCircle( img, cvPoint(cvRound(p[0]),cvRound(p[1])), 3, CV_RGB(0,255,0), -1, 8, 0 );

             cvCircle( img, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );

        }

        cvNamedWindow( "circles", 1 );

        cvShowImage( "circles", img );

    }

    return 0;

}

檢測矩形程式碼:

/*在程式裡找尋矩形*/

#ifdef _CH_

#pragma package <opencv>

#endif

 #ifndef _EiC

#include "cv.h"

#include "highgui.h"

#include <stdio.h>

#include <math.h>

#include <string.h>

#endif

int thresh = 50;

IplImage* img = 0;

IplImage* img0 = 0;

CvMemStorage* storage = 0;

CvPoint pt[4];

const char* wndname = "Square Detection Demo";

// helper function:

// finds a cosine of angle between vectors

// from pt0->pt1 and from pt0->pt2

 double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )

{   

    double dx1 = pt1->x - pt0->x;   

    double dy1 = pt1->y - pt0->y;   

    double dx2 = pt2->x - pt0->x;

    double dy2 = pt2->y - pt0->y;

    return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);

}

// returns sequence of squares detected on the image.

// the sequence is stored in the specified memory storage

CvSeq* findSquares4( IplImage* img, CvMemStorage* storage )

{

    CvSeq* contours;

    int i, c, l, N = 11;

    CvSize sz = cvSize( img->width & -2, img->height & -2 );

    IplImage* timg = cvCloneImage( img ); // make a copy of input image

    IplImage* gray = cvCreateImage( sz, 8, 1 );

    IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );

    IplImage* tgray;

    CvSeq* result;

    double s, t;

    // create empty sequence that will contain points -

    // 4 points per square (the square's vertices)

    CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );

    // select the maximum ROI in the image

    // with the width and height divisible by 2

    cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height )); 

    // down-scale and upscale the image to filter out the noise

    cvPyrDown( timg, pyr, 7 );

    cvPyrUp( pyr, timg, 7 );

    tgray = cvCreateImage( sz, 8, 1 );    

    // find squares in every color plane of the image

    for( c = 0; c < 3; c++ )

    {    

         // extract the c-th color plane

        cvSetImageCOI( timg, c+1 );

        cvCopy( timg, tgray, 0 ); 

       // try several threshold levels   

      for( l = 0; l < N; l++ )    

     {        

         // hack: use Canny instead of zero threshold level.      

        // Canny helps to catch squares with gradient shading      

         if( l == 0 )      

         {         

             // apply Canny. Take the upper threshold from slider 

               // and set the lower to 0 (which forces edges merging)

                 cvCanny( tgray, gray, 0, thresh, 5 );  

              // dilate canny output to remove potential   

             // holes between edge segments 

                cvDilate( gray, gray, 0, 1 );        

          }    

         else  

         {      

             // apply threshold if l!=0:

                //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 

               cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY ); 

           } 

         // find contours and store them all as a list

            cvFindContours( gray, storage, &contours, sizeof(CvContour),  

                        CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );  

         // test each contour

            while( contours )

            {

                // approximate contour with accuracy proportional

                // to the contour perimeter

                result = cvApproxPoly( contours, sizeof(CvContour), storage,

                    CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );

                // square contours should have 4 vertices after approximation

                // relatively large area (to filter out noisy contours)

                // and be convex.

                // Note: absolute value of an area is used because

                // area may be positive or negative - in accordance with the

                // contour orientation

                if( result->total == 4 &&

                    fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&

                    cvCheckContourConvexity(result) )

                {

                    s = 0;

                    for( i = 0; i < 5; i++ )

                    {

                        // find minimum angle between joint 

                       // edges (maximum of cosine)

                        if( i >= 2 )

                        {

                            t = fabs(angle(

                            (CvPoint*)cvGetSeqElem( result, i ),

                            (CvPoint*)cvGetSeqElem( result, i-2 ),

                            (CvPoint*)cvGetSeqElem( result, i-1 )));

                            s = s > t ? s : t;

                        }

                    }

                // if cosines of all angles are small 

                // (all angles are ~90 degree) then write quandrange

                    // vertices to resultant sequence

                     if( s < 0.3 )

                        for( i = 0; i < 4; i++ ) 

                           cvSeqPush( squares, 

                               (CvPoint*)cvGetSeqElem( result, i ));

                }

                // take the next contour 

               contours = contours->h_next;

            }

        }

    }

    // release all the temporary images

    cvReleaseImage( &gray );

    cvReleaseImage( &pyr );

    cvReleaseImage( &tgray );

    cvReleaseImage( &timg );

    return squares;

}

  // the function draws all the squares in the image

void drawSquares( IplImage* img, CvSeq* squares )

{   

    CvSeqReader reader;

    IplImage* cpy = cvCloneImage( img );

    int i;

        // initialize reader of the sequence

    cvStartReadSeq( squares, &reader, 0 );

        // read 4 sequence elements at a time (all vertices of a square)

    for( i = 0; i < squares->total; i += 4 )

    {

        CvPoint* rect = pt;

        int count = 4;

        // read 4 vertices

        memcpy( pt, reader.ptr, squares->elem_size );

        CV_NEXT_SEQ_ELEM( squares->elem_size, reader );

        memcpy( pt + 1, reader.ptr, squares->elem_size );

        CV_NEXT_SEQ_ELEM( squares->elem_size, reader );

        memcpy( pt + 2, reader.ptr, squares->elem_size );

        CV_NEXT_SEQ_ELEM( squares->elem_size, reader );

        memcpy( pt + 3, reader.ptr, squares->elem_size );

        CV_NEXT_SEQ_ELEM( squares->elem_size, reader );

        // draw the square as a closed polyline

         cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );

    }

   // show the resultant image

    cvShowImage( wndname, cpy );

    cvReleaseImage( &cpy );

 void on_trackbar( int a )

{

    if( img )

        drawSquares( img, findSquares4( img, storage ) );

}

char* names[] = { "pic1.png", "pic2.png", "pic3.png",

                  "pic4.png", "pic5.png", "pic6.png", 0 };

 int main(int argc, char** argv)

{

    int i, c;

    // create memory storage that will contain all the dynamic data

    storage = cvCreateMemStorage(0);

     for( i = 0; names[i] != 0; i++ )

    {

        // load i-th image

        img0 = cvLoadImage( names[i], 1 );

        if( !img0 )

        {

            printf("Couldn't load %s/n", names[i] );

            continue;

        }

        img = cvCloneImage( img0 );

        // create window and a trackbar (slider) with parent "image" and set callback

        // (the slider regulates upper threshold, passed to Canny edge detector)

         cvNamedWindow( wndname, 1 );

        cvCreateTrackbar( "canny thresh", wndname, &thresh, 1000, on_trackbar );

        // force the image processing

        on_trackbar(0);

        // wait for key.

        // Also the function cvWaitKey takes care of event processing

        c = cvWaitKey(0);

        // release both images

        cvReleaseImage( &img );

        cvReleaseImage( &img0 );

        // clear memory storage - reset free space position

        cvClearMemStorage( storage );

        if( c == 27 )

            break;

    } 

       cvDestroyWindow( wndname ); 

       return 0;

}

 #ifdef _EiC

main(1,"squares.c");

#endif

其它參考部落格:

#include <cv.h>
#include <highgui.h>
#include <math.h>

int main()
{
    IplImage* src;
if( (src=cvLoadImage("5.bmp", 1)) != 0)
    {
        IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );
        IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
        CvMemStorage* storage = cvCreateMemStorage(0);//儲存檢測到線段,當然可以是N*1的矩陣數列,如果

實際的直線數量多餘N,那麼最大可能數目的線段被返回
        CvSeq* lines = 0;
        int i;
IplImage* src1=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);

cvCvtColor(src, src1, CV_BGR2GRAY); //把src轉換成灰度影象儲存在src1中,注意進行邊緣檢測一定要

換成灰度圖
        cvCanny( src1, dst, 50, 200, 3 );//引數50,200的灰度變換

        cvCvtColor( dst, color_dst, CV_GRAY2BGR );
#if 1
        lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 150, 0, 0 );//標準霍夫變

換後兩個引數為0,由於line_storage是記憶體空間,所以返回一個CvSeq序列結構的指標

        for( i = 0; i < lines->total; i++ )
        {
            float* line = (float*)cvGetSeqElem(lines,i);//用GetSeqElem得到直線
            float rho = line[0];
            float theta = line[1];//對於SHT和MSHT(標準變換)這裡line[0],line[1]是rho(與畫素相關單位的距

離精度)和theta(弧度測量的角度精度)
            CvPoint pt1, pt2;
            double a = cos(theta), b = sin(theta);
            if( fabs(a) < 0.001 )
            {
                pt1.x = pt2.x = cvRound(rho);
                pt1.y = 0;
                pt2.y = color_dst->height;
            }
            else if( fabs(b) < 0.001 )
            {
                pt1.y = pt2.y = cvRound(rho);
                pt1.x = 0;
                pt2.x = color_dst->width;
            }
            else
            {
                pt1.x = 0;
                pt1.y = cvRound(rho/b);
                pt2.x = cvRound(rho/a);
                pt2.y = 0;
            }
            cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
        }
#else
        lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 80, 30, 10 );
        for( i = 0; i < lines->total; i++ )
        {
            CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
            cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
        }
#endif
        cvNamedWindow( "Source", 1 );
        cvShowImage( "Source", src );

        cvNamedWindow( "Hough", 1 );
        cvShowImage( "Hough", color_dst );

        cvWaitKey(0);
    }
}

line_storage 
檢測到的線段儲存倉. 可以是記憶體儲存倉 (此種情況下,一個線段序列在儲存倉中被建立,並且由函式返回),或者是包含線段引數的特殊型別(見下面)的具有單行/單列的矩陣(CvMat*)。矩陣頭為函式所修改,使得它的 cols/rows 將包含一組檢測到的線段。如果 line_storage 是矩陣,而實際線段的數目超過矩陣尺寸,那麼最大可能數目的線段被返回(線段沒有按照長度、可信度或其它指標排序). 
method 
Hough 變換變數,是下面變數的其中之一: 
CV_HOUGH_STANDARD - 傳統或標準 Hough 變換. 每一個線段由兩個浮點數 (ρ, θ) 表示,其中 ρ 是直線與原點 (0,0) 之間的距離,θ 線段與 x-軸之間的夾角。因此,矩陣型別必須是 CV_32FC2 type. 
CV_HOUGH_PROBABILISTIC - 概率 Hough 變換(如果影象包含一些長的線性分割,則效率更高). 它返回線段分割而不是整個線段。每個分割用起點和終點來表示,所以矩陣(或建立的序列)型別是 CV_32SC4. 
CV_HOUGH_MULTI_SCALE - 傳統 Hough 變換的多尺度變種。線段的編碼方式與 CV_HOUGH_STANDARD 的一致。 
rho 
與象素相關單位的距離精度 
theta 
弧度測量的角度精度 
threshold 
閾值引數。如果相應的累計值大於 threshold, 則函式返回的這個線段. 
param1 
第一個方法相關的引數: 
對傳統 Hough 變換,不使用(0). 
對概率 Hough 變換,它是最小線段長度. 
對多尺度 Hough 變換,它是距離精度 rho 的分母 (大致的距離精度是 rho 而精確的應該是 rho / param1 ). 
param2 
第二個方法相關引數: 
對傳統 Hough 變換,不使用 (0). 
對概率 Hough 變換,這個引數表示在同一條直線上進行碎線段連線的最大間隔值(gap), 即當同一條直線上的兩條碎線段之間的間隔小於param2時,將其合二為一。 
對多尺度 Hough 變換,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精確的角度應該是 theta / param2).
函式 cvHoughLines2 實現了用於線段檢測的不同 Hough 變換方法. Example. 用 Hough transform 檢測線段