作者 群号 C语言交流中心 240137450 微信 15013593099
形态学滤波
形态滤波进行角点检测
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#include "morphoFeatures.h"
int main()
{
Mat image = imread("f:/img/adc.jpg",0);
if(!image.data)
return -1;
imshow("源图像",image);
MorphoFeatures morpho;
morpho.setThreshold(40);
//获取边沿
Mat edges;
edges = morpho.getEdges(image);
imshow("边沿",edges);
//获取角点
morpho.setThreshold(-1);
Mat corners;
corners = morpho.getCorners(image);
morphologyEx(corners,corners,MORPH_TOPHAT,Mat());
threshold(corners,corners,40,255,THRESH_BINARY_INV);
//imshow("角点",corners);
//展示图片上的角点
morpho.drawOnImage(corners,image);
imshow("图片上的角点",image);
waitKey(0);
return 0;
}
MorphoFeatures.h
#if ! defined MORPHOF
#define MORPHOF
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
class MorphoFeatures
{
private:
//产生二值图像的门限
int threshold;
//结构元
Mat cross;
Mat diamond;
Mat square;
Mat x;
//对图像二值化
void applyThreshold(Mat &result)
{
if(threshold>0)
cv::threshold(result,result,threshold,255,THRESH_BINARY_INV);
}
public:
//构造函数
MorphoFeatures():threshold(-1),
cross(5,5,CV_8U,Scalar(0)),
diamond(5,5,CV_8U,Scalar(255)),
square(5,5,CV_8U,Scalar(255)),
x(5,5,CV_8U,Scalar(0))
{
//创建十字型结构元
for(int i = 0; i < 5; i++)
{
cross.at<uchar>(2,i) = 255;
cross.at<uchar>(i,2) = 255;
}
//创建菱形结构元:手动画,只需要把不是菱形的部分变白即可
diamond.at<uchar>(0,0)= 0;
diamond.at<uchar>(0,1)= 0;
diamond.at<uchar>(1,0)= 0;
diamond.at<uchar>(4,4)= 0;
diamond.at<uchar>(3,4)= 0;
diamond.at<uchar>(4,3)= 0;
diamond.at<uchar>(4,0)= 0;
diamond.at<uchar>(4,1)= 0;
diamond.at<uchar>(3,0)= 0;
diamond.at<uchar>(0,4)= 0;
diamond.at<uchar>(0,3)= 0;
diamond.at<uchar>(1,4)= 0;
//创建X形
for(int i = 0; i < 5; i++)
{
//主对角线
x.at<uchar>(i,i) = 255;
//副对角线
x.at<uchar>(4-i,i) = 255;
}
}
//设置门限函数
void setThreshold(int t)
{
threshold = t;
}
//获取当前门限
int getThreshold() const
{
return threshold;
}
//检测直线函数
Mat getEdges(const Mat &image)
{
Mat result;
//获取图像的梯度
morphologyEx(image,result,cv::MORPH_GRADIENT,Mat());
//结果二值化
applyThreshold(result);
return result;
}
//检测角点函数
Mat getCorners(const Mat &image)
{
Mat result;
dilate(image,result,cross);
erode(result,result,diamond);
Mat result2;
dilate(image,result2,x);
erode(result2,result2,square);
absdiff(result2,result,result);
applyThreshold(result);
return result;
}
//在角点处画圆
void drawOnImage(const Mat &binary,Mat &image)
{
Mat_<uchar>::const_iterator it = binary.begin<uchar>();
Mat_<uchar>::const_iterator itend = binary.end<uchar>();
for(int i = 0;it != itend;++it,++i)
{
if(!*it)
circle(image,Point(i%image.step,i/image.step),5,Scalar(255,0,0));
}
}
};
#endif
角点检测2
#include <stdio.h>
#include <opencv\\cv.h>
#include <opencv\\highgui.h>
#define max_corners 100
int main( int argc, char** argv )
{
int cornerCount=max_corners;
CvPoint2D32f corners[max_corners];
IplImage *srcImage = 0, *grayImage = 0, *corners1 = 0, *corners2 = 0;
int i;
CvScalar color = CV_RGB(255,0,0);
char* filename = "f:\\img\\cornor.png"; // 注意相对路径
cvNamedWindow( "image", 1 ); // create HighGUI window with name "image"
cvNamedWindow( "src", 1 );
//Load the image to be processed
srcImage = cvLoadImage(filename, 1);
cvShowImage("src",srcImage);
grayImage = cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_8U, 1);
//copy the source image to copy image after converting the format
cvCvtColor(srcImage, grayImage, CV_BGR2GRAY);
//create empty images of same size as the copied images
corners1= cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_32F, 1);
corners2= cvCreateImage(cvGetSize(srcImage),IPL_DEPTH_32F, 1);
cvGoodFeaturesToTrack (grayImage, corners1,
corners2, corners,
&cornerCount, 0.05,
5,
0,
3, // block size
0, // not use harris
0.4 );
printf("num corners found: %d\n", cornerCount);
// draw circles at each corner location in the gray image and
//print out a list the corners
if(cornerCount>0)
{
for (i=0; i<cornerCount; i++)
{
cvCircle(srcImage, cvPoint((int)(corners[i].x), (int)(corners[i].y)), 6,
color, 2, CV_AA, 0);
}
}
cvShowImage( "image", srcImage );
cvReleaseImage(&srcImage);
cvReleaseImage(&grayImage);
cvReleaseImage(&corners1);
cvReleaseImage(&corners2);
cvWaitKey(0); // wait for key. The function has
return 0;
}
凸包
例子1
#include "opencv\\cv.h"
#include "opencv\\highgui.h"
#include <stdlib.h>
#define ARRAY 0 /* switch between array/sequence method by replacing 0<=>1 */
int main( int argc, char** argv )
{
IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
cvNamedWindow( "hull", 1 );
#if !ARRAY
CvMemStorage* storage = cvCreateMemStorage();
#endif
for(;;)
{
int i, count = rand()%100 + 1, hullcount;
CvPoint pt0;
#if !ARRAY
CvSeq* ptseq = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour),
sizeof(CvPoint), storage );
CvSeq* hull;
for( i = 0; i < count; i++ )
{
pt0.x = rand() % (img->width/2) + img->width/4;
pt0.y = rand() % (img->height/2) + img->height/4;
cvSeqPush( ptseq, &pt0 );
}
hull = cvConvexHull2( ptseq, 0, CV_CLOCKWISE, 0 );
hullcount = hull->total;
#else
CvPoint* points = (CvPoint*)malloc( count * sizeof(points[0]));
int* hull = (int*)malloc( count * sizeof(hull[0]));
CvMat point_mat = cvMat( 1, count, CV_32SC2, points );
CvMat hull_mat = cvMat( 1, count, CV_32SC1, hull );
for( i = 0; i < count; i++ )
{
pt0.x = rand() % (img->width/2) + img->width/4;
pt0.y = rand() % (img->height/2) + img->height/4;
points[i] = pt0;
}
cvConvexHull2( &point_mat, &hull_mat, CV_CLOCKWISE, 0 );
hullcount = hull_mat.cols;
#endif
cvZero( img );
for( i = 0; i < count; i++ )
{
#if !ARRAY
pt0 = *CV_GET_SEQ_ELEM( CvPoint, ptseq, i );
#else
pt0 = points[i];
#endif
cvCircle( img, pt0, 2, CV_RGB( 255, 0, 0 ), CV_FILLED );
}
#if !ARRAY
pt0 = **CV_GET_SEQ_ELEM( CvPoint*, hull, hullcount - 1 );
#else
pt0 = points[hull[hullcount-1]];
#endif
for( i = 0; i < hullcount; i++ )
{
#if !ARRAY
CvPoint pt = **CV_GET_SEQ_ELEM( CvPoint*, hull, i );
#else
CvPoint pt = points[hull[i]];
#endif
cvLine( img, pt0, pt, CV_RGB( 0, 255, 0 ));
pt0 = pt;
}
cvShowImage( "hull", img );
int key = cvWaitKey(0);
if( key == 27 ) // 'ESC'
break;
#if !ARRAY
cvClearMemStorage( storage );
#else
free( points );
free( hull );
#endif
}
}
例子2
#include "opencv\cv.h"
#include "opencv\highgui.h"
#include <stdlib.h>
#define ARRAY 1 /* switch between array/sequence method by replacing 0<=>1 */
int main( int argc, char** argv )
{
IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
cvNamedWindow( "hull", 1 );
#if !ARRAY
CvMemStorage* storage = cvCreateMemStorage();
#endif
for(;;)
{
int i,count = rand()%100+1,hullcount;
CvPoint pt0;
#if !ARRAY
CvSeq *ptseq = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2,
sizeof(CvContour),sizeof(CvPoint),storage);
CvSeq *hull;
//随机得到点
for (i=0;i<count;i++)
{
pt0.x = rand()%(img->width/2) + img->height/4;
pt0.y = rand()%(img->height/2) + img->width/4;
cvSeqPush(ptseq,&pt0);
}
hull = cvConvexHull2(ptseq,0,CV_CLOCKWISE,0);//顺时针
hullcount = hull->total;
#else
CvPoint *points = (CvPoint *)malloc(count * sizeof(points[0]));
int *hull = (int *)malloc(count*sizeof(hull[0]));
CvMat point_mat = cvMat(1,count,CV_32SC2,points);
CvMat hull_mat = cvMat(1,count,CV_32SC1,hull);
//随机得到点
for (i=0;i<count;i++)
{
pt0.x = rand()%(img->width/2) + img->height/4;
pt0.y = rand()%(img->height/2) + img->width/4;
points[i]=pt0;
}
cvConvexHull2(&point_mat,&hull_mat,CV_CLOCKWISE,0);
hullcount = hull_mat.cols;//注意这些与序列处理方式不同的地方
#endif
cvZero(img);//清空img,准备画新图
//画点
for (i=0;i<count;i++)
{
#if !ARRAY
pt0 = *CV_GET_SEQ_ELEM(CvPoint , ptseq ,i);
#else
pt0 = points[i];
#endif
cvCircle(img,pt0,2,CV_RGB(255,0,0),CV_FILLED);
}
//确定一个端点
#if !ARRAY
pt0 = **CV_GET_SEQ_ELEM(CvPoint*,hull,hullcount -1);
#else
pt0 = points[hull[hullcount-1]];
#endif
for (i=0;i<hullcount;i++)
{
#if !ARRAY
CvPoint pt = **CV_GET_SEQ_ELEM(CvPoint*,hull,i);
#else
CvPoint pt = points[hull[i]];
#endif
cvLine(img,pt0,pt,CV_RGB(0,255,0));
pt0 = pt;
}
cvShowImage("hull",img);
cvWaitKey(0);
cvSaveImage("hull.jpg",img);
cvDestroyWindow("hull");
#if !ARRAY
cvClearMemStorage(storage);
#else
free(points);
free(hull);
#endif
}
return 0;
}
例子3
#include <opencv\cv.h>
#include <opencv\cxcore.h>
#include <opencv\highgui.h>
#include <opencv\cvaux.h>
#include <iostream>
using namespace std;
int main()
{
IplImage *src = cvLoadImage("f:\\img\\1.bmp",CV_LOAD_IMAGE_GRAYSCALE);
IplImage *dst = cvCreateImage(cvGetSize(src),8,3);cvZero(dst);
CvMemStorage *storage = cvCreateMemStorage();
CvSeq *contour = NULL , *hull = NULL;
cvShowImage("src",src);
CvContourScanner scanner = cvStartFindContours(src,storage);
while((contour = cvFindNextContour(scanner)) != NULL){
cvDrawContours(dst,contour,CV_RGB(255,0,0),CV_RGB(0,255,0),0);
cout<<cvCheckContourConvexity(contour)<<endl;
hull = cvConvexHull2(contour,0,CV_CLOCKWISE,0);
CvPoint pt0 = **(CvPoint**)cvGetSeqElem(hull,hull->total - 1);
for(int i = 0;i<hull->total;++i){
CvPoint pt1 = **(CvPoint**)cvGetSeqElem(hull,i);
cvLine(dst,pt0,pt1,CV_RGB(0,0,255));
pt0 = pt1;
}
CvSeq *defect = cvConvexityDefects(contour,hull);
for(int i = 0;i<defect->total;++i){
CvConvexityDefect df = *(CvConvexityDefect*)cvGetSeqElem(defect,i);
cvCircle(dst,*df.start,2,CV_RGB(255,255,0),-1);
cvCircle(dst,*df.end,2,CV_RGB(255,255,0),-1);
cvCircle(dst,*df.depth_point,2,CV_RGB(0,255,255),-1);
}
cvShowImage("dst",dst);
cvWaitKey();
}
cvEndFindContours(&scanner);
}
例子4
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include <math.h>
#define POINT_NUM 100
#define WIDTH 800
#define HEIGHT 800
#define zero 1e-12
#define DIS(a,b) sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))
#define SGN(x) (fabs(x)<zero?0:(x>0?1:-1))
#define CROSS(a,b,c) ((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))//叉积,用来判断旋转方向
#define CMP(a,b) (a.x < b.x || SGN((float)a.x -(float) b.x)==0 && a.y < b.y)//坐标的比较
#define RAND (rand() % 100000 / 100000.0)//产生0-1之间的浮点数
CvPoint p[POINT_NUM];
CvPoint* hull_p = new CvPoint[POINT_NUM];//用来储存凸包上的点
int hull_size = 0;
IplImage* img;
//下面简单实现了栈操作
inline void push(CvPoint* S,CvPoint pt)
{
S[hull_size++] = pt;
}
inline CvPoint pop(CvPoint* S)
{
return S[--hull_size];
}
inline void swap(int x,int y)
{
CvPoint pt = p[x];
p[x] = p[y];
p[y] = pt;
}
inline bool compare(CvPoint a,CvPoint b,CvPoint c)
{
int tmp = SGN((float)CROSS(a,b,c));
if(tmp != 0)
return tmp > 0;
else//如果两点共线的话,就需要比较远近了
return DIS((float)a,(float)b) < DIS((float)a,(float)b);
}
//快排,极角的排序
void sort(int l,int r)
{
CvPoint tmp = p[(l + r) / 2];
int i = l;
int j = r;
do
{
while(compare(p[0],p[i],tmp))i++;
while(compare(p[0],tmp,p[j]))j--;
if(i <= j)
{
swap(i,j);
i++;
j--;
}
}while(i <=j);
if(i < r)sort(i,r);
if(j > l)sort(l,j);
}
void draw_hull()
{
int min = -1;
for(int j = 0;j < POINT_NUM;j++)//找出x坐标最小的,作为起始点
{
if(min == -1 || CMP(p[j],p[min]))
min = j;
}
if(min != 0)
swap(0,min);
sort(1,POINT_NUM - 1);//其他点排序
push(hull_p,p[0]);
push(hull_p,p[1]);
push(hull_p,p[2]);
for(int i = 3;i < POINT_NUM;i++)
{
while(CROSS(hull_p[hull_size - 2],hull_p[hull_size - 1],p[i]) < 0)//非左转
{
pop(hull_p);
cvLine(img,hull_p[hull_size - 1],p[i],cvScalar(255,0,255));//为了看清运行过程而加的
cvShowImage("Image",img);
cvWaitKey(100);
}
cvLine(img,hull_p[hull_size - 1],p[i],cvScalar(255,0,255));
push(hull_p,p[i]);
}
cvPolyLine(img,&hull_p,&hull_size,1,1,cvScalar(0,0,255),2);//最终画出凸包
}
void show_outcome()
{
cvSet(img,cvScalar(255,255,255));
CvScalar color = cvScalar(0,0,0);
for(int i = 0;i < POINT_NUM;i++)//画出每个点,十字
{
int x = p[i].x;
int y = p[i].y;
cvLine(img,cvPoint(x - 5,y),cvPoint(x + 5,y),color,2);
cvLine(img,cvPoint(x,y - 5),cvPoint(x,y + 5),color,2);
}
draw_hull();
cvShowImage("Image",img);
cvWaitKey(0);
}
int main()
{
img = cvCreateImage(cvSize(WIDTH,HEIGHT),IPL_DEPTH_8U,3);
srand((unsigned)time(NULL));
double phase = RAND * CV_PI * 2.0;
for (int i = 0; i < POINT_NUM / 2; i++) {
double r = RAND * WIDTH / 4.0;
double theta = RAND * 1.5 * CV_PI + phase;
p[i] = cvPoint( WIDTH /4 + r * cos(theta), HEIGHT / 2 + 2 * r * sin(theta) );//椭圆
}
phase = RAND * CV_PI * 2.0;
for (int i = 0; i < POINT_NUM / 2; i++) {
double r = RAND * WIDTH / 4.0;
double theta = RAND * 1.5 * CV_PI + phase;
p[i + POINT_NUM / 2] = cvPoint(WIDTH / 4 * 3 + r * cos(theta), HEIGHT / 2 + r * sin(theta));//圆
}
show_outcome();
delete [] hull_p;
return 0;
}
例子5
#include "stdafx.h"
#include "opencv\cv.h"
#include "opencv\highgui.h"
int main( int argc, char** argv )
{
IplImage* src;
src=cvLoadImage("f:\\img\\pf.jpg",CV_LOAD_IMAGE_GRAYSCALE);
cvShowImage( "Source", src );
IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3 );
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contour = 0;
cvThreshold( src, src,120, 255, CV_THRESH_BINARY );//二值化
//cvNamedWindow( "Source", 1 );
//cvShowImage( "Source", src );
//提取轮廓
cvFindContours( src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
cvZero( dst );//清空数组
CvSeq* _contour =contour;
double maxarea=0;
double minarea=100;
int n=-1,m=0;//n为面积最大轮廓索引,m为迭代索引
for( ; contour != 0; contour = contour->h_next )
{
double tmparea=fabs(cvContourArea(contour));
if(tmparea < minarea)
{
cvSeqRemove(contour,0); //删除面积小于设定值的轮廓
continue;
}
CvRect aRect = cvBoundingRect( contour, 0 );
if ((aRect.width/aRect.height)<1)
{
cvSeqRemove(contour,0); //删除宽高比例小于设定值的轮廓
continue;
}
if(tmparea > maxarea)
{
maxarea = tmparea;
n=m;
}
m++;
// CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 );//创建一个色彩值
CvScalar color = CV_RGB( 0, 255,255 );
//max_level 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。
//如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。
//如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。
cvDrawContours( dst, contour, color, color, -1, 1, 8 );//绘制外部和内部的轮廓
}
contour =_contour; /*int k=0;*/
int count=0;
for( ; contour != 0; contour = contour->h_next )
{
count++;
double tmparea=fabs(cvContourArea(contour));
if (tmparea==maxarea /*k==n*/)
{
CvScalar color = CV_RGB( 255, 0, 0);
cvDrawContours( dst, contour, color, color, -1, 1, 8 );
}
/*k++;*/
}
printf("The total number of contours is:%d",count);
cvNamedWindow( "Components", 1 );
cvShowImage( "Components", dst );
cvWaitKey(0);
cvDestroyWindow( "Source" );
cvReleaseImage(&src);
cvDestroyWindow( "Components" );
cvReleaseImage(&dst);
return 0;
}
二值化查找最大连通区域的方法
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
Mat srcImg ;
srcImg=imread("f:\\img\\pf.jpg");
imshow( "Source", srcImg );
cv::Mat bwImg;
vector<vector<cv::Point>> contours ;
// 二值化图像
//cv::threshold(srcImg, bwImg, 0.0, 255.0, CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::threshold(srcImg, bwImg, 100, 255, CV_THRESH_BINARY_INV);
cv::imshow("binary image", bwImg);
cv::waitKey();
// 查找轮廓,对应连通域
cv::findContours(bwImg,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
// 寻找最大连通域
double maxArea = 0;
vector<cv::Point> maxContour;
for(size_t i = 0; i < contours.size(); i++)
{
double area = cv::contourArea(contours[i]);
if (area > maxArea)
{
maxArea = area;
maxContour = contours[i];
}
}
// 将轮廓转为矩形框
cv::Rect maxRect = cv::boundingRect(maxContour);
// 显示连通域
cv::Mat result1, result2;
bwImg.copyTo(result1);
bwImg.copyTo(result2);
for (size_t i = 0; i < contours.size(); i++)
{
cv::Rect r = cv::boundingRect(contours[i]);
cv::rectangle(result1, r, cv::Scalar(255));
}
cv::imshow("all regions", result1) ;
cv::waitKey();
cv::rectangle(result2, maxRect, cv::Scalar(255));
cv::imshow("largest region", result2) ;
cv::waitKey();
return 0;
}
识别3角形
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
int thresh = 50;
IplImage* img =NULL;
IplImage* img0 = NULL;
CvMemStorage* storage =NULL;
const char * wndname = "三角形识别 Demo";
// 两个向量之间找到角度的余弦值
// 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);
}
// 返回图像中找到的轮廓序列
// 序列存储在内存存储器中
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 );
// find squares in every color plane of the image
for( c = 0; c < 3; c++ )
{
// 提取 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 );
}
// 找到所有轮廓并且存储在序列中
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 );
// 三角形轮廓应当有3个顶点,3条边
// relatively large area (过滤干扰的轮廓)
// cvCheckContourConvexity保证是凸面的
// cvContourArea计算三角形区域面积,去掉一些不相干的区域
if( result->total == 3 &&
fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&
fabs(cvContourArea(result,CV_WHOLE_SEQ)) < 100000 &&
cvCheckContourConvexity(result) )
{
for( i = 0; i < 3; i++ )
cvSeqPush( squares,
(CvPoint*)cvGetSeqElem( result, i ));
}
// 继续查找下一个轮廓
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 += 3 )
{
CvPoint pt[3], *rect = pt;
int count = 3;
// read 3 vertices
CV_READ_SEQ_ELEM( pt[0], reader );
CV_READ_SEQ_ELEM( pt[1], reader );
CV_READ_SEQ_ELEM( pt[2], reader );
// cvPolyLine函数画出三角形轮廓
cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
}
// show the resultant image
cvShowImage( wndname, cpy );
cvReleaseImage( &cpy );
}
char* names[] = { "c:\\img\\1.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 )
{
cout<<"不能载入"<<names[i]<<"继续下一张图片"<<endl;
continue;
}
img = cvCloneImage( img0 );
cvNamedWindow( wndname, 1 );
drawSquares( img, findSquares4( img, storage ) );
c = cvWaitKey(0);
// release both images
cvReleaseImage( &img );
cvReleaseImage( &img0 );
// clear memory storage - reset free space position
cvClearMemStorage( storage );
if( (char)c == 27 )
break;
}
cvDestroyWindow( wndname );
return 0;
}
识别长方形
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
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
// Canny helps to catch squares with gradient shading
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 )
{
CvSeqReader reader;
IplImage* cpy = cvCloneImage( img );
int i;
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 pt[4], *rect = pt;
int count = 4;
// read 4 vertices
CV_READ_SEQ_ELEM( pt[0], reader );
CV_READ_SEQ_ELEM( pt[1], reader );
CV_READ_SEQ_ELEM( pt[2], reader );
CV_READ_SEQ_ELEM( pt[3], reader );
// 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[] = { "c:\\img\\1.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;
}
检测圆
#include "cv.h"
#include "highgui.h"
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
int thresh = 50;
IplImage* img =NULL;
IplImage* img0 = NULL;
IplImage * pImg8u=NULL;
CvMemStorage* storage =NULL;
const char * wndname = "圆形检测 Demo";
char* names[] = { "c:\\img\\1.png", 0};
void HoughCircle()
{
CvSeq * circles=NULL;
pImg8u=cvCreateImage(cvGetSize(img),8,1);
CvMemStorage* storage = cvCreateMemStorage(0);
cvCvtColor(img,pImg8u,CV_BGR2GRAY);
//最好先cvSmooth一下,再调用cvHoughCircles
cvSmooth(pImg8u,pImg8u,CV_GAUSSIAN,7,7);
circles=cvHoughCircles(pImg8u,storage,CV_HOUGH_GRADIENT,
2, //最小分辨率,应当>=1
pImg8u->height/4, //该参数是让算法能明显区分的两个不同圆之间的最小距离
140, //用于Canny的边缘阀值上限,下限被置为上限的一半
118, //累加器的阀值
2, //最小圆半径
120 //最大圆半径
);
int k;
for (k=0;k<circles->total;k++)
{
float *p=(float*)cvGetSeqElem(circles,k);
//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(0,0,0),3,CV_AA,0);
}
cvClearMemStorage( storage );
}
int main(int argc, char** argv)
{
int i, c;
// create memory storage that will contain all the dynamic data
for( i = 0; names[i] != 0; i++ )
{
img0 = cvLoadImage( names[i], 1 );
if( !img0 )
{
cout<<"不能载入"<<names[i]<<"继续下一张图片"<<endl;
continue;
}
img = cvCloneImage( img0 );
HoughCircle();
cvNamedWindow( wndname, 1 );
cvShowImage(wndname,img);
c = cvWaitKey(0);
cvReleaseImage( &img );
cvReleaseImage( &img0 );
cvReleaseImage(&pImg8u);
if( (char)c == 27 )
break;
}
cvDestroyWindow( wndname );
return 0;
}
findrgb
#include "cv.h"
#include "highgui.h"
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
IplImage* img =NULL;
IplImage* img0 =NULL;
IplImage* pic = NULL;
CvMemStorage* storage =NULL;
const char * wndname = "RGB检测 Demo";
char* names[] = { "c:\\img\\2.jpg",0};
void findRGB(IplImage *img,IplImage *pic)
{
for(int x=0;x<img->height;x++)
{
for(int y=0;y<img->width;y++)
{
if( ((uchar*)(img->imageData+x*img->widthStep))[y*img->nChannels+0]<30 //b
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 1]<30 //g
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 2]>150) //r
CV_IMAGE_ELEM(pic,uchar,x,y)=255;
if (((uchar*)(img->imageData+x*img->widthStep))[y*img->nChannels+0]<30 //b
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 1]>150 //g
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 2]<30) //r
CV_IMAGE_ELEM(pic,uchar,x,y)=255;
if (((uchar*)(img->imageData+x*img->widthStep))[y*img->nChannels+0]>150 //b
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 1]<30 //g
&&((uchar*)(img->imageData + x*img->widthStep))[y*img->nChannels + 2]<30) //r
CV_IMAGE_ELEM(pic,uchar,x,y)=255;
}
}
}
//cvFindContours( pic, storage, &contour, sizeof(CvContour),
//CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//cvDrawContours(pic, contour,
// CV_RGB(180,0,0), CV_RGB(0, 0, 0),
// 2, 2, 8);
int main(int argc, char** argv)
{
int i, c;
for( i = 0; names[i] != 0; i++ )
{
img0 = cvLoadImage( names[i], 1 );
if( !img0 )
{
cout<<"不能载入"<<names[i]<<"继续下一张图片"<<endl;
continue;
}
img = cvCloneImage( img0 );
IplImage* pic=cvCreateImage( cvSize(img->width,img->height),8,1 );
cvNamedWindow( wndname, 1 );
cvShowImage(wndname,img);
findRGB(img,pic);
cvNamedWindow("findRGB",1);
cvShowImage("findRGB",pic);
c = cvWaitKey(0);
cvReleaseImage( &img );
cvReleaseImage( &img0 );
cvReleaseImage(&pic);
if( (char)c == 27 )
break;
}
cvDestroyWindow( "wndname" );
cvDestroyWindow("findRGB");
return 0;
}
判断点在3角形
#include "stdafx.h"
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
double get_distance(CvPoint aA,CvPoint aB)
{
double distanceAB=0.0;
distanceAB=sqrt(double(aA.x-aB.x)*double(aA.x-aB.x)+double(aA.y-aB.y)*double(aA.y-aB.y));
return distanceAB;
}
double get_triangleArea(CvPoint aA, CvPoint aB, CvPoint aC)
{
double distanceAB=get_distance(aA,aB);
double distanceBC=get_distance(aB,aC);
double distanceCA=get_distance(aC,aA);
double distanceSum=(distanceAB+distanceBC+distanceCA)/2;
double area=0.0;
area=sqrt(distanceSum*(distanceSum-distanceAB)*(distanceSum-distanceBC)*(distanceSum-distanceCA));
return area;
}
bool PointinTriangle(CvPoint aA, CvPoint aB, CvPoint aC, CvPoint aP)
{
double areaABC=get_triangleArea(aA,aB,aC);
double areaABP=get_triangleArea(aA,aB,aP);
double areaACP=get_triangleArea(aA,aC,aP);
double areaBCP=get_triangleArea(aB,aC,aP);
if (areaABC==areaABP+areaACP+areaBCP)
{
return true;
}
else
{
return false;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
CvPoint A;
A.x=50;
A.y=10;
CvPoint B;
B.x=10;
B.y=50;
CvPoint C;
C.x=90;
C.y=50;
CvPoint P;
P.x=50;
P.y=30;
if(PointinTriangle(A,B,C,P))
{
cout<<"在三角形内"<<endl;
}
else
{
cout <<"不在三角形内"<<endl;
}
cvWaitKey(30);
return 0;
}
定位圆心出现偏斜
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
IplImage* img;
img=cvLoadImage("f:\\img\\circle.jpg", 1);
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,5,200 );
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 );
cout<<"圆心坐标x= "<<cvRound(p[0])<<endl<<"圆心坐标y= "<<cvRound(p[1])<<endl;
cout<<"半径="<<cvRound(p[2])<<endl;
}
cout<<"圆数量="<<circles->total<<endl;
cvNamedWindow( "circles", 1 );
cvShowImage( "circles", img );
cvWaitKey(0);
return 0;
}
填充孔洞
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
// 内轮廓填充
// 参数:
// 1. pBinary: 输入二值图像,单通道,位深IPL_DEPTH_8U。
// 2. dAreaThre: 面积阈值,当内轮廓面积小于等于dAreaThre时,进行填充。
void FillInternalContours(IplImage *pBinary, double dAreaThre)
{
double dConArea;
CvSeq *pContour = NULL;
CvSeq *pConInner = NULL;
CvMemStorage *pStorage = NULL;
// 执行条件
if (pBinary)
{
// 查找所有轮廓
pStorage = cvCreateMemStorage(0);
cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
// 填充所有轮廓
cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));
// 外轮廓循环
for (; pContour != NULL; pContour = pContour->h_next)
{
// 内轮廓循环
for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)
{
// 内轮廓面积
dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));
if (dConArea <= dAreaThre)
{
cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));
}
}
}
cvReleaseMemStorage(&pStorage);
pStorage = NULL;
}
}
int main()
{
IplImage *img = cvLoadImage("f:\\img\\1.jpg", 0);
IplImage *bin = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
cvCopy(img, bin);
FillInternalContours(bin, 200);
cvNamedWindow("img");
cvShowImage("img", img);
cvNamedWindow("result");
cvShowImage("result", bin);
cvWaitKey(-1);
cvReleaseImage(&img);
cvReleaseImage(&bin);
return 0;
}
角点检测
#include "stdafx.h"
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <cmath>
#include <iostream>
using namespace cv;
Mat harris(Mat &im, double sigma, int thresh, int radius){
Mat dx,dy,Ix,Iy,Ix2,Iy2,Ixy,cim;
Sobel( im, Ix, CV_64F, 1, 0, 3); //算法第一步,计算水平垂直差分
Sobel( im, Iy, CV_64F, 0, 1, 3);
int ksize = max(1, (int)(6*sigma));
if(ksize % 2 == 0)
ksize++;
GaussianBlur(Ix.mul(Ix), Ix2, Size(ksize, ksize), sigma); //算法第二步,计算二阶高斯差分矩阵
GaussianBlur(Iy.mul(Iy), Iy2, Size(ksize, ksize), sigma);
GaussianBlur(Ix.mul(Iy), Ixy, Size(ksize, ksize), sigma);
//Harris corner measure
//cim = (Ix2.*Iy2 - Ixy.^2)./(Ix2 + Iy2);
cim = (Ix2.mul(Iy2) - Ixy.mul(Ixy)) / (Ix2+Iy2); //算法第三步,计算响应函数,我使用了另外一种
Mat structedElement(radius, radius, CV_8U, Scalar(1));
Mat mx,norm_cim;
normalize( cim, norm_cim, 0, 255, NORM_MINMAX, CV_8U, Mat() );
dilate(norm_cim, mx, structedElement);
norm_cim = ( norm_cim == mx) & (norm_cim>thresh); //算法第4第5步融合,非极大值抑制和阀值检测
return norm_cim;
}
int main( int, char** argv )
{
Mat src,gray;
src = imread( "c:\\img\\1.png");
cvtColor( src, gray, CV_RGB2GRAY );
Mat corners = harris(gray, 1.5, 30, 2);
for( int j = 0; j < corners.rows ; j++ ) {
for( int i = 0; i < corners.cols; i++ ) {
if( corners.at<unsigned char>(j,i) > 0)
{
circle( gray, Point( i, j ), 3, Scalar(0), 2, 8, 0 );
}
}
}
namedWindow("result", 1);
imshow("result", gray);
waitKey();
return 0;
}
角点检测2
#include "stdafx.h"
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
/// Global variables
Mat src, src_gray;
int thresh = 200;
int max_thresh = 255;
char* source_window = "Source image";
char* corners_window = "Corners detected";
/// Function header
void cornerHarris_demo( int, void* );
/** @function main */
int main( int argc, char** argv )
{
/// Load source image and convert it to gray
src = imread( "c:\\img\\1.png", 1 );
cvtColor( src, src_gray, CV_BGR2GRAY );
/// Create a window and a trackbar
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
imshow( source_window, src );
cornerHarris_demo( 0, 0 );
waitKey(0);
return(0);
}
/** @function cornerHarris_demo */
void cornerHarris_demo( int, void* )
{
Mat dst, dst_norm, dst_norm_scaled;
dst = Mat::zeros( src.size(), CV_32FC1 );
/// Detector parameters
int blockSize = 2;
int apertureSize = 3;
double k = 0.04;
/// Detecting corners
cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );
/// Normalizing
normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
convertScaleAbs( dst_norm, dst_norm_scaled );
/// Drawing a circle around corners
for( int j = 0; j < dst_norm.rows ; j++ )
{ for( int i = 0; i < dst_norm.cols; i++ )
{
if( (int) dst_norm.at<float>(j,i) > thresh )
{
circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 );
}
}
}
/// Showing the result
namedWindow( corners_window, CV_WINDOW_AUTOSIZE );
imshow( corners_window, dst_norm_scaled );
}
聚类
#include "stdafx.h"
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
using namespace cv;
using namespace std;
// static void help()
// {
// cout << "\nThis program demonstrates kmeans clustering.\n"
// "It generates an image with random points, then assigns a random number of cluster\n"
// "centers and uses kmeans to move those cluster centers to their representitive location\n"
// "Call\n"
// "./kmeans\n" << endl;
// }
int main( int /*argc*/, char** /*argv*/ )
{
const int MAX_CLUSTERS = 5;
Scalar colorTab[] = //因为最多只有5类,所以最多也就给5个颜色
{
Scalar(0, 0, 255),
Scalar(0,255,0),
Scalar(255,100,100),
Scalar(255,0,255),
Scalar(0,255,255)
};
Mat img(500, 500, CV_8UC3);
RNG rng(12345); //随机数产生器
for(;;)
{
int k, clusterCount = rng.uniform(2, MAX_CLUSTERS+1);
int i, sampleCount = rng.uniform(1, 1001);
Mat points(sampleCount, 1, CV_32FC2), labels; //产生的样本数,实际上为2通道的列向量,元素类型为Point2f
clusterCount = MIN(clusterCount, sampleCount);
Mat centers(clusterCount, 1, points.type()); //用来存储聚类后的中心点
/* generate random sample from multigaussian distribution */
for( k = 0; k < clusterCount; k++ ) //产生随机数
{
Point center;
center.x = rng.uniform(0, img.cols);
center.y = rng.uniform(0, img.rows);
Mat pointChunk = points.rowRange(k*sampleCount/clusterCount,
k == clusterCount - 1 ? sampleCount :
(k+1)*sampleCount/clusterCount); //最后一个类的样本数不一定是平分的,
//剩下的一份都给最后一类
//每一类都是同样的方差,只是均值不同而已
rng.fill(pointChunk, CV_RAND_NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
}
randShuffle(points, 1, &rng); //因为要聚类,所以先随机打乱points里面的点,注意points和pointChunk是共用数据的。
kmeans(points, clusterCount, labels,
TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0),
3, KMEANS_PP_CENTERS, centers); //聚类3次,取结果最好的那次,聚类的初始化采用PP特定的随机算法。
img = Scalar::all(0);
for( i = 0; i < sampleCount; i++ )
{
int clusterIdx = labels.at<int>(i);
Point ipt = points.at<Point2f>(i);
circle( img, ipt, 2, colorTab[clusterIdx], CV_FILLED, CV_AA );
}
imshow("clusters", img);
char key = (char)waitKey(); //无限等待
if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'
break;
}
return 0;
}