OPENCV图像轮廓检测

前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像轮廓检测.

         一.图像轮廓检测

         在opencv中,轮廓对应着一系列的点的集合,opencv提供了一个函数,用来获得这些点的集合

         API:void finContours(输入图像,输出轮廓点集,输出向量,int 轮廓检索模式,int 轮廓近似方法,Point 轮廓点的可选偏移量)

         注:1.输入图像,是单通道八位阈值化图像,也就是对应canny检测之后的图像,如果不是阈值化图像,算法会将图像中不为0的点当成1,0点当成0处理,我们可以通过canny threshold adaptiveThreshold来处理,

            2.该函数在检测轮廓的时候会修改源图像的数据,所以最好不要拿源图像直接检测轮廓,最好是用拷贝

            3.输出点集的形式为vector<vector<Point>>contours,每个向量包含有多个向量,包含的向量一个就是一条轮廓,而包含的向量是由一个个点构成的.

                  4.输出向量包含了图像轮廓的拓扑信息,比如这个轮廓的前一个轮廓编号,后一个轮廓编号,父轮廓编号以及子轮廓编号,形式为vector<vec4i>h,h[0]为后一个轮廓编号 h[1]后一个轮廓编号 h[2]父轮廓编号 h[3]内嵌轮廓编号

                  5.轮廓的检索模式包含有如下几种选择RETR_EXTERNAL只检测最外围的轮廓  RETR_LIST提取所有的轮廓,不建立上下等级关系,只有兄弟等级关系  RETR_CCOMP提取所有轮廓,建立为双层结构 RETR_TREE提取所有轮廓,建立网状结构

                  6.轮廓的近似方法有以下取值 CHAIN_APPROX_NONE获取轮廓的每一个像素,像素的最大间距不超过1 CHAIN_APPROX_SIMPLE压缩水平垂直对角线的元素,只保留该方向的终点坐标(也就是说一条中垂线a-b,中间的点被忽略了) CHAIN_APPROX_TC89_LI使用TEH_CHAIN逼近算法中的LI算法  CHAIN_APPROX_TC89_KCOS使用TEH_CHAIN逼近算法中的KCOS算法

                  7.可选偏移量,对ROI区域中获得的轮廓要在整个图像中分析的时候,该参数可以派上用场

         二.图像轮廓绘制

         该函数可以很方便的绘制出我们找到的轮廓点集和拓扑结构构成的轮廓图.

         API:void drawContours(源图像,轮廓点集,int 绘制指示,scalar 轮廓颜色,int 轮廓粗细,int 轮廓线形,inputarray 轮廓的拓扑结构信息,int 轮廓的最大绘制等级,int 可选的轮廓偏移参数)

         注:1.如果绘制指示为负值,则绘制所有轮廓,轮廓粗细默认值为1,为负值的化,绘制在轮廓的内部进行,使用filled,填充轮廓,线条类型默认为8,LINE_AA将绘制抗锯齿的线型,轮廓绘制等级,默认值是INT_MAX,指示最多可以绘制几层轮廓,=.

         轮廓检测的基本步骤为1.图像转换为灰度图像,2.图像的滤波,降噪,3.图像的canny二值化或者其他二值化方式,4.寻找轮廓findContours

代码例子如下

//边À?缘¦Ì轮?廓¤a查¨¦找¨°
//先¨¨进?行D边À?缘¦Ì滤?波¡§ 然¨?后¨®用®?canny算?法¤¡§得Ì?到Ì?二t值¦Ì化¡¥图ª?像?
//滤?波¡§算?法¤¡§选?择?均¨´值¦Ì滤?波¡§ 可¨¦调Ì¡Â的Ì?是º?孔¡Á径?尺?寸ä?
//canny算?法¤¡§可¨¦调Ì¡Â的Ì?上¦?下?限T和¨ªsobel算?子Á¨®孔¡Á径?3 5 7
Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage;
vector<vector<Point>>g_vContours;
vector<Vec4i>g_vHierarchy;
RNG g_rng(12345);const int g_BlurMax = 100;
int g_BlurValue;
void onBlurTrackBar(int pos,void* userData);const int g_sobelSizeMax = 2;//sobel孔¡Á径?
int g_sobelValue;const int g_lowThresholdMax = 80;//边À?缘¦Ì检¨¬测a低̨ª阈D值¦Ì
int g_lowThresholdValue;
int g_upThresholdValue;void onTrackBarSobelSize(int pos,void* userData);
void onTrackBarLowThresholdSize(int pos,void* userData);int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\findContours.jpg");g_BlurValue = 4;g_sobelValue = 1;g_lowThresholdValue = 80;g_upThresholdValue = 240;namedWindow("canny Image");namedWindow("contours Image");createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);onBlurTrackBar(g_BlurValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("canny Image", srcImage.cols, 0);moveWindow("contour image", srcImage.cols*2, 0);waitKey(0);return 0;
}//修T改?了¢?滤?波¡§参?数ºy
void onBlurTrackBar(int pos,void* userData)
{int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}//修T改?了¢?sobel孔¡Á径?参?数ºy
void onTrackBarSobelSize(int pos,void* userData)
{int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}//修T改?了¢?阈D值¦Ì参?数ºy
void onTrackBarLowThresholdSize(int pos,void* userData)
{int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}

二.凸包

         凸包是指,给定一个二维平面上的点集,凸包就是将这个点集最外层的点连接起来的构成的凸多边形

         计算一个物体的凸包,然后计算凸包的凹缺陷,是理解物体轮廓与形状的有效方式,可以用于典型的相似物体查找中.

         API:void convertHull(输入二维点集,输出凸包,bool 操作方向标识符,bool 返回点类型)

         注:1.操作方向标识符,是指在笛卡尔坐标中,当为true的时候,起始点到结束点顺时针,否则,逆时针.

                  2返回点类型为真时,返回凸包的各个定点,否则,返回凸包各点的指数,当输出为vector<point>时,这个参数被忽略.

                  3.返回的二维点集形态类是vector<point>,得到的凸包类型,也是vector<point>hull,hull.size()是凸包的点的个数.

         一般寻找凸包,主要是先对图像二值化,后寻找轮廓,然后寻找一条指定轮廓的凸包.

示例代码如下

//对?一°?张?图ª?片?进?行D轮?廓¤a查¨¦找¨°,并¡é对?每?一°?个?查¨¦找¨°出?来¤¡ä的Ì?轮?廓¤a运?行D凸ª1包㨹查¨¦找¨°程¨¬序¨°,生¦¨²成¨¦最Á?终?显?示º?图ª?片?
//边À?缘¦Ì轮?廓¤a查¨¦找¨°
//先¨¨进?行D边À?缘¦Ì滤?波¡§ 然¨?后¨®用®?canny算?法¤¡§得Ì?到Ì?二t值¦Ì化¡¥图ª?像?
//滤?波¡§算?法¤¡§选?择?均¨´值¦Ì滤?波¡§ 可¨¦调Ì¡Â的Ì?是º?孔¡Á径?尺?寸ä?
//canny算?法¤¡§可¨¦调Ì¡Â的Ì?上¦?下?限T和¨ªsobel算?子Á¨®孔¡Á径?3 5 7
Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage,hullImage;
vector<vector<Point>>g_vContours;
vector<Vec4i>g_vHierarchy;
RNG g_rng(12345);const int g_BlurMax = 100;
int g_BlurValue;
void onBlurTrackBar(int pos,void* userData);const int g_sobelSizeMax = 2;//sobel孔¡Á径?
int g_sobelValue;const int g_lowThresholdMax = 80;//边À?缘¦Ì检¨¬测a低̨ª阈D值¦Ì
int g_lowThresholdValue;
int g_upThresholdValue;void onTrackBarSobelSize(int pos,void* userData);
void onTrackBarLowThresholdSize(int pos,void* userData);int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\convexHull.jpg");g_BlurValue = 4;g_sobelValue = 1;g_lowThresholdValue = 80;g_upThresholdValue = 240;namedWindow("canny Image");namedWindow("contours Image");namedWindow("hull image");createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);onBlurTrackBar(g_BlurValue, 0);moveWindow("src image", 0, 0);moveWindow("canny Image", srcImage.cols, srcImage.rows);moveWindow("contour image", srcImage.cols*2, 0);moveWindow("hull image", srcImage.cols*2, srcImage.rows);waitKey(0);return 0;
}//修T改?了¢?滤?波¡§参?数ºy
void onBlurTrackBar(int pos,void* userData)
{imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}//修T改?了¢?sobel孔¡Á径?参?数ºy
void onTrackBarSobelSize(int pos,void* userData)
{imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}//修T改?了¢?阈D值¦Ì参?数ºy
void onTrackBarLowThresholdSize(int pos,void* userData)
{imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage);
}

三.对于轮廓代表的二维点集的其他处理方式

         获取点集的外围矩形边界

         API:Rect boundingRect(输入点集)

         返回点集的最小包围矩形

         API:RotatedRect minAreaRect(输入点集)

         寻找点集的最小包围圆心

         API:void minEnclosingCircle(输入点集,Point2f& 圆心,float& 半径)

         椭圆拟合二维点集

         API:RatatedRect fitEllipse(输入点集)

         逼近多边形曲线

         API:void approxPolyDp(输入二维点集,输出多边形逼近结果,double epsilon,bool close是否封闭)

         注:epsilon为原始曲线和近似曲线之间的最大值

            closed为真,则封闭,为假,得到的多边形不封闭

         以上各个API使用例程如下

//首º¡Á先¨¨查¨¦找¨°轮?廓¤a,再¨´用®?多¨¤边À?形?逼À?近¨¹轮?廓¤a
//然¨?后¨®依°¨¤靠?多¨¤边À?形?轮?廓¤a获?得Ì?包㨹围¡ì多¨¤边À?形?轮?廓¤a的Ì?圆2形? 最Á?小?矩?形? 矩?形?边À?界?
//需¨¨要°a变À?更¨¹的Ì?只?有®D二t值¦Ì化¡¥操¨´作Á¡Â时º¡À的Ì?阈D值¦Ì
//阈D值¦Ì最Á?大䨮值¦Ì 255 最Á?小?值¦Ì可¨¦变À?
Mat srcImage,srcCopyImage,srcGrayImage,srcThresholdImage,DstImage;
vector<vector<Point>>contours;
vector<Vec4i> hierarchys;
const int g_lowThresholdMax = 254;
int g_lowThresholdValue;
int g_upThresholdValue;
void onTrackBarLowThreshold(int pos,void* userData);
RNG g_rng(12345);
int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\contour.jpg");srcCopyImage = srcImage.clone();//转Áa化¡¥RGB为a灰¨°度¨¨图ª?像?if(srcImage.channels() == 3){cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);}else{srcGrayImage = srcImage.clone();}blur(srcGrayImage, srcGrayImage,Size(3,3));namedWindow("src image");namedWindow("threshold image");namedWindow("dst image");g_lowThresholdValue = 80;g_upThresholdValue = 255;createTrackbar("low threshold value", "threshold image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);onTrackBarLowThreshold(g_lowThresholdValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("threshold image", srcImage.cols, 0);moveWindow("dst image", srcImage.cols*2, 0);waitKey(0);return 0;
}void onTrackBarLowThreshold(int pos,void* userData)
{if(g_lowThresholdValue == 0)g_lowThresholdValue = 1;threshold(srcGrayImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue, THRESH_BINARY);//二t值¦Ì化¡¥完ª¨º成¨¦,寻¡ã找¨°轮?廓¤afindContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));//多¨¤边À?形?毕À?竟1轮?廓¤a,先¨¨生¦¨²成¨¦变À?量¢?vector<vector<Point>>contours_polys(contours.size());//多¨¤边À?形?vector<Rect>boundRect(contours.size());//轮?廓¤a最Á?外ªa层?矩?形?边À?界?vector<Point2f>center(contours.size());//最Á?小?面?积y包㨹围¡ì圆2vector<float>radius(contours.size());DstImage = srcCopyImage.clone();for(int i = 0; i < contours.size();i++){//逼À?近¨¹多¨¤边À?形?approxPolyDP(Mat(contours[i]), contours_polys[i], 3, true);//逼À?近¨¹精?度¨¨3且¨°封¤a闭À?//从䨮逼À?近¨¹到Ì?的Ì?多¨¤边À?形?得Ì?到Ì?最Á?外ªa层?矩?形?boundRect[i] = boundingRect(Mat(contours_polys[i]));//从䨮逼À?近¨¹的Ì?多¨¤边À?形?得Ì?到Ì?最Á?小?圆2形?
       minEnclosingCircle(Mat(contours_polys[i]), center[i], radius[i]);}//先¨¨绘?制?轮?廓¤adrawContours(DstImage, contours, -1, Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)));//依°¨¤次ä?在¨²轮?廓¤a上¦?绘?制?矩?形?和¨ª圆2形?for(int i = 0; i < contours.size(); i++){rectangle(DstImage, boundRect[i].tl(), boundRect[i].br(), Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);circle(DstImage, center[i], (int)radius[i], Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);}imshow("threshold image", srcThresholdImage);imshow("dst image", DstImage);
}

四.图像的矩

         图像说到底还是一个矩阵,而矩阵在不同的空间大小的情况下,进行分析的时候就需要用到矩阵的矩,也就是图像的矩,矩函数在图像分析中具有重要的作用,模式识别,目标分类,目标识别方位估计,图像编码重构等都需要用到图像的矩.

         图像的一阶矩与图像的形状相关,二阶矩显示图像中曲线围绕直线平均值的扩展程度,三阶矩是关于平均值的对称性的测量,由二阶矩和三阶矩可以到处七个不变矩,也即是7Hu不变矩.

         不变矩是图像的统计特征,满足平移,旋转,伸缩均不变的不变性,可以用于图像识别.

         另外,通过中心矩也可以计算图像的轮廓长度以及面积.

         API:Moments moments(源图像,bool 非0像素是否全看做1);

         注:源可以是二维数组或者单通道,八位或浮点型

                  第二个参数默认为false,非零像素不全部看作为1

         计算轮廓面积

         API:double contourArea(输入,bool 面向区域标识符)

         输入输入为向量或者是二维点,也可以说Mat,一般是查找到的图像的轮廓点集或者是根据轮廓点集拟合出的矩形,椭圆,圆,多边形,也可以使图像的凸包vector<poit2f>,返回图像的面积,默认为false,表示返回图像的面积是绝对值,不带符号.

         计算轮廓长度

         API:double arcLength(输入点集,bool 指示曲线是否封闭)

         注:输入点集类型与上一个api一致,默认曲线是封闭的.

测试使用图像的中心矩和opencv提供的算法,来计算图像轮廓面积,代码如下

//图ª?像?的Ì?矩?
//canny算?法¤¡§获?得Ì?二t值¦Ì图ª?像? 二t值¦Ì图ª?像?获?得Ì?边À?缘¦Ì 边À?缘¦Ì获?得Ì?边À?缘¦Ì矩? 边À?缘¦Ì矩?获?得Ì?中D心?距¨¤
//通ª¡§过y中D心?距¨¤计?算?图ª?像?的Ì?轮?廓¤a的Ì?面?积y和¨ª距¨¤离¤?

Mat srcImage,srcGrayImage,srcBlurImage,srcThresholdImage,srcCopyImage;vector<vector<Point>>contours;
vector<Vec4i> hierarchys;const int g_lowThresholdMax = 85;
int g_lowThresholdValue;
int g_upThresholdValue;
void onTrackBarLowThreshold(int pos,void* userData);int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\mement.jpg");if(srcImage.channels() == 3){cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);}else{srcGrayImage = srcImage.clone();}blur(srcGrayImage,srcBlurImage,Size(3,3));namedWindow("canny image");g_lowThresholdValue = 80;g_upThresholdValue = g_lowThresholdValue*3;createTrackbar("low threshold value", "canny image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);onTrackBarLowThreshold(g_lowThresholdValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("canny image", srcBlurImage.cols, 0);moveWindow("dst image", srcBlurImage.cols*2, 0);waitKey(0);return 0;
}void onTrackBarLowThreshold(int pos,void* userData)
{Canny(srcBlurImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue,3);//二t进?制?图ª?像?获?取¨?轮?廓¤afindContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));//计?算?矩?vector<Moments>mu(contours.size());for(int i = 0; i < contours.size(); i++){mu[i] = moments(contours[i],false);}//计?算?中D心?矩?vector<Point2f>mc(contours.size());for(int i = 0; i < contours.size(); i++){mc[i] = Point2f(static_cast<float>(mu[i].m10/mu[i].m00),static_cast<float>(mu[i].m01/mu[i].m00));}//绘?制?轮?廓¤a//srcCopyImage = srcImage.clone();srcCopyImage = Mat(srcImage.rows,srcImage.cols,CV_8UC1,Scalar::all(0));drawContours(srcCopyImage, contours, -1, Scalar(255));for(int i = 0; i < contours.size(); i++){circle(srcCopyImage, mc[i], 4, Scalar(255),-1,8,0);}imshow("canny image", srcThresholdImage);imshow("dst image", srcCopyImage);//开a始º?计?算?轮?廓¤a并¡é且¨°输º?出?值¦Ì,通ª¡§过y矩?和¨ªopencv函¡¥数ºy计?算?出?来¤¡ä的Ì?面?积y对?比À¨¨for(int i = 0; i < contours.size();i++){printf("计?算?轮?廓¤a面?积y以°?及¡ã长¡è度¨¨,第̨²%d个?轮?廓¤a的Ì?面?积y为a(矩?计?算?得Ì?出?):%.2f\n通ª¡§过yopencv函¡¥数ºy计?算?出?来¤¡ä的Ì?面?积y为a%.2f\t长¡è度¨¨为a%.2f\n",i,mu[i].m00,contourArea(contours[i],false),arcLength(contours[i],true));}
}

五.分水岭算法

         分水岭算法的主要意义在于分割图像,从背景图像中获得有用信息,比如在一张图像中,前景和背景的像素差异总是很大,此时需要将前景背景分离开来,就需要分水岭算法了.

         分水岭算法市一中基于标记的分割算法,表示的是输入图像的极大值点,在把图像传递给函数之前,需要大致勾画出图像中需要分割的区域,这些标记的值可以使用轮廓查找算法和轮廓绘制算法在图像中标记.

         最终形成的,是由极值点构成的一个一个的区域,如果图像中目标是连在一起的,分割起来有困难,可以使用该算法将黏着在一起的目标分开

         直接用边界来进行分水岭算法的效果不佳,一般来说,先对前后景进行标记,在应用分水岭算法,每个对象内部都是相连的,背景里面的每个像素都不属于任何目标,在应用分水岭算法就会取得较好的效果.

         void waterShed(输入图像,图像掩码);

         注:输入图像必须为八位三通道彩色图像,掩码是运算结果,32位单通道图像,和源图有一样的尺寸和类型,为啥为32位呢,因为一张图像完全可能被分成不止255个区域,那八位就不够用了呀.

         具体效果看如下代码例程

//分水岭算法waterShed

//在源图像中绘制区域线条,同时把绘制的区域线条保存在mask中,然后对mask进行

//轮廓查找算法,找到轮廓以后,在新的掩码中按照不同的轮廓绘制不同的灰度值

//调用分水岭算法,根据结果的不同灰度进行着色,最终的图像和源图像混合生成最终图像

//这就是分水岭算法的意义

//分水岭算法配合膨胀 腐蚀等形态学运算,效果应该很好

Mat srcImage,srcImageCopy;
Mat maskImage,maskImageCopy;
bool draw;
Point2i prevPoint;//记?录?前¡ã一°?个?鼠º¨®标À¨º事º?件t点Ì?的Ì?位?置?
RNG g_rng(12345);
void onMouseEvent(int event,int x,int y,int flag,void* userData);
int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\waterShed.jpg");imshow("src image", srcImage);prevPoint = Point2i(-1,-1);srcImageCopy = srcImage.clone();maskImage = Mat(srcImage.rows,srcImage.cols,CV_32SC1,Scalar::all(0));maskImageCopy = maskImage.clone();setMouseCallback("src image", onMouseEvent);imshow("dst image", maskImage);moveWindow("src image", 0, 0);moveWindow("dst image", srcImage.cols, 0);draw = false;int keyValue = 0;do{keyValue = waitKey(30);if(keyValue == '1')//清?除y图ª?像?
       {draw = false;srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("src image", srcImageCopy);imshow("dst image", maskImageCopy);}else if(keyValue == '2'){//开a始º?计?算?if(draw == true)//有®D轮?廓¤a
           {vector<vector<Point2i>>contours;vector<Vec4i>hierarchy;findContours(maskImageCopy, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//检¨¬查¨¦轮?廓¤aif(contours.size() == 0)continue;//找¨°出?轮?廓¤a以°?后¨®将?轮?廓¤a绘?制?在¨²源¡ämask上¦?//对?不?同ª?的Ì?轮?廓¤a,用®?不?同ª?的Ì?值¦Ì进?行D区?分¤?Mat maskDstImage = Mat(maskImageCopy.size(),CV_32SC1,Scalar::all(0));int c_comp = 0;for(int i = 0 ; i >= 0;i = hierarchy[i][0],c_comp++){//hierarchy[i][0]对?应®|后¨®一°?个?轮?廓¤a//绘?制?轮?廓¤a,轮?廓¤a的Ì?值¦Ì依°¨¤次ä?为a1 2 3 4 5drawContours(maskDstImage, contours, i, Scalar::all(c_comp+1),-1,8,hierarchy,INT_MAX);}//此ä?时º¡À,一°?共2有®Dc_comp个?轮?廓¤a点Ì?,生¦¨²成¨¦scalar数ºy组Á¨¦,用®?于®¨²后¨®期¨²着Á?色¦?vector<Vec3b>colorTab;for (int i = 0; i < c_comp; i++) {int r = g_rng.uniform(0, 255);int g = g_rng.uniform(0, 255);int b = g_rng.uniform(0, 255);colorTab.push_back(Vec3b((uchar)r,(uchar)g,(uchar)b));}//进?行D分¤?水?岭¢?处ä|理¤¨ª算?法¤¡§double startTime = (double)getTickCount();watershed(srcImageCopy, maskDstImage);double endTime = (double)getTickCount();printf("算?法¤¡§使º1用®?时º¡À间?为a%.2f \r\n",((endTime-startTime)*1000)/getTickFrequency());//得Ì?到Ì?了¢?分¤?水?岭¢?图ª?像?以°?后¨®,按ã¡ä照?colortab的Ì?内¨²容¨Y,对?分¤?水?岭¢?图ª?像?进?行D区?域®¨°着Á?色¦?//分¤?水?岭¢?算?法¤¡§的Ì?处ä|理¤¨ª结¨¢果?存ä?放¤?在¨²maskdstImage中D
               Mat dstImage(maskDstImage.size(),CV_8UC3);for(int i = 0 ; i< maskDstImage.rows;i++){for (int j = 0; j < maskDstImage.cols; j++) {int index = maskDstImage.at<int>(i,j);if(index == -1){dstImage.at<Vec3b>(i,j) = Vec3b(255,255,255);}else if(index < 0 || index > c_comp){dstImage.at<Vec3b>(i,j) = Vec3b(0,0,0);}else{dstImage.at<Vec3b>(i,j) = colorTab[index-1];}}}//再¨´把ã?源¡ä图ª?像?和¨ª得Ì?到Ì?的Ì?掩¨²码?图ª?像?混¨¬合?显?示º?新?的Ì?图ª?片?addWeighted(srcImage, 0.5, dstImage, 0.5, 0.0, dstImage);//这a里¤?就¨ª已°?经-得Ì?到Ì?最Á?终?的Ì?maskdst,显?示º?看¡ä看¡äimshow("dst image", dstImage);}else{continue;}}}while(keyValue != 27);return 0;
}void onMouseEvent(int event,int x,int y,int flag,void* userData)
{if(x < 0||y< 0||x>=srcImage.cols||y>=srcImage.rows)return;if(event == EVENT_LBUTTONDOWN){prevPoint = Point2i(x,y);}else if(event == EVENT_LBUTTONUP){prevPoint = Point2i(-1,-1);}else if(event == EVENT_MOUSEMOVE){if(flag&EVENT_FLAG_LBUTTON){//鼠º¨®标À¨º左Á¨®键¨¹滑?动¡¥draw = true;line(srcImageCopy, prevPoint,Point2i(x,y), Scalar::all(255),4,LINE_AA);line(maskImageCopy,prevPoint,Point2i(x,y), Scalar::all(INT_MAX),4,LINE_AA);prevPoint = Point2i(x,y);imshow("dst image", maskImageCopy);imshow("src image", srcImageCopy);}}
}

六.图像修复

        图像修复是指利用那些已经被破坏区域的边沿,即边缘的颜色和结构,通过对它的繁殖和混合,填充的被损坏区域中去,已达到图像修补的目的,注意,边缘损坏过多的图像也是难以修复的,对于小块破损比较有效

         API:void inPaint(原图片,修复区域掩码,输出图片,double inpaintRadius修复算法参考半径,int 修复算法标识符)

         注:1.原图片必须为八位单通道或者是三通道图片,掩码是八位单通道图片,其中那个非零的那些像素点表示需要修复的区域,输出图像必须和源图像有一样的尺寸和类型,最小修复半径修复的每个店所参考的周围区域颜色的半径.

                  2.修复算法标识符表示用什么修复算法进行计算,取值INPAINT_NS基于nerier_stokes方法 ALEXANDRU_TELEA另一种方法.

         修复图片的例子见面的代码

//图ª?像?修T补1
//需¨¨要°a原-图ª? 掩¨²码?图ª? 目?标À¨º图ª?
//修T复¡ä算?法¤¡§参?考?半ã?径?为a3 修T复¡ä方¤?法¤¡§为a INPAINT_TELEA

Mat srcImage,srcImageCopy;
Mat maskImage,maskImageCopy;
bool draw;Point2i prevPoint;//记?录?前¡ã一°?个?鼠º¨®标À¨º事º?件t点Ì?的Ì?位?置?
void onMouseEvent(int event,int x,int y,int flag,void* userData);int main(int argc,char* argv[])
{srcImage = imread("F:\\opencv\\OpenCVImage\\inpaint.jpg");imshow("src image", srcImage);prevPoint = Point2i(-1,-1);srcImageCopy = srcImage.clone();maskImage = Mat(srcImage.rows,srcImage.cols,CV_8UC1,Scalar::all(0));maskImageCopy = maskImage.clone();setMouseCallback("src image", onMouseEvent);imshow("dst image", maskImage);moveWindow("src image", 0, 0);moveWindow("dst image", srcImage.cols, 0);draw = false;int keyValue = 0;do {keyValue = waitKey(30);if(keyValue == '1'){//清?除ydraw = false;srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("src image", srcImageCopy);imshow("dst image", maskImageCopy);}else if(keyValue == '2'){//修T复¡äif(draw == true){Mat srcImageInpaint = srcImageCopy.clone();Mat dstImage = Mat(srcImageInpaint.size(),CV_8UC3,Scalar::all(0));inpaint(srcImageInpaint, maskImageCopy, dstImage, 3, INPAINT_TELEA);srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("dst image", dstImage);}}} while (keyValue != 27);return 0;
}void onMouseEvent(int event,int x,int y,int flag,void* userData)
{if(x < 0||y< 0||x>=srcImage.cols||y>=srcImage.rows)return;if(event == EVENT_LBUTTONDOWN){prevPoint = Point2i(x,y);}else if(event == EVENT_LBUTTONUP){prevPoint = Point2i(-1,-1);}else if(event == EVENT_MOUSEMOVE){if(flag&EVENT_FLAG_LBUTTON){//鼠º¨®标À¨º左Á¨®键¨¹滑?动¡¥draw = true;line(srcImageCopy, prevPoint,Point2i(x,y), Scalar::all(255),4,LINE_AA);line(maskImageCopy,prevPoint,Point2i(x,y), Scalar::all(INT_MAX),4,LINE_AA);prevPoint = Point2i(x,y);imshow("dst image", maskImageCopy);imshow("src image", srcImageCopy);}}
}

 

转载于:https://www.cnblogs.com/dengxiaojun/p/5252294.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 5.7.11 授权_mysql 5.7.11 安装配置教程

六步轻松搞定mysql5.7.11的安装1、下载安装包。mysql-5.7.11版本&#xff1a;2、拷贝到任意盘&#xff1a;例如&#xff0c;解压后拷贝文件夹至C盘&#xff1a;C:\Program Files\mysql。建议文件夹名字使用英文。3、配置环境变量&#xff1a;计算机—>右键—>高级系统设置…

iOS 面试之Block

转自&#xff1a;http://blog.csdn.net/xunyn/article/details/11658261 1 什么是block 对于闭包&#xff08;block),有很多定义&#xff0c;其中闭包就是能够读取其它函数内部变量的函数&#xff0c;这个定义即接近本质又较好理解。对于刚接触Block的同学&#xff0c;会觉得有…

当安全遇到大数据 “永恒之蓝”也将无所遁形!

文章讲的是当安全遇到大数据 “永恒之蓝”也将无所遁形&#xff01;5月12日&#xff0c;席卷全球的勒索病毒“永恒之蓝”让全世界都为之震动&#xff0c;这是迄今为止全球最大规模的勒索病毒网络攻击&#xff0c;100多个国家受到病毒感染&#xff0c;国内中石油、公安内网、高校…

[ES] 安装

1.ElasticSearch安装的准备工作 Linux&#xff1a;CentOS6.4 Elasticsearc:elasticsearch-2.2.0 JDK:jdk-7u79-linux-x64 IK:1.8.0 MAVEN:apache-maven-3.3.3-bin 2.配置网络静态文件 虚拟机设置桥接模式 配置&#xff1a;vim /etc/sysconfig/network-scripts/ifcfg-eth0 DEVIC…

语言基础之description方法

1.description方法的一般用处 1: // 指针变量的地址 2: NSLog("%p", &p); 3: // 对象的地址 4: NSLog("%p", p); 5: // <类名&#xff1a;对象地址> 6: NSLog("%", p); 1: Class c [Person class]; 2: …

亚信安全协助绿谷制药确保“秘方”安全

近几年&#xff0c;我国医药生物技术发展态势迅猛&#xff0c;加强知识产权保护己成为当务之急。为确保制药配方数据和生产管理信息系统安全&#xff0c;上海绿谷制药有限公司采用亚信安全服务器深度安全防护系统&#xff08;Deep Security&#xff09;和亚信安全防毒墙网络版&…

mysql判断叠字_格律诗的八大语法特点

古风的语法&#xff0c;本来就和散文的语法大致相同&#xff0c;直到近体诗&#xff0c;才渐和散文不同&#xff0c;原因是&#xff0c;首先在区区五字或七字之中&#xff0c;要施展丰富的想象&#xff0c;不能不力求简洁&#xff0c;凡可省去而不至于影响语意的字&#xff0c;…

旅游行业春节档期的大数据营销

本文讲的是旅游行业春节档期的大数据营销,虽然我国是以传统农耕文化为主导的社会&#xff0c;每逢春节讲究返乡团聚。但现代化的城市文明更是对很多人的生活方式产生了影响&#xff0c;特别是生活在大城市中的年轻人&#xff0c;以及由年轻人构成的小家庭来说&#xff0c;春节的…

openwrt lamp

https://applefreak111.wordpress.com/2013/03/12/howtoopenwrt-lamp-stack%E5%AE%89%E8%A3%9D/opkg update安裝Lighttpd, MySQL 5, 和PHP 5。opkg install lighttpd lighttpd-mod-cgi lighttpd-mod-fastcgivi /etc/lighttpd/lighttpd.confcgi.assign ( “.php” > “/usr/…

MySQL本天早上8点到明早8点_似乎找到 OSChina 早上 8 点钟容易宕机的原因

最近一段时间&#xff0c;OSChina 网站在早上 8 点出头的时候很容易因为数据库连接池爆满而导致网站宕机。表现的情况是数据库处理大量的查询&#xff0c;堆积大量并发连接&#xff0c;导致无法再连接到数据库&#xff0c;执行一个简单的查询速度也非常慢&#xff0c;数据库机器…

基于Eclipse搭建STM32开源开发环境

最近项目不忙&#xff0c;想着没事看看简单的嵌入式&#xff0c;弄弄物联网什么的。于是就从廉价的STM32开刀了。因为一直是做PC软件开发的&#xff0c;那VS的智能感知那叫一个爽啊&#xff0c;相比之下&#xff0c;觉得这个Keil简直就像文本编辑器一样low。于是想换一个开发环…

数据中心不再有空调、风扇等冷却装置会怎样?

数据中心的变革有望依赖移动设备实现&#xff0c;手机里轻便设备或将成为下一代数据中心的基础设施&#xff0c;服务Google和Facebook等大型的应用程序服务企业。同时&#xff0c;这种商业模式也会构建新一代企业的发展形态&#xff0c;为初创企业带来前所未有的机遇。 CSDN大数…

.NET 数据库缓存依赖策略实现

处理大型门户网站 一般都需要 使用缓存技术这个web加速器在 PHP 和 java 一般 使用的是 基于squid 来做. 当然在 windows .NET 平台也是可以的 squid有 windows版本.这个以后再去研究,现在 就介绍一下 .NET 自带的 缓存策略.Microsoft的petshop就用到了它;  一、基于数据库触…

大数据面临的挑战:当大数据遭遇云计算

本文讲的是大数据面临的挑战&#xff1a;当大数据遭遇云计算,大数据正在彻底改变IT世界。那么&#xff0c;什么样的数据谈得上数据呢? 根据IDC的报告&#xff0c;未来十年全球大数据将增加50倍。仅在2011年&#xff0c;我们就将看到1.8ZB(也就是1.8万亿GB)的大数据创建产生。这…

Climbing Stairs - Print Path

stair climbing&#xff0c; print out all of possible solutions of the methods to climb a stars, you are allowed climb one or two steps for each time; what is time/space complexity? &#xff08;use recursion&#xff09; 这道题难是难在这个ArrayList<Strin…

java 单例设计_Java 之单例设计模式

设计模式: 对问题行之有效的解决方式, 其实它是一种思想.单例设计模式解决的问题:就是可以保证一个类在内存中的对象唯一性. 即单个实例.比如对于A 和 B 两个程序使用同一个配置信息对象时, A 对配置信息作出修改, B 也与之对应的更新配置信息, 即需要保证该对象的唯一性.如何保…

Javascript之RegExp

RegExp对象的构造器 new RegExp(pattern[, flags]) pattern 正则表达式文本flags 该参数可以是下面几个值的任意组合&#xff1a;g 全局匹配i 忽略大小写m 让开始和结束字符&#xff08;^ 和 $&#xff09;工作在多行模式&#xff08;也就是&#xff0c;^ 和 $ 可以匹配字符串中…

DS汽车通过采用沉浸式虚拟现实技术实现展厅转型

PSA集团&#xff08;PSA Group&#xff09;旗下的高端品牌DS汽车公司&#xff08;DS Automobiles&#xff09;采用达索系统的“虚拟车库&#xff08;Virtual Garage&#xff09;”行业解决方案为全新的SUV车型DS 7 CROSSBACK提供全面支持&#xff0c;推动其展厅转型&#xff0c…

java 日历记事本_calendar 一个用java编写的日历记事本. 具有正常日历功能;也可以用于在不同日期记录下当日重要的事情 - 下载 - 搜珍网...

日历记事本/日历记事本/build/classes/日历记事本/CalendarPad$1.class日历记事本/日历记事本/build/classes/日历记事本/CalendarPad.class日历记事本/日历记事本/build/classes/日历记事本/Month.class日历记事本/日历记事本/build/classes/日历记事本/NotePad.class日历记事…

要的需求 ip提取网站源码带采集 要求是PHP源码

求。ip提取网站源码带采集 要求是PHP源码。必须带采集类似 小峰IP提取网站&#xff0c;安小莫IP提取&#xff0c;迷惘IP提取&#xff0c;冰封IP提取免费类型的 不要淘宝类型的 200 转载于:https://www.cnblogs.com/PS-apple/p/4342866.html