# OpenCV基元检测 Primitive Detection

OpenCV边缘检测：Sobel、拉普拉斯算子
OpenCV边缘检测：坎尼算子算子

OpenCV LoG算子：SIFT算法
OpenCV Blob特征检测算子

OpenCV 角点检测：Harris算子

XY空间中的直线检测就等同于PQ空间的点检测

;高斯核是实现尺度变换的唯一变换核，具有多种优良性质，不会引入假信号

OpenCV 尺度空间与图像金字塔

http://blog.csdn.net/xiaowei_cqu

========

# 利用opencv识别并提取图片中的矩形

1. 对输入灰度图片进行高斯滤波
2. 做灰度直方图，提取阈值，做二值化处理
3. 提取图片轮廓
4. 识别图片中的矩形
5. 提取图片中的矩形

1.对输入灰度图片进行高斯滤波

cv::Mat hsv;
GaussianBlur(src,hsv,cv::Size(5,5),0,0);
2.做灰度直方图，提取阈值，做二值化处理

// Quantize the gray scale to 30 levels
int gbins = 16;
int histSize[] = {gbins};
// gray scale varies from 0 to 256
float granges[] = {0,256};
const float* ranges[] = { granges };
cv::MatND hist;
// we compute the histogram from the 0-th and 1-st channels
int channels[] = {0};

//calculate hist
calcHist( &hsv, 1, channels, cv::Mat(), // do not use mask
hist, 1, histSize, ranges,
true, // the histogram is uniform
false );
//find the max value of hist
double maxVal=0;
minMaxLoc(hist, 0, &maxVal, 0, 0);

int scale = 20;
cv::Mat histImg;
histImg.create(500,gbins*scale,CV_8UC3);

//show gray scale of hist image
for(int g=0;g<gbins;g++){
float binVal = hist.at<float>(g,0);
int intensity = cvRound(binVal*255);
rectangle( histImg, cv::Point(g*scale,0),
cv::Point((g+1)*scale - 1,binVal/maxVal*400),
CV_RGB(0,0,0),
CV_FILLED );
}
cv::imshow("histImg",histImg);

//threshold processing
cv::Mat hsvRe;
threshold( hsv, hsvRe, 64, 255,cv::THRESH_BINARY);

3.提取图片轮廓

4.识别矩形

http://opencv-code.com/tutorials/detecting-simple-shapes-in-an-image/

vector<Point> approx;

for (size_t i = 0; i < contours.size(); i++)
{
approxPolyDP(Mat(contours[i]), approx,
arcLength(Mat(contours[i]), true)*0.02, true);

if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;

for( int j = 2; j < 5; j++ )
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}

if( maxCosine < 0.3 )
squares.push_back(approx);
}
}

5.提取图片中的矩形

    //get rect from image
std::vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);

for(int i=0;i<squares.size();i++){
int rect[4],*tmp;
tmp = findRectInfo(squares[i]);
for(int j=0;j<4;j++)
rect[j] = *(tmp+j);
cv::Rect roi(rect[1],rect[0],rect[3],rect[2]);
cv::Mat roi_of_image = src(roi);

char * filename = new char[100];
sprintf(filename,"F:\\vs\\ConsoleApplication2\\resultPic_t35\\%i.png",i);
cv::imwrite(filename,roi_of_image,compression_params);
}

int* findRectInfo(std::vector<cv::Point> rect)
{
int rectInfo[4] = {0};
int x[4]= {0},y[4]= {0};
int maxX = 0,maxY = 0,minX = 2000,minY = 2000;
//get the rect points
for(int i=0;i<4;i++){
x[i] = rect[i].x;
y[i] = rect[i].y;
if(maxX<x[i])
maxX = x[i];
if(maxY<y[i])
maxY = y[i];
if(minX>x[i])
minX = x[i];
if(minY>y[i])
minY = y[i];
}
rectInfo[0] = minY;
rectInfo[1] = minX;
rectInfo[2] = maxY - minY;
rectInfo[3] = maxX - minX;
return rectInfo;
}

Reference:
1.http://opencv-code.com/tutorials/detecting-simple-shapes-in-an-image/
2.http://stackoverflow.com/questions/8667818/opencv-c-obj-c-detecting-a-sheet-of-paper-

square-detection
3.http://blog.csdn.net/timidsmile/article/details/8519751
4.http://blog.163.com/lee_020/blog/static/1247556020136473917915/
========

# OpenCV实现Hough变换检测圆形

在图像处理中，Hough变换（霍夫变换）主要用来识别已知的几何形状，最常见的比如直线、线段

、圆形、椭圆、矩形等。如果要检测比较复杂的曲线图形，就需要利用广义霍夫变换。

霍夫变换的原理是根据参数空间的统计规律进行参数估计。

具体说来就是，将直角坐标系中的图形(x,y)变换到参数空间(k1,...,kn)，对直角坐标系中的每

以直线检测为例，详细讲一下步骤：（圆和直线的原理相同，只是直线的公式比较好打~）

1.图像二值化，待检测的线变为黑色，背景置为白色。既然是形状检测，这步是必不可少的。

2.假设直线的参数方程为p=x*cosa+y*sina，对于直线上的某个点(x,y)来说，变换到参数空间的

比如我想求几张方向都朝上（y轴正方向）的半平面的交，我想得到的应该是一个下侧以向下凸的

3.把参数空间分割为n*m个格子，得到参数矩阵，矩阵元(pi,aj)的初始值均为0，用来对参数计数

。计数值代表这个参数是最终结果的可能性，计数值越大，说明落在这条直线上的像素点越多，也就说

4.按照栅格顺序扫描图像，遇到黑色像素就做如下操作：

pi的i从0取到n-1，对每一个pi，把它和像素点的坐标(x,y)代入参数方程，计算得到相应的ai

，如果ai在定义域范围内（或者在图像内），将矩阵元(pi,ai)加一。

处理完所有像素后，如果想识别d条直线，就在参数矩阵中找到前d个数值最大的矩阵元，他们

OpenCV中提供了计算霍夫变换的库函数HoughLines和HoughLinesP，想知道怎样使用，请戳传送门

圆形检测的过程很类似，只是参数方程有变化，而且参数空间增加了一个维度（圆心坐标x,y和半

霍夫变换的一个好处就是不需要图像中出现完整的圆，只要落在一个圆上的像素数量足够多，就

关于误差的问题：如果待检测像素没有严格落在同一个圆上，比如构成圆的圆弧彼此有些错位，

下面为了计算简便，我给出只检测一个半径为100的圆形的代码（要想采用聚类算法，只需修改第

71-81行的代码块）：

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

#define X_MAX 400
#define Y_MAX 400
#define TO_BE_BLACK 40

int houghTrans_r(IplImage *src, IplImage *dst, IplImage *tmp, float r, int xstep, int

ystep)
{
int width = src->width;
int height = src->height;

int channel = src->nChannels;
int xmax = width%xstep ? width/xstep+1 : width/xstep;
int ymax = height%ystep ? height/ystep+1 : height/ystep;

int i,j,x,y;
int para[X_MAX][Y_MAX] = {0};

//i,j are in the pixel space
//x,y are in the parameter space
for(j=0; j<height; j++)
{
uchar* pin = (uchar*)(src->imageData + j*src->widthStep);
for(i=0; i<width; i++)
{
//pixel is black
if(pin[channel*i] < TO_BE_BLACK)
{
float temp;

//calculate every probable y-cord based on x-cord
for(x=0; x<xmax; x++)
{
temp = r*r - (i-x*xstep)*(i-x*xstep);
temp = sqrt(temp);

y = j - (int)temp;
if(y>=0 && y<height){
para[x][y/ystep]++;
}

y = j + (int)temp;
if(y>=0 && y<height){
para[x][y/ystep]++;
}
}
}
}
}

//find circle in parameter space
int paramax=0,findx=-1,findy=-1;
for(y=0; y<ymax; y++)
{
for(x=0; x<xmax; x++)
{
if(para[x][y] > paramax)
{
paramax=para[x][y];
findx=x;
findy=y;
}
}
}

//draw the parameter space image
int ii,jj;
for(y=0; y<ymax; y++)
{
uchar* pout = (uchar*)(tmp->imageData + y*tmp->widthStep);
for(x=0; x<xmax; x++)
{
pout[channel*x]=para[x][y]*255/paramax;
pout[channel*x+1]=para[x][y]*255/paramax;
pout[channel*x+2]=para[x][y]*255/paramax;
}
}

//draw the found circle
if(findx>=0 && findy>=0)
{
for(j=0;j<height;j++)
{
uchar* pin=(uchar*)(src->imageData+j*src->widthStep);
uchar* pout=(uchar*)(dst->imageData+j*dst->widthStep);
for(i=0;i<width;i++)
{
pout[3*i]=128+pin[3*i]/2;
pout[3*i+1]=128+pin[3*i+1]/2;
pout[3*i+2]=128+pin[3*i+2]/2;
}
}
cvCircle(dst,cvPoint(findx*xstep+xstep/2.0,findy*ystep

+ystep/2.0),r,cvScalar(255,0,0),1,8,0);
}

return 1;
}

int main()
{
cvNamedWindow("Src",CV_WINDOW_AUTOSIZE);
cvNamedWindow("Result",CV_WINDOW_AUTOSIZE);
cvNamedWindow("Temp",CV_WINDOW_AUTOSIZE);

IplImage *houghImg = cvCreateImage(cvGetSize(srcImg),IPL_DEPTH_8U,3);
IplImage *houghTmp = cvCreateImage(cvGetSize(srcImg),IPL_DEPTH_8U,3);

houghTrans_r(srcImg,houghImg,houghTmp,100.0,1,1);

cvShowImage("Src",srcImg);
cvShowImage("Temp",houghTmp);
cvShowImage("Result",houghImg);

cvWaitKey(0);
cvReleaseImage(&srcImg);
cvReleaseImage(&houghImg);

cvDestroyWindow("Src");
cvDestroyWindow("Result");
return 0;
}

以下是检测示例：（左中右分别为原图像、参数空间图像和检测结果，检测结果用蓝色线绘制）

由于固定半径r，所以参数就是圆心位置(x,y)，绘制的点代表圆心的可能位置，颜色越浅，可

检测单独的圆

在很乱的线中检测不完整的圆

检测彼此错位的圆弧（参数划分扩大为5*5）
========

# opencv 检测直线、线段、圆、矩形

http://blog.csdn.net/byxdaz/archive/2009/12/01/4912136.aspx

/* 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.

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 )

{

IplImage* cpy = cvCloneImage( img );

int i;

// initialize reader of the sequence

// 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;

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

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

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

// 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++ )

{

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

if( !img0 )

{

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

1、http://blog.csdn.net/superdont/article/details/6664254

2、http://hi.baidu.com/%CE%C4%BF%A1%B5%C4%CF%A3%CD

%FB/blog/item/3a5cb2079158b304738b65f2.html

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

int main()
{
IplImage* src;
{
IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
CvMemStorage* storage = cvCreateMemStorage(0);//存储检测到线段，当然可以是N*1的矩阵

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

);//标准霍夫变

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(与像素

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

method
Hough 变换变量，是下面变量的其中之一：
CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示，其中 ρ 是

CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割，则效率更高). 它返回

CV_32SC4.
CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一

rho

theta

threshold

param1

param1 ).
param2

theta / param2).

3、http://www.opencv.org.cn/index.php/Hough%E7%BA%BF%E6%AE%B5%E6%A3%80%E6%B5%8B

========

# opencv形状分析

OpenCV支持大量的轮廓、边缘、边界的相关函数，相应的函数有moments、HuMoments、findContours、

drawContours、approxPolyDP、arcLength、boundingRect、contourArea、convexHull、fitEllipse、

fitLine、isContourConvex、minAreaRect、minEnclosingCircle、mathcShapes、pointPolygonTest。

OpenCV里支持很多边缘提取的办法，可是如何在一幅图像里得到轮廓区域的参数呢，这就需要用到

findContours函数，这个函数的原型为：

//C++:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray

hierarchy, int mode, int method, Point offset=Point())
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int

method, Point offset=Point())
[cpp] view plain copy
//C++:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray

hierarchy, int mode, int method, Point offset=Point())
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int

method, Point offset=Point())

contours参数为检测的轮廓数组，每一个轮廓用一个point类型的vector表示

hiararchy参数和轮廓个数相同，每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ]

~hierarchy[ i ][ 3 ]，分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号，如果没有

mode表示轮廓的检索模式

CV_RETR_EXTERNAL表示只检测外轮廓

CV_RETR_LIST检测的轮廓不建立等级关系

CV_RETR_CCOMP建立两个等级的轮廓，上面的一层为外边界，里面的一层为内孔的边界信息。如果内孔内

CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo

method为轮廓的近似办法

CV_CHAIN_APPROX_NONE存储所有的轮廓点，相邻的两个点的像素位置差不超过1，即max（abs（x1-x2）

，abs（y2-y1））==1

CV_CHAIN_APPROX_SIMPLE压缩水平方向，垂直方向，对角线方向的元素，只保留该方向的终点坐标，例

CV_CHAIN_APPROX_TC89_L1，CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

offset表示代表轮廓点的偏移量，可以设置为任意值。对ROI图像中找出的轮廓，并要在整个图像中进行

findContours后会对输入的2值图像改变，所以如果不想改变该2值图像，需创建新mat来存放，

findContours后的轮廓信息contours可能过于复杂不平滑，可以用approxPolyDP函数对该多边形曲线做

contourArea函数可以得到当前轮廓包含区域的大小，方便轮廓的筛选

findContours经常与drawContours配合使用，用来将轮廓绘制出来。其中第一个参数image表示目标图像

，第二个参数contours表示输入的轮廓组，每一组轮廓由点vector构成，第三个参数contourIdx指明画

thickness为轮廓的线宽，如果为负值或CV_FILLED表示填充轮廓内部，第六个参数lineType为线型，第

minAreaRect，返回值为RotatedRect；也可以得到轮廓的外包络圆，对应的函数为minEnclosingCircle

；想得到轮廓的外包络椭圆，对应的函数为fitEllipse，返回值也是RotatedRect，可以用ellipse函数

========

# 实用OpenCV  图像中的形状

?霍夫变换，可以使我们检测图像里的常规形状如线条和圆形。

?随机样本一致性（RANSAC），一个广泛使用的可以确定数据点来匹配特定模型的框架。我们将编写算法

?对象周围的绑定盒，绑定椭圆和凸形外壳的计算。

?形状匹配。

OpenCV的轮廓提取基于二值图像（像Canny边缘检测的输出或对Scharr边缘做阈值处理或者一张黑白图）

?使用inRange()检查像素值边界
?Canny边缘
?Scharr边缘做阈值处理

// Program to illustrate hierarchical contour extraction
// Author: Samarth Manoj Brahmbhatt, University of Pennsylvania
#include <opencv2 opencv.hpp="">
#include <opencv2 highgui="" highgui.hpp="">
#include <opencv2 imgproc="" imgproc.hpp="">
using namespace std;
using namespace cv;
Mat img;
vector<vector<point> > contours;
vector<vec4i> heirarchy;
int levels = 0;
void on_trackbar(int, void *) {
if(contours.empty()) return;
Mat img_show = img.clone();
// Draw contours of the level indicated by slider
drawContours(img_show, contours, -1, Scalar(0, 0, 255), 3, 8, heirarchy, levels);
imshow("Contours", img_show);
}
int main() {
Mat img_b;
cvtColor(img, img_b, CV_RGB2GRAY);
Mat edges;
Canny(img_b, edges, 50, 100);
// Extract contours and heirarchy
findContours(edges, contours, heirarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
namedWindow("Contours");
createTrackbar("levels", "Contours", &levels, 15, on_trackbar);
// Initialize by drawing the top level contours (as 'levels' is initialized to 0)
on_trackbar(0, 0);
while(char(waitKey(1)) != 'q') {}
return 0;
}
</vec4i></vector<point></opencv2></opencv2></opencv2>

\\
\

<"http://www.2cto.com/kf/ware/vc/" target="_blank"

class="keylink">vcD4KPHByZSBjbGFzcz0="brush:java;">// Program to find the smallest contour

that surrounds the clicked point // Author: Samarth Manoj Brahmbhatt, University of

Pennsylvania #include #include #include using namespace std; using namespace cv; Mat

img_all_contours; vector > closed_contours; vector heirarchy; // Function to approximate

contours by closed contours vector > make_contours_closed(vector > contours) { vector >

closed_contours; closed_contours.resize(contours.size()); for(int i = 0; i < contours.size

(); i++) approxPolyDP(contours[i], closed_contours[i], 0.1, true); return closed_contours;

} // Function to return the index of smallest contour in 'closed_contours' surrounding the

clicked point int smallest_contour(Point p, vector > contours, vector heirarchy) { int idx

= 0, prev_idx = -1; while(idx >= 0) { vector c = contours[idx]; // Point-polgon test double

d = pointPolygonTest(c, p, false); // If point is inside the contour, check its children

for an even smaller contour... if(d > 0) { prev_idx = idx; idx = heirarchy[idx][2]; } //

...else, check the next contour on the same level else idx = heirarchy[idx][0]; } return

prev_idx; } void on_mouse(int event, int x, int y, int, void *) { if(event !=

EVENT_LBUTTONDOWN) return; // Clicked point Point p(x, y); // Find index of smallest

enclosing contour int contour_show_idx = smallest_contour(p, closed_contours, heirarchy);

// If no such contour, user clicked outside all contours, hence clear image if

(contour_show_idx < 0) { imshow("Contours", img_all_contours); return; } // Draw the

smallest contour using a thick red line vector > contour_show; contour_show.push_back

(closed_contours[contour_show_idx]); if(!contour_show.empty()) { Mat img_show =

img_all_contours.clone(); drawContours(img_show, contour_show, -1, Scalar(0, 0, 255), 3);

imshow("Contours", img_show); } } int main() { Mat img = imread("circles.jpg");

img_all_contours = img.clone(); Mat img_b; cvtColor(img, img_b, CV_RGB2GRAY); Mat edges;

Canny(img_b, edges, 50, 100); // Extract contours and heirarchy vector > contours;

findContours(edges, contours, heirarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); // Make

contours closed so point-polygon test is valid closed_contours = make_contours_closed

(contours); // Draw all contours usign a thin green line drawContours(img_all_contours,

closed_contours, -1, Scalar(0, 255, 0)); imshow("Contours", img_all_contours); // Mouse

callback setMouseCallback("Contours", on_mouse); while(char(waitKey(1)) != 'q') {} return

0; } 假设 idx为轮廓在点向量的向量中的索引而hierarchy代表层次的话：
? hierarchy[idx][0] 返回同等级层次结构的下一个轮廓索引
? hierarchy[idx][1] 返回同等级层次结构的上一个轮廓索引
? hierarchy[idx][2] 返回第一个子轮廓的索引
? hierarchy[idx][3] 返回父轮廓的索引

OpenCV也提供了一些函数检查其中的一些属性来帮助你过滤噪声图像的轮廓。如表6-1

ArcLength() 查找轮廓长度
ContourArea() 查找轮廓区域和方向
BoundingRect() 计算轮廓的垂直边界矩形
ConvexHull() 计算轮廓围绕的凸形壳
IsContourConvex() 测试轮廓的凸性
MinAreaRect() 计算围绕轮廓的最小旋转矩形
MinEnclosingCircle() 查找围绕轮廓的最小区域圆形
FitLine() 基于轮廓匹配一条线（最小二乘）
========

# opencv识别正方形（矩形）代码

//正方形检测源码
//载入数张包含各种形状的图片，检测出其中的正方形
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>

int thresh = 50;
IplImage* img =NULL;
IplImage* img0 = NULL;
CvMemStorage* storage =NULL;
const char * wndname = "正方形检测 demo";

//angle函数用来返回（两个向量之间找到角度的余弦值）
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);
}

// 返回图像中找到的所有轮廓序列，并且序列存储在内存存储器中

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 );
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;
// 创建一个空序列用于存储轮廓角点
CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );

cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
// 过滤噪音
cvPyrDown( timg, pyr, 7 );
cvPyrUp( pyr, timg, 7 );
tgray = cvCreateImage( sz, 8, 1 );

// 红绿蓝3色分别尝试提取
for( c = 0; c < 3; c++ )
{
// 提取 the c-th color plane
cvSetImageCOI( timg, c+1 );
cvCopy( timg, tgray, 0 );

// 尝试各种阈值提取得到的（N=11）
for( l = 0; l < N; l++ )
{
// apply Canny. Take the upper threshold from slider
if( l == 0 )
{
cvCanny( tgray, gray, 0, thresh, 5 );
//使用任意结构元素膨胀图像
cvDilate( gray, gray, 0, 1 );
}
else
{
// apply threshold if l!=0:
cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
}

// 找到所有轮廓并且存储在序列中
cvFindContours( gray, storage, &contours, sizeof(CvContour),
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );

// 遍历找到的每个轮廓contours
while( contours )
{
//用指定精度逼近多边形曲线
result = cvApproxPoly( contours, sizeof(CvContour), storage,
CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );

if( result->total == 4 &&
fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 500 &&
fabs(cvContourArea(result,CV_WHOLE_SEQ)) < 100000 &&
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 余弦值 足够小，可以认定角度为90度直角
//cos0.1=83度，能较好的趋近直角
if( s < 0.1 )
for( i = 0; i < 4; i++ )
cvSeqPush( squares,
(CvPoint*)cvGetSeqElem( result, i ));
}

// 继续查找下一个轮廓
contours = contours->h_next;
}
}
}
cvReleaseImage( &gray );
cvReleaseImage( &pyr );
cvReleaseImage( &tgray );
cvReleaseImage( &timg );

return squares;
}

//drawSquares函数用来画出在图像中找到的所有正方形轮廓
void drawSquares( IplImage* img, CvSeq* squares )
{
IplImage* cpy = cvCloneImage( img );
int i;

// read 4 sequence elements at a time (all vertices of a square)
for( i = 0; i < squares->total; i += 4 )
{
CvPoint pt[4], *rect = pt;
int count = 4;

// draw the square as a closed polyline
cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 2, CV_AA, 0 );
}

cvShowImage( wndname, cpy );
cvReleaseImage( &cpy );
}

char* names[] = { "pic1.png", "pic2.png", "pic3.png",
"pic4.png", "pic5.png", "pic6.png","pic7.png","pic8.png",
"pic9.png","pic10.png","pic11.png","pic12.png", 0 };

int main(int argc, char** argv)
{
int i, c;
storage = cvCreateMemStorage(0);

for( i = 0; names[i] != 0; i++ )
{
img0 = cvLoadImage( names[i], 1 );
if( !img0 )
{
cout<<"不能载入"<<names[i]<<"继续下一张图片"<<endl;
continue;
}
img = cvCloneImage( img0 );
cvNamedWindow( wndname, 1 );

// find and draw the squares
drawSquares( img, findSquares4( img, storage ) );

c = cvWaitKey(0);

cvReleaseImage( &img );
cvReleaseImage( &img0 );

cvClearMemStorage( storage );

if( (char)c == 27 )
break;
}

cvDestroyWindow( wndname );
return 0;
}

========

# OpenCV入门之线段检测

。最基本的霍夫变换是从黑白图像中检测直线(线段)。

CvSeq* cvHoughLines2(
CvArr* image,
void* line_storage,
int method,
double rho,
double theta,
int threshold,
double param1=0, double param2=0
);

CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示，其中 ρ

CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割，则效率更高)。它返

CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的

对传统 Hough 变换，不使用(0).
对概率 Hough 变换，它是最小线段长度.
对多尺度 Hough 变换，它是距离精度 rho 的分母 (大致的距离精度是 rho 而精确的应该是 rho /

param1 ).

对传统 Hough 变换，不使用 (0).
对概率 Hough 变换，这个参数表示在同一条线段上进行碎线段连接的最大间隔值(gap), 即当同一条

对多尺度 Hough 变换，它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是

theta / param2)。

hough.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace std;

int main (int argc, char **argv)
{
const char *pstrWindowsSrcTitle = "initial";
const char *pstrWindowsLineName = "hough";

IplImage *pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);

IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCanny(pGrayImage, pCannyImage, 30, 90);

CvMemStorage *pcvMStorage = cvCreateMemStorage();
double fRho = 1;
double fTheta = CV_PI / 180;
int nMaxLineNumber = 50; //最多检测条直线
double fMinLineLen = 50; //最小线段长度
double fMinLineGap = 10; //最小线段间隔
CvSeq *pcvSeqLines = cvHoughLines2(pCannyImage, pcvMStorage, CV_HOUGH_PROBABILISTIC, fRho,

fTheta, nMaxLineNumber, fMinLineLen, fMinLineGap);

IplImage *pColorImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);
cvCvtColor(pCannyImage, pColorImage, CV_GRAY2BGR);
int i;
for(i = 0; i < pcvSeqLines->total; i++)
{
CvPoint* line = (CvPoint*)cvGetSeqElem(pcvSeqLines, i);
cvLine(pColorImage, line[0], line[1], CV_RGB(255,0,0), 2);
}

cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsSrcTitle, pSrcImage);
cvNamedWindow(pstrWindowsLineName, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsLineName, pColorImage);

cvWaitKey(0);

cvReleaseMemStorage(&pcvMStorage);
cvDestroyWindow(pstrWindowsSrcTitle);
cvDestroyWindow(pstrWindowsLineName);
cvReleaseImage(&pSrcImage);
cvReleaseImage(&pGrayImage);
cvReleaseImage(&pCannyImage);
cvReleaseImage(&pColorImage);
return 0;
}

makefile:

INCLUDE = $(shell pkg-config --cflags opencv) LIBS =$(shell pkg-config --libs opencv)
SOURCES = hough.cpp
# 目标文件
OBJECTS = $(SOURCES:.cpp=.o) # 可执行文件 TARGET = hough$(TARGET):$(OBJECTS) g++ -o$(TARGET) $(OBJECTS) -I$(INCLUDE) $(LIBS)$(OBJECTS):$(SOURCES) g++ -c$(SOURCES)
clean:
rm $(OBJECTS)$(TARGET)
# 编译规则 $@代表目标文件$< 代表第一个依赖文件
%.o:%.cpp
g++ -I $(INCLUDE) -o$@ -c \$<

========

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客