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值|ì化?¥圖a?像?
//濾?波?§算?法¤?§選?擇?均¨′值|ì濾?波?§ 可¨|調ì??的ì?是o?孔?á徑?尺?寸??
//canny算?法¤?§可¨|調ì??的ì?上|?下?限T和¨asobel算?子á¨?孔?á徑?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低ì¨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改?了¢?濾?波?§參?數oy
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孔?á徑?參?數oy
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值|ì參?數oy
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()是凸包的點的個數.

???????? 一般尋找凸包,主要是先對圖像二值化,后尋找輪廓,然后尋找一條指定輪廓的凸包.

示例代碼如下

//對?一°?張?圖a?片?進?行D輪?廓¤a查¨|找¨°,并?é對?每?一°?個?查¨|找¨°出?來¤??的ì?輪?廓¤a運?行D凸a1包?¨1查¨|找¨°程¨?序¨°,生|¨2成¨|最á?終?顯?示o?圖a?片?
//邊à?緣|ì輪?廓¤a查¨|找¨°
//先¨¨進?行D邊à?緣|ì濾?波?§ 然¨?后¨?用??canny算?法¤?§得ì?到ì?二t值|ì化?¥圖a?像?
//濾?波?§算?法¤?§選?擇?均¨′值|ì濾?波?§ 可¨|調ì??的ì?是o?孔?á徑?尺?寸??
//canny算?法¤?§可¨|調ì??的ì?上|?下?限T和¨asobel算?子á¨?孔?á徑?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低ì¨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改?了¢?濾?波?§參?數oy
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孔?á徑?參?數oy
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值|ì參?數oy
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使用例程如下

//首o?á先¨¨查¨|找¨°輪?廓¤a,再¨′用??多¨¤邊à?形?逼à?近¨1輪?廓¤a
//然¨?后¨?依°¨¤靠?多¨¤邊à?形?輪?廓¤a獲?得ì?包?¨1圍?ì多¨¤邊à?形?輪?廓¤a的ì?圓2形? 最á?小?矩?形? 矩?形?邊à?界?
//需¨¨要°a變à?更¨1的ì?只?有?D二t值|ì化?¥操¨′作á??時o?à的ì?閾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灰¨°度¨¨圖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值|ì化?¥完a¨o成¨|,尋??找¨°輪?廓¤afindContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));//多¨¤邊à?形?畢à?竟1輪?廓¤a,先¨¨生|¨2成¨|變à?量¢?vector<vector<Point>>contours_polys(contours.size());//多¨¤邊à?形?vector<Rect>boundRect(contours.size());//輪?廓¤a最á?外aa層?矩?形?邊à?界?vector<Point2f>center(contours.size());//最á?小?面?積y包?¨1圍?ì圓2vector<float>radius(contours.size());DstImage = srcCopyImage.clone();for(int i = 0; i < contours.size();i++){//逼à?近¨1多¨¤邊à?形?approxPolyDP(Mat(contours[i]), contours_polys[i], 3, true);//逼à?近¨1精?度¨¨3且¨°封¤a閉à?//從?¨?逼à?近¨1到ì?的ì?多¨¤邊à?形?得ì?到ì?最á?外aa層?矩?形?boundRect[i] = boundingRect(Mat(contours_polys[i]));//從?¨?逼à?近¨1的ì?多¨¤邊à?形?得ì?到ì?最á?小?圓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)));//依°¨¤次??在¨2輪?廓¤a上|?繪?制?矩?形?和¨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提供的算法,來計算圖像輪廓面積,代碼如下

//圖a?像?的ì?矩?
//canny算?法¤?§獲?得ì?二t值|ì圖a?像? 二t值|ì圖a?像?獲?得ì?邊à?緣|ì 邊à?緣|ì獲?得ì?邊à?緣|ì矩? 邊à?緣|ì矩?獲?得ì?中D心?距¨¤
//通a?§過y中D心?距¨¤計?算?圖a?像?的ì?輪?廓¤a的ì?面?積y和¨a距¨¤離¤?

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進?制?圖a?像?獲?取¨?輪?廓¤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始o?計?算?輪?廓¤a并?é且¨°輸o?出?值|ì,通a?§過y矩?和¨aopencv函?¥數oy計?算?出?來¤??的ì?面?積y對?比ਨfor(int i = 0; i < contours.size();i++){printf("計?算?輪?廓¤a面?積y以°?及??長?è度¨¨,第ì¨2%d個?輪?廓¤a的ì?面?積y為a(矩?計?算?得ì?出?):%.2f\n通a?§過yopencv函?¥數oy計?算?出?來¤??的ì?面?積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;//記?錄?前??一°?個?鼠o¨?標à¨o事o?件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圖a?像?
       {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始o?計?算?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繪?制?在¨2源??mask上|?//對?不?同a?的ì?輪?廓¤a,用??不?同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);}//此??時o?à,一°?共2有?Dc_comp個?輪?廓¤a點ì?,生|¨2成¨|scalar數oy組á¨|,用??于?¨2后¨?期¨2著á?色|?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分¤?水?嶺¢?處?|理¤¨a算?法¤?§double startTime = (double)getTickCount();watershed(srcImageCopy, maskDstImage);double endTime = (double)getTickCount();printf("算?法¤?§使o1用??時o?à間?為a%.2f \r\n",((endTime-startTime)*1000)/getTickFrequency());//得ì?到ì?了¢?分¤?水?嶺¢?圖a?像?以°?后¨?,按???照?colortab的ì?內¨2容¨Y,對?分¤?水?嶺¢?圖a?像?進?行D區?域?¨°著á?色|?//分¤?水?嶺¢?算?法¤?§的ì?處?|理¤¨a結¨¢果?存??放¤?在¨2maskdstImage中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];}}}//再¨′把??源??圖a?像?和¨a得ì?到ì?的ì?掩¨2碼?圖a?像?混¨?合?顯?示o?新?的ì?圖a?片?addWeighted(srcImage, 0.5, dstImage, 0.5, 0.0, dstImage);//這a里¤?就¨a已°?經-得ì?到ì?最á?終?的ì?maskdst,顯?示o?看??看??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){//鼠o¨?標à¨o左á¨?鍵¨1滑?動?¥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另一種方法.

???????? 修復圖片的例子見面的代碼

//圖a?像?修T補1
//需¨¨要°a原-圖a? 掩¨2碼?圖a? 目?標à¨o圖a?
//修T復??算?法¤?§參?考?半??徑?為a3 修T復??方¤?法¤?§為a INPAINT_TELEA

Mat srcImage,srcImageCopy;
Mat maskImage,maskImageCopy;
bool draw;Point2i prevPoint;//記?錄?前??一°?個?鼠o¨?標à¨o事o?件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){//鼠o¨?標à¨o左á¨?鍵¨1滑?動?¥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.pswp.cn/news/257583.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257583.shtml
英文地址,請注明出處:http://en.pswp.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