目前,使用opencv的目标是跟踪一个规则物体(比如圆形的药片),需要得到它的位置,以及移动的速度等。有前面得到的轮廓,我下面需要解决的是 它的重心位置以及半径。轮廓的矩提供了我需要的基本参数。
轮廓的长度(曲线长度或闭合曲线周长)求取
double cvArcLength(const void* curve, CvSlice slice=CV_WHOLE_SEQ, int isClosed=-1 )
轮廓的矩
void cvMoments(const CvArr* array, CvMoments* moments, int binary=0 )
array 图像 (1-通道或3-通道,有COI设置) 或多边形(点的 CvSeq 或一族点的向量).
moments 返回的矩状态接口的指针
binary (仅对图像) 如果标识为非零,则所有零象素点被当成零,其它的被看成 1.
函数 cvMoments 计算最高达三阶的空间和中心矩,并且将结果存在结构 moments 中。矩用来计算形状的重心,面积,主轴和其它的形状特征
用cvMoments函数时有一个参数是CvMoments * 结构,但是需要对这个结构初始化,也就是申请内存,用moment = (CvMoments*)malloc( sizeof(CvMoments).
在尝试上述目标时,发现有时得到的轮廓并非我想要的轮廓,立刻着手研究这问题,发现了mode变量: CV_RETR_ExTERNAL, CV_RETR_LIST, CV_RETR_CCOMP或CV_RETR_TREE.mode的值影响向cvFindeContours()。详见技术蛀虫的文章:http://www.cnblogs.com/nktblog/p/4027137.html。本文有两图来至于这片文章
以下程序描述了面积以及周长的求法(存在误差), 参考了morewindows的程序代码,做了调整,尝试去了解cvFindContours函数。
//图像的轮廓检测上
//By pengjc2001 (http://blog.csdn.net/pengjc2001)
#include <opencv2/opencv.hpp>
#include <stdlib.h>
#include "cxcore.h"
#include "cv.h"
#include "highgui.h"
double area = 0;
double perimeter = 0;
int main(int argc, char** argv)
{
const char *pstrWindowsSrcTitle = "原图(http://blog.csdn.net/pengjc2001)";
const char *pstrWindowsOutLineTitle = "轮廓图(http://blog.csdn.net/pengjc2001)";
const char *pstrWindowsBinaryTitle = "Binary(http://blog.csdn.net/pengjc2001)";
const char *pstrWindowsGrayTitle = "Gray(http://blog.csdn.net/pengjc2001)";
const int IMAGE_WIDTH = 400;
const int IMAGE_HEIGHT = 200;
printf("\IMAGE height : %d\n\tvideo width : %d\n", IMAGE_WIDTH, IMAGE_HEIGHT);
// 创建图像
IplImage *pSrcImage = cvCreateImage(cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 3);
// 填充成白色
cvRectangle(pSrcImage, cvPoint(0, 0), cvPoint(pSrcImage->width, pSrcImage->height), CV_RGB(255, 255, 255), CV_FILLED);
float S = (float)IMAGE_WIDTH*(float)IMAGE_HEIGHT;
float P = 2 * ((float)IMAGE_WIDTH + (float)IMAGE_HEIGHT);
printf("c0_Rectangle area:%f, perimeter:%f\n",S,P);
// 画圆
CvPoint ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);
int nRadius = 80;
cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 0), CV_FILLED);
S = 3.14*nRadius*nRadius;
P = 2 * 3.14*nRadius;
printf("h00_Circle1 area:%f, perimeter:%f\n", S, P);
ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);
nRadius = 30;
cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 255), CV_FILLED);
S = 3.14*nRadius*nRadius;
P = 2 * 3.14*nRadius;
printf("c000_Circle2 area:%f, perimeter:%f\n", S, P);
// 画矩形
CvPoint ptLeftTop = cvPoint(IMAGE_WIDTH / 2 + 40, 40);
CvPoint ptRightBottom = cvPoint(IMAGE_WIDTH - 40, IMAGE_HEIGHT - 40);
cvRectangle(pSrcImage, ptLeftTop, ptRightBottom, CV_RGB(0, 255, 255), CV_FILLED);
// 画圆
S = 120*120;
P = 2 * (120 + 120);
printf("h01_Rectangle area:%f, perimeter:%f\n", S, P);
ptCircleCenter = cvPoint(IMAGE_WIDTH * 3 / 4-20, IMAGE_HEIGHT / 2);
nRadius = 15;
cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 255), CV_FILLED);
S = 3.14*nRadius*nRadius;
P = 2 * 3.14*nRadius;
printf("c010_Circle2 area:%f, perimeter:%f\n", S, P);
ptCircleCenter = cvPoint(IMAGE_WIDTH * 3 / 4 + 20, IMAGE_HEIGHT / 2);
nRadius = 10;
cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 255), CV_FILLED);
S = 3.14*nRadius*nRadius;
P = 2 * 3.14*nRadius;
printf("c011_Circle2 area:%f, perimeter:%f\n", S, P);
// 显示原图
cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsSrcTitle, pSrcImage);
// 转为灰度图
IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
cvNamedWindow(pstrWindowsGrayTitle, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsGrayTitle, pGrayImage);
// 转为二值图
IplImage *pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1);
cvThreshold(pGrayImage, pBinaryImage, 250, 255, CV_THRESH_BINARY);
cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsBinaryTitle, pBinaryImage);
// 检索轮廓并返回检测到的轮廓的个数
CvMemStorage *pcvMStorage = cvCreateMemStorage();
CvSeq *pcvSeq = NULL;
cvFindContours(pBinaryImage, pcvMStorage, &pcvSeq, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
// 画轮廓图
IplImage *pOutlineImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);
int nLevels = 3;
// 填充成白色
cvRectangle(pOutlineImage, cvPoint(0, 0), cvPoint(pOutlineImage->width, pOutlineImage->height), CV_RGB(255, 255, 255), CV_FILLED);
cvDrawContours(pOutlineImage, pcvSeq, CV_RGB(255, 0, 0), CV_RGB(0, 0, 0), nLevels, 1);
// 显示轮廓图
cvNamedWindow(pstrWindowsOutLineTitle, CV_WINDOW_AUTOSIZE);
cvShowImage(pstrWindowsOutLineTitle, pOutlineImage);
CvSeq *pcvSeqInner = NULL;
int inner = 0;
int external = 0;
area = cvContourArea(pcvSeq);
perimeter = cvArcLength(pcvSeq);
for (; pcvSeq != NULL; pcvSeq = pcvSeq->h_next)
{
external++;
area = cvContourArea(pcvSeq);
perimeter = cvArcLength(pcvSeq);
CvMoments *moments = (CvMoments*)malloc(sizeof(CvMoments));
cvMoments(pcvSeq, moments, 1);
double moment10 = cvGetSpatialMoment(moments, 1, 0);
double moment01 = cvGetSpatialMoment(moments, 0, 1);
area = cvGetCentralMoment(moments, 0, 0);
printf("area:%f\t perimeter:%f\n", area,perimeter );
printf("external:%d\t inner = %d\t h_next\n", external, inner);
for (pcvSeqInner = pcvSeq->v_next; pcvSeqInner != NULL; pcvSeqInner = pcvSeqInner->h_next)
{
inner++;
double area = cvContourArea(pcvSeqInner);
double perimeter = cvArcLength(pcvSeqInner);
CvMoments *moments = (CvMoments*)malloc(sizeof(CvMoments));
cvMoments(pcvSeqInner, moments, 1);
double moment10 = cvGetSpatialMoment(moments, 1, 0);
double moment01 = cvGetSpatialMoment(moments, 0, 1);
area = cvGetCentralMoment(moments, 0, 0);
printf("area:%f\t perimeter:%f\n", area,perimeter );
printf("external:%d\t inner = %d\t \n", external, inner);
}
}
cvWaitKey(0);
cvReleaseMemStorage(&pcvMStorage);
cvDestroyWindow(pstrWindowsSrcTitle);
cvDestroyWindow(pstrWindowsOutLineTitle);
cvReleaseImage(&pSrcImage);
cvReleaseImage(&pGrayImage);
cvReleaseImage(&pBinaryImage);
cvReleaseImage(&pOutlineImage);
return 0;
}