//学习OPENCV,第8章:轮廓
//内存存储器:大小相同的内存块组成的双向链表
//cvCreateImage从内存存储器中分配并释放内存
/*
一个轮廓对应一系列的点,也就是图像中的一条曲线。
在OPENCV中一般用序列来存储轮廓信息。序列中每个元素对应曲线中的一个点的位置。
*/
#include <cv.h>
#include <highgui.h>
//Some defines we left out of the book
#define CVX_RED CV_RGB(0xff,0x00,0x00)
#define CVX_GREEN CV_RGB(0x00,0xff,0x00)
#define CVX_BLUE CV_RGB(0x00,0x00,0xff)
IplImage* g_image = NULL;
IplImage* g_gray = NULL;
int g_thresh = 100;
CvMemStorage* g_storage = NULL;
void on_trackbar(int);
int main( int argc, char** argv )
{
//-------------------------------------------------------------------
//创建窗口用于显示图像,滑动条用于设置阈值,然后对二值化后的图像进行轮廓提取并绘制
//当控制参数的滑动条变化时,图像被更新
if(!(g_image = cvLoadImage("D:\\AI_Proj\\OPENCV\\dog.jpg")))
return -1;
cvNamedWindow("src");
cvShowImage("src",g_image);
cvNamedWindow("Contours",1);
cvCreateTrackbar("Threshold","Contours",&g_thresh,255,on_trackbar);
on_trackbar(0);//回调函数需要显示调用???
cvReleaseImage(&g_gray);
cvDestroyWindow("Contours");
cvReleaseImage(&g_image);
cvDestroyWindow("src");
cvWaitKey();
//-------------------------------------------------------------------
//检测出输入图像轮廓并逐个绘制
//cvNamedWindow("",1);
IplImage* img_8uc1 = NULL;
//Changed this a little for safer image loading and help if not
if(!(img_8uc1 = cvLoadImage("D:\\AI_Proj\\OPENCV\\dog.jpg", CV_LOAD_IMAGE_GRAYSCALE )))
return -1;
IplImage* img_edge = cvCreateImage(cvGetSize(img_8uc1),8,1);
IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
cvThreshold(img_8uc1,img_edge,128,255,CV_THRESH_BINARY);
CvMemStorage* storage = cvCreateMemStorage();
CvSeq* first_contour = NULL;
int Nc = cvFindContours(img_edge,storage, //Nc返回找到所有轮廓的个数
&first_contour,//函数会自动分配该指针,不需要手动分配和释放,指向轮廓树的首地址
sizeof(CvContour),//对象分配信息
CV_RETR_LIST);//轮廓类型:检查所有轮廓并保存到表list中
//显示二值化后的图像轮廓
cvNamedWindow("img_edge");
cvShowImage("img_edge",img_edge);
cvNamedWindow("img_8uc3");
int n=0,k;
printf("\n\nHit any key to draw the next contour, ESC to quit\n\n");
printf("Total Contours Detected: %d\n",Nc);
//遍历轮廓
for(CvSeq* c=first_contour;c!=NULL;c=c->h_next)
{
cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);//在BRG图像中绘制轮廓
//绘制轮廓P267
cvDrawContours(img_8uc3,c,CVX_RED,CVX_BLUE,
0,//maxlevel=0表示与输入轮廓属于同一等级的所有轮廓(实验现象看每次只画一个轮廓)
2,8);
printf("Contour #%d\n",n);
cvShowImage("img_8uc3",img_8uc3);
printf(" %d elements:\n", c->total);
for(int i=0;i<c->total;++i)
{
CvPoint* p = CV_GET_SEQ_ELEM(CvPoint,c,i);
printf(" (%d,%d)\n", p->x, p->y );
}
if((k = cvWaitKey()&0x7F) == 27)
break;
n++;
}
printf("Finished all contours. Hit key to finish\n");
//cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);
//cvShowImage("",img_8uc3);
cvWaitKey(0);
cvDestroyWindow("img_edge");
cvDestroyWindow("img_8uc3");
//cvReleaseImage(&img_8uc1);
cvReleaseImage(&img_8uc3);
cvReleaseImage(&img_edge);
return 0;
}
//通过滑动条回调函数改变二值化阈值,从中检索轮廓,并绘制
void on_trackbar(int)
{
//创建图像并开辟内存,将RGB转化为GRAY图像,并二值化分割
if(g_storage==NULL)
{
g_gray = cvCreateImage(cvGetSize(g_image),8,1);
g_storage = cvCreateMemStorage(0);
}
else
//cvReleaseMemStorage释放内存存储器所有空间到系统
//cvClearMemStorage清空内存存储器。不返还给系统,可以实现重复使用内存存储器中的内存空间
cvClearMemStorage(g_storage);
CvSeq* contours = 0;
//将图像转换为8位单通道灰度图
cvCvtColor(g_image,g_gray,CV_BGR2GRAY);
//将图像二值化
cvThreshold(g_gray,g_gray,g_thresh,255,CV_THRESH_BINARY);
//从二值图像中检索轮廓,并返回检测到的轮廓的个数contours。P259
cvFindContours( g_gray,//必须是8位单通道,二值化图像,会修改源图像
g_storage,//将找到的轮廓存储到此内存
&contours);//指向轮廓树首地址,无需手动创建或释放指针
cvZero(g_gray);
if(contours != NULL) //用于在图像上绘制外部和内部轮廓
cvDrawContours( g_gray,//要绘制轮廓的图像;同其他绘图函数,轮廓是ROI的修剪结果。
contours,//指向第一个轮廓的指针
cvScalarAll(255),//外轮廓的颜色
cvScalarAll(255),//内轮廓的颜色
1);//画轮廓的最大层数 ,如何处理通过节点树变量连接到一个轮廓上的其他任何轮廓
cvShowImage("Contours",g_gray);//显示添加轮廓的图像
cvWaitKey();
}