opencv形状识别学习总结
OpenCV基元檢測 Primitive Detection
?
目錄
基元的概念
基元泛指圖像中有特點的單元。常說的基元有:邊緣、角點、斑點、直線段、圓、等
基元檢測是圖像分析的基礎
?
?
?
?
邊緣(Edge)檢測
?
?
?
?
邊緣是圖像中像素灰度值發生劇烈變化而不連續的結果
邊緣是賦予單個像素的一種性質,與圖像函數在該像素的一個鄰域內的梯度特性相關
邊緣幅值:梯度的幅值
邊緣方向:梯度方向旋轉-90度
?
邊緣檢測既是常見基元檢測的基礎,也是基于邊界的圖像分割的第一步。
?
邊緣檢測算法
OpenCV邊緣檢測:Sobel、拉普拉斯算子
OpenCV邊緣檢測:坎尼算子算子
斑點(Blob)檢測
斑點:與周圍灰度有一定差別的區域
面部的雀斑
衛星照片中的一棵數
鋼材X光照片中的雜質或氣泡
?
醫學圖像中的細微腫塊
?
?
斑點檢測算法
OpenCV LoG算子:SIFT算法
OpenCV Blob特征檢測算子
角點(Conner)檢測
角點:物體的拐角、交叉點、 曲線上曲率最大的點等
?
角點的鄰域是圖像中信息比較豐富的區域
?
?
角點檢測方法
基于邊緣的方法:在小鄰域內有兩個不同的主邊緣方向,實際圖像中,孤立點、線段端點也會有類似特
性。缺點是:1)需要先提取邊緣并編碼,計算量大;2)局部變化對穩定性影響大。
?
基于灰度的方法:計算點的曲率和梯度,目前的主流
?
?
角點檢測算法:
OpenCV 角點檢測:Harris算子
?
?
?
哈夫變換-幾何形狀檢測
?
?
?
?
基本哈夫變換:直線檢測
點–線對偶性:直線所在的圖像空間(記為XY)和參數空間PQ(p斜率,q截距)之間的一一映射
XY空間中的直線檢測就等同于PQ空間的點檢測
基本哈夫變換:曲線檢測
對于任意能夠用f(x,c)=0(其中x是圖像點坐標矢量,c是參數矢量)表示曲線或目標輪廓,均可用類似
的方法檢測,只是計算復雜度隨著c維數的增加而增加,需要考慮降維
廣義哈夫變換:目標檢測
問題:待檢目標不是參數化曲線(如正方形),而只是一組輪廓點,希望自動檢測目標的存在及其中心
參考點(p,q)
廣義哈夫變換能夠檢測到特定目標的位置(即參考點(p,q) ),或者說任意位置的待檢目標都是可以發
現的,滿足平移不變性
?
多尺度檢測
?
萬物都有其合適的尺度
原子和基本粒子:普朗克常數
集成電路:微米、納米
人、車、樹、建筑:米-厘米-毫米
地理:千米
太空:光年
多分辨率 與 尺度空間
多分辨率( 圖像金字塔):(低通濾波)再下采樣,多級進行形成金字塔;可能出現假結構.
尺度空間(Wikin’83):用一列單參數、寬度遞增的高斯濾波器將原始信號濾波而得到的一組低頻信號
;高斯核是實現尺度變換的唯一變換核,具有多種優良性質,不會引入假信號
OpenCV 尺度空間與圖像金字塔
http://blog.csdn.net/xiaowei_cqu
========
利用opencv識別并提取圖片中的矩形
?
這次是利用opencv來識別圖片中的矩形。?
其中遇到的問題主要是識別輪廓時矩形內部的形狀導致輪廓不閉合。?
過程如下:
1. 對輸入灰度圖片進行高斯濾波?
2. 做灰度直方圖,提取閾值,做二值化處理?
3. 提取圖片輪廓?
4. 識別圖片中的矩形?
5. 提取圖片中的矩形
1.對輸入灰度圖片進行高斯濾波
? ? cv::Mat src = cv::imread("F:\\t13.bmp",CV_BGR2GRAY);
? ? cv::Mat hsv;
? ? GaussianBlur(src,hsv,cv::Size(5,5),0,0);
2.做灰度直方圖,提取閾值,做二值化處理?
由于給定圖片,背景是黑色,矩形背景色為灰色,矩形中有些其他形狀為白色,可以參考為:?
提取輪廓時,矩形外部輪廓并未閉合。因此,我們需要對整幅圖做灰度直方圖,找到閾值,進行二值化
處理。即令像素值(黑色)小于閾值的,設置為0(純黑色);令像素值(灰色和白色)大于閾值的,設
置為255(白色)
?
?
?
?
?
?
// Quantize the gray scale to 30 levels int gbins = 16; int histSize[] = {gbins};// gray scale varies from 0 to 256 float granges[] = {0,256}; const float* ranges[] = { granges }; cv::MatND hist; // we compute the histogram from the 0-th and 1-st channels int channels[] = {0};//calculate hist calcHist( &hsv, 1, channels, cv::Mat(), // do not use maskhist, 1, histSize, ranges,true, // the histogram is uniformfalse ); //find the max value of hist double maxVal=0; minMaxLoc(hist, 0, &maxVal, 0, 0);int scale = 20; cv::Mat histImg; histImg.create(500,gbins*scale,CV_8UC3);//show gray scale of hist image for(int g=0;g<gbins;g++){float binVal = hist.at<float>(g,0);int intensity = cvRound(binVal*255);rectangle( histImg, cv::Point(g*scale,0),cv::Point((g+1)*scale - 1,binVal/maxVal*400),CV_RGB(0,0,0),CV_FILLED ); } cv::imshow("histImg",histImg);//threshold processing cv::Mat hsvRe; threshold( hsv, hsvRe, 64, 255,cv::THRESH_BINARY);?
?
?
3.提取圖片輪廓?
為了識別圖片中的矩形,在識別之前還需要提取圖片的輪廓。在經過濾波、二值化處理后,輪廓提取后
的效果比未提取前的效果要好很多。
4.識別矩形?
識別矩形的條件為:圖片中識別的輪廓是一個凸邊形、有四個頂角、所有頂角的角度都為90度。?
具體可以參考:?
http://opencv-code.com/tutorials/detecting-simple-shapes-in-an-image/
?
?
?
?
?
vector<Point> approx;for (size_t i = 0; i < contours.size(); i++) {approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);if (approx.size() == 4 &&fabs(contourArea(Mat(approx))) > 1000 &&isContourConvex(Mat(approx))){double maxCosine = 0;for( int j = 2; j < 5; j++ ){double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));maxCosine = MAX(maxCosine, cosine);}if( maxCosine < 0.3 )squares.push_back(approx);} }?
5.提取圖片中的矩形?
由于圖片中矩形傾斜角度不太大,所以沒有做傾斜校正這步操作?
這是主函數提取部分代碼
?
?
?
這是計算每個矩形的點x,y坐標和長寬值
?
?
?
?
?
?
int* findRectInfo(std::vector<cv::Point> rect) {int rectInfo[4] = {0};int x[4]= {0},y[4]= {0};int maxX = 0,maxY = 0,minX = 2000,minY = 2000;//get the rect pointsfor(int i=0;i<4;i++){x[i] = rect[i].x;y[i] = rect[i].y;if(maxX<x[i])maxX = x[i];if(maxY<y[i])maxY = y[i];if(minX>x[i])minX = x[i];if(minY>y[i])minY = y[i];}rectInfo[0] = minY;rectInfo[1] = minX;rectInfo[2] = maxY - minY;rectInfo[3] = maxX - minX;return rectInfo; }
識別并提取結果為:?
?
利用opencv識別并提取圖片中的矩形
Reference:?
1.http://opencv-code.com/tutorials/detecting-simple-shapes-in-an-image/?
2.http://stackoverflow.com/questions/8667818/opencv-c-obj-c-detecting-a-sheet-of-paper-
square-detection?
3.http://blog.csdn.net/timidsmile/article/details/8519751?
4.http://blog.163.com/lee_020/blog/static/1247556020136473917915/
========
OpenCV實現Hough變換檢測圓形
? ? ? 在圖像處理中,Hough變換(霍夫變換)主要用來識別已知的幾何形狀,最常見的比如直線、線段
、圓形、橢圓、矩形等。如果要檢測比較復雜的曲線圖形,就需要利用廣義霍夫變換。
? ? ? 霍夫變換的原理是根據參數空間的統計規律進行參數估計。
? ? ? 具體說來就是,將直角坐標系中的圖形(x,y)變換到參數空間(k1,...,kn),對直角坐標系中的每
一個像素點,計算它在參數空間里的所有可能的參數向量。處理完所有像素點后,把出現次數(頻率)
最多的(一個或幾個)參數向量的坐標作為參數代入直角坐標方程,即檢測到的圖形方程。
? ? ? 以直線檢測為例,詳細講一下步驟:(圓和直線的原理相同,只是直線的公式比較好打~)
? ? ? 1.圖像二值化,待檢測的線變為黑色,背景置為白色。既然是形狀檢測,這步是必不可少的。
? ? ? 2.假設直線的參數方程為p=x*cosa+y*sina,對于直線上的某個點(x,y)來說,變換到參數空間的
坐標就是(p,a),而且這條直線上的所有點都對應于(p,a)。對于一個固定點(x,y)來說,經過它的直線系
可以表示為p=(x^2+y^2)^1/2*sin(a+b),其中tanb=x/y,對應參數空間里的一條正弦曲線。也就是說,
圖像中的一條直線對應參數空間的一點,圖像中的一點對應參數空間的一條正弦曲線。
? ? ??
關于參數變換,我再白話幾句。如果直線方程寫成y=k*x-b,則對應于參數空間里的點(k,-b),這就有點
像圖論中的對偶變換了。在寫圖的程序時有時會遇到若干半平面求交的問題(整張平面被一條直線分割
后得到兩張半平面)。半平面求交關鍵在于找到求交后的邊界(如果交集非空),既可以使用遞增式算
法(在已經找到的一部分邊界基礎上引入下一張半平面的直線求下一步的邊界),也可以使用上面提到
的參數變換方法。
? ? ? 比如我想求幾張方向都朝上(y軸正方向)的半平面的交,我想得到的應該是一個下側以向下凸的
折線為邊界的上側無窮的區域。我的問題關鍵在于找到這條下凸的折線。直線y=k*x-b做參數變換,得到
點(k,-b),所有半平面的邊界直線經變換得到若干個點。這些點形成的點集存在一個凸包(包含點集的
最小凸多邊形,而且該多邊形每個頂點都來自點集),其中構成折線的直線所對應的點恰好是凸包的上
半部分,也就是“下包絡”變換成上凸包。而求點集的上凸包可是很簡單的(也是增量式算法)。
? ? ? 3.把參數空間分割為n*m個格子,得到參數矩陣,矩陣元(pi,aj)的初始值均為0,用來對參數計數
。計數值代表這個參數是最終結果的可能性,計數值越大,說明落在這條直線上的像素點越多,也就說
明它越有可能是我們想找到的參數。p的范圍可以是[0,圖像對角線長度],a的范圍可以是[0,PI/2](如
果取左上角為原點的話),但要包含整個圖像。
? ? ? 4.按照柵格順序掃描圖像,遇到黑色像素就做如下操作:
? ? ? ? pi的i從0取到n-1,對每一個pi,把它和像素點的坐標(x,y)代入參數方程,計算得到相應的ai
,如果ai在定義域范圍內(或者在圖像內),將矩陣元(pi,ai)加一。
? ? ? ? 處理完所有像素后,如果想識別d條直線,就在參數矩陣中找到前d個數值最大的矩陣元,他們
的坐標作為方程參數,在直角坐標系繪制出直線就可以了。
? ? ? OpenCV中提供了計算霍夫變換的庫函數HoughLines和HoughLinesP,想知道怎樣使用,請戳傳送門
。
? ? ? 圓形檢測的過程很類似,只是參數方程有變化,而且參數空間增加了一個維度(圓心坐標x,y和半
徑r)。
? ? ? 霍夫變換的一個好處就是不需要圖像中出現完整的圓,只要落在一個圓上的像素數量足夠多,就
能正確識別。
? ? ? 關于誤差的問題:如果待檢測像素沒有嚴格落在同一個圓上,比如構成圓的圓弧彼此有些錯位,
如果依據參數點最多準則,只會識別出弧長最長的圓弧而忽略其他本來也屬于同一個圓的圓弧。如果目
標是檢測不止一個圓,這種誤差可能會使得程序依據同一個圓上的幾個圓弧識別到幾個不同的圓。解決
這個問題一種方法是仍然采用參數點最多準則,但減小參數空間分割的份數,讓錯位圓弧的圓心落在同
一個參數矩陣元上,但這樣做會使檢測到的圓心位置有比較大的誤差。另一種方法是仍然把參數空間細
密分割,用聚類算法尋找可能的圓心,因為錯位圓弧的圓心彼此靠得很近而且計數值都很大,只要找到
這些點的外接圓圓心就可以了。
? ? ? 下面為了計算簡便,我給出只檢測一個半徑為100的圓形的代碼(要想采用聚類算法,只需修改第
71-81行的代碼塊):
?
?
? ? ? 以下是檢測示例:(左中右分別為原圖像、參數空間圖像和檢測結果,檢測結果用藍色線繪制)
? ? ? ? ?由于固定半徑r,所以參數就是圓心位置(x,y),繪制的點代表圓心的可能位置,顏色越淺,可
能性越大。
? ? ? ? 檢測單獨的圓
? ? ? ? 在很亂的線中檢測不完整的圓
? ? ? ? 檢測彼此錯位的圓弧(參數劃分擴大為5*5)
========
?
?
opencv 檢測直線、線段、圓、矩形
http://blog.csdn.net/byxdaz/archive/2009/12/01/4912136.aspx
檢測直線:cvHoughLines,cvHoughLines2
檢測圓:cvHoughCircles
檢測矩形:opencv中沒有對應的函數,下面有段代碼可以檢測矩形,是通過先找直線,然后找到直線平
行與垂直的四根線。
檢測直線代碼:
/* This is a standalone program. Pass an image name as a first parameter of the program.
? ?Switch between standard and probabilistic Hough transform by changing "#if 1" to "#if 0"?
and back */
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main(int argc, char** argv)
{
? ? const char* filename = argc >= 2 ? argv[1] : "pic1.png";
? ? IplImage* src = cvLoadImage( filename, 0 );
? ? IplImage* dst;
? ? IplImage* color_dst;
? ? CvMemStorage* storage = cvCreateMemStorage(0);
? ? CvSeq* lines = 0;
? ? int i;
? ? if( !src )
? ? ? ? return -1;
??
? ? dst = cvCreateImage( cvGetSize(src), 8, 1 );
? ? color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
? ?
? ? cvCanny( src, dst, 50, 200, 3 );
? ? cvCvtColor( dst, color_dst, CV_GRAY2BGR );
#if 0
? ? lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 100, 0, 0 );
?
? ? for( i = 0; i < MIN(lines->total,100); i++ )
? ? {
? ? ? ? float* line = (float*)cvGetSeqElem(lines,i);
? ? ? ? float rho = line[0];
? ? ? ? float theta = line[1];
? ? ? ? CvPoint pt1, pt2;
? ? ? ? double a = cos(theta), b = sin(theta);
? ? ? ? double x0 = a*rho, y0 = b*rho;
? ? ? ? pt1.x = cvRound(x0 + 1000*(-b));
? ? ? ? pt1.y = cvRound(y0 + 1000*(a));
? ? ? ? pt2.x = cvRound(x0 - 1000*(-b));
? ? ? ? pt2.y = cvRound(y0 - 1000*(a));
? ? ? ? cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, CV_AA, 0 );
? ? }
#else
? ? lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10?
);
? ? for( i = 0; i < lines->total; i++ )
? ? {
? ? ? ? CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
? ? ? ? cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
? ? }
#endif
? ? cvNamedWindow( "Source", 1 );
? ? cvShowImage( "Source", src );
?
? ? cvNamedWindow( "Hough", 1 );
? ? cvShowImage( "Hough", color_dst );
? ? ?cvWaitKey(0);
? ? ?return 0;
}
檢測圓代碼:
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main(int argc, char** argv)
{
? ? IplImage* img;
? ? if( argc == 2 && (img=cvLoadImage(argv[1], 1))!= 0)
? ? {
? ? ? ? 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 );
? ? ? ? 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 );
? ? ? ? }
? ? ? ? cvNamedWindow( "circles", 1 );
? ? ? ? cvShowImage( "circles", img );
? ? }
? ? return 0;
}
檢測矩形代碼:
/*在程序里找尋矩形*/
#ifdef _CH_
#pragma package <opencv>
#endif
?#ifndef _EiC
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#endif
int thresh = 50;
IplImage* img = 0;
IplImage* img0 = 0;
CvMemStorage* storage = 0;
CvPoint pt[4];
const char* wndname = "Square Detection Demo";
// helper function:
// finds a cosine of angle between vectors
// 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);
}
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
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 ); // make a copy of input image
? ? 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;
? ? // create empty sequence that will contain points -
? ? // 4 points per square (the square's vertices)
? ? CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
? ? // select the maximum ROI in the image
? ? // with the width and height divisible by 2
? ? cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));?
? ? // down-scale and upscale the image to filter out the noise
? ? 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++ )
? ? { ? ?
? ? ? ? ?// extract 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 );?
? ? ? ? ? ?}?
? ? ? ? ?// find contours and store them all as a list
? ? ? ? ? ? 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 );
? ? ? ? ? ? ? ? // square contours should have 4 vertices after approximation
? ? ? ? ? ? ? ? // relatively large area (to filter out noisy contours)
? ? ? ? ? ? ? ? // and be convex.
? ? ? ? ? ? ? ? // Note: absolute value of an area is used because
? ? ? ? ? ? ? ? // area may be positive or negative - in accordance with the
? ? ? ? ? ? ? ? // contour orientation
? ? ? ? ? ? ? ? if( result->total == 4 &&
? ? ? ? ? ? ? ? ? ? fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&
? ? ? ? ? ? ? ? ? ? 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 cosines of all angles are small?
? ? ? ? ? ? ? ? // (all angles are ~90 degree) then write quandrange
? ? ? ? ? ? ? ? ? ? // vertices to resultant sequence
? ? ? ? ? ? ? ? ? ? ?if( s < 0.3 )
? ? ? ? ? ? ? ? ? ? ? ? for( i = 0; i < 4; i++ )?
? ? ? ? ? ? ? ? ? ? ? ? ? ?cvSeqPush( squares,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(CvPoint*)cvGetSeqElem( result, i ));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // take the next contour?
? ? ? ? ? ? ? ?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 += 4 )
? ? {
? ? ? ? CvPoint* rect = pt;
? ? ? ? int count = 4;
? ? ? ? // read 4 vertices
? ? ? ? memcpy( pt, reader.ptr, squares->elem_size );
? ? ? ? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
? ? ? ? memcpy( pt + 1, reader.ptr, squares->elem_size );
? ? ? ? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
? ? ? ? memcpy( pt + 2, reader.ptr, squares->elem_size );
? ? ? ? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
? ? ? ? memcpy( pt + 3, reader.ptr, squares->elem_size );
? ? ? ? CV_NEXT_SEQ_ELEM( squares->elem_size, reader );
? ? ? ? // draw the square as a closed polyline
? ? ? ? ?cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
? ? }
? ?// show the resultant image
? ? cvShowImage( wndname, cpy );
? ? cvReleaseImage( &cpy );
}?
?
?void on_trackbar( int a )
{
? ? if( img )
? ? ? ? drawSquares( img, findSquares4( img, storage ) );
}
char* names[] = { "pic1.png", "pic2.png", "pic3.png",
?
?
?
? ? ? ? ? ? ? ? ? "pic4.png", "pic5.png", "pic6.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++ )
? ? {
? ? ? ? // load i-th image
? ? ? ? img0 = cvLoadImage( names[i], 1 );
? ? ? ? if( !img0 )
? ? ? ? {
? ? ? ? ? ? printf("Couldn't load %s/n", names[i] );
? ? ? ? ? ? continue;
? ? ? ? }
? ? ? ? img = cvCloneImage( img0 );
? ? ? ? // create window and a trackbar (slider) with parent "image" and set callback
? ? ? ? // (the slider regulates upper threshold, passed to Canny edge detector)
? ? ? ? ?cvNamedWindow( wndname, 1 );
? ? ? ? cvCreateTrackbar( "canny thresh", wndname, &thresh, 1000, on_trackbar );
?
? ? ? ? // force the image processing
? ? ? ? on_trackbar(0);
? ? ? ? // wait for key.
? ? ? ? // Also the function cvWaitKey takes care of event processing
? ? ? ? c = cvWaitKey(0);
? ? ? ? // release both images
? ? ? ? cvReleaseImage( &img );
? ? ? ? cvReleaseImage( &img0 );
? ? ? ? // clear memory storage - reset free space position
? ? ? ? cvClearMemStorage( storage );
? ? ? ? if( c == 27 )
? ? ? ? ? ? break;
? ? }?
? ? ? ?cvDestroyWindow( wndname );?
? ? ? ?return 0;
}
?#ifdef _EiC
main(1,"squares.c");
#endif
其它參考博客:
1、http://blog.csdn.net/superdont/article/details/6664254
2、http://hi.baidu.com/%CE%C4%BF%A1%B5%C4%CF%A3%CD
%FB/blog/item/3a5cb2079158b304738b65f2.html
#include <cv.h>
#include <highgui.h>
#include <math.h>
int main()
{
? ? IplImage* src;
if( (src=cvLoadImage("5.bmp", 1)) != 0)
? ? {
? ? ? ? IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );
? ? ? ? IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
? ? ? ? CvMemStorage* storage = cvCreateMemStorage(0);//存儲檢測到線段,當然可以是N*1的矩陣
數列,如果
實際的直線數量多余N,那么最大可能數目的線段被返回
? ? ? ? CvSeq* lines = 0;
? ? ? ? int i;
IplImage* src1=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
cvCvtColor(src, src1, CV_BGR2GRAY); //把src轉換成灰度圖像保存在src1中,注意進行邊緣檢測一定
要
換成灰度圖
? ? ? ? cvCanny( src1, dst, 50, 200, 3 );//參數50,200的灰度變換
? ? ? ? cvCvtColor( dst, color_dst, CV_GRAY2BGR );
#if 1
? ? ? ? lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 150, 0, 0?
);//標準霍夫變
換后兩個參數為0,由于line_storage是內存空間,所以返回一個CvSeq序列結構的指針
? ? ? ? for( i = 0; i < lines->total; i++ )
? ? ? ? {
? ? ? ? ? ? float* line = (float*)cvGetSeqElem(lines,i);//用GetSeqElem得到直線
? ? ? ? ? ? float rho = line[0];
? ? ? ? ? ? float theta = line[1];//對于SHT和MSHT(標準變換)這里line[0],line[1]是rho(與像素
相關單位的距
離精度)和theta(弧度測量的角度精度)
? ? ? ? ? ? CvPoint pt1, pt2;
? ? ? ? ? ? double a = cos(theta), b = sin(theta);
? ? ? ? ? ? if( fabs(a) < 0.001 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pt1.x = pt2.x = cvRound(rho);
? ? ? ? ? ? ? ? pt1.y = 0;
? ? ? ? ? ? ? ? pt2.y = color_dst->height;
? ? ? ? ? ? }
? ? ? ? ? ? else if( fabs(b) < 0.001 )
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pt1.y = pt2.y = cvRound(rho);
? ? ? ? ? ? ? ? pt1.x = 0;
? ? ? ? ? ? ? ? pt2.x = color_dst->width;
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pt1.x = 0;
? ? ? ? ? ? ? ? pt1.y = cvRound(rho/b);
? ? ? ? ? ? ? ? pt2.x = cvRound(rho/a);
? ? ? ? ? ? ? ? pt2.y = 0;
? ? ? ? ? ? }
? ? ? ? ? ? cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
? ? ? ? }
#else
? ? ? ? lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 80, 30,?
10 );
? ? ? ? for( i = 0; i < lines->total; i++ )
? ? ? ? {
? ? ? ? ? ? CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
? ? ? ? ? ? cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
? ? ? ? }
#endif
? ? ? ? cvNamedWindow( "Source", 1 );
? ? ? ? cvShowImage( "Source", src );
? ? ? ? cvNamedWindow( "Hough", 1 );
? ? ? ? cvShowImage( "Hough", color_dst );
? ? ? ? cvWaitKey(0);
? ? }
}
line_storage?
檢測到的線段存儲倉. 可以是內存存儲倉 (此種情況下,一個線段序列在存儲倉中被創建,并且由函數
返回),或者是包含線段參數的特殊類型(見下面)的具有單行/單列的矩陣(CvMat*)。矩陣頭為函數所
修改,使得它的 cols/rows 將包含一組檢測到的線段。如果 line_storage 是矩陣,而實際線段的數目
超過矩陣尺寸,那么最大可能數目的線段被返回(線段沒有按照長度、可信度或其它指標排序).?
method?
Hough 變換變量,是下面變量的其中之一:?
CV_HOUGH_STANDARD - 傳統或標準 Hough 變換. 每一個線段由兩個浮點數 (ρ, θ) 表示,其中 ρ 是
直線與原點 (0,0) 之間的距離,θ 線段與 x-軸之間的夾角。因此,矩陣類型必須是 CV_32FC2 type.?
CV_HOUGH_PROBABILISTIC - 概率 Hough 變換(如果圖像包含一些長的線性分割,則效率更高). 它返回
線段分割而不是整個線段。每個分割用起點和終點來表示,所以矩陣(或創建的序列)類型是?
CV_32SC4.?
CV_HOUGH_MULTI_SCALE - 傳統 Hough 變換的多尺度變種。線段的編碼方式與 CV_HOUGH_STANDARD 的一
致。?
rho?
與象素相關單位的距離精度?
theta?
弧度測量的角度精度?
threshold?
閾值參數。如果相應的累計值大于 threshold, 則函數返回的這個線段.?
param1?
第一個方法相關的參數:?
對傳統 Hough 變換,不使用(0).?
對概率 Hough 變換,它是最小線段長度.?
對多尺度 Hough 變換,它是距離精度 rho 的分母 (大致的距離精度是 rho 而精確的應該是 rho /?
param1 ).?
param2?
第二個方法相關參數:?
對傳統 Hough 變換,不使用 (0).?
對概率 Hough 變換,這個參數表示在同一條直線上進行碎線段連接的最大間隔值(gap), 即當同一條直
線上的兩條碎線段之間的間隔小于param2時,將其合二為一。?
對多尺度 Hough 變換,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精確的角度應該是?
theta / param2).
函數 cvHoughLines2 實現了用于線段檢測的不同 Hough 變換方法. Example. 用 Hough transform 檢
測線段
3、http://www.opencv.org.cn/index.php/Hough%E7%BA%BF%E6%AE%B5%E6%A3%80%E6%B5%8B
========
?
opencv形狀分析
OpenCV支持大量的輪廓、邊緣、邊界的相關函數,相應的函數有moments、HuMoments、findContours、
drawContours、approxPolyDP、arcLength、boundingRect、contourArea、convexHull、fitEllipse、
fitLine、isContourConvex、minAreaRect、minEnclosingCircle、mathcShapes、pointPolygonTest。
還有一些c版本的針對老版本的數據結構的函數比如cvApproxChains、cvConvexityDefects。這里先介紹
一些我用過的函數,以后用到再陸續補充。
OpenCV里支持很多邊緣提取的辦法,可是如何在一幅圖像里得到輪廓區域的參數呢,這就需要用到
findContours函數,這個函數的原型為:
//C++: ? ?
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray?
hierarchy, int mode, int method, Point offset=Point()) ?
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int?
method, Point offset=Point()) ?
[cpp] view plain copy
//C++: ??
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray?
hierarchy, int mode, int method, Point offset=Point()) ?
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int?
method, Point offset=Point()) ?
這里介紹下該函數的各個參數:
輸入圖像image必須為一個2值單通道圖像
contours參數為檢測的輪廓數組,每一個輪廓用一個point類型的vector表示
hiararchy參數和輪廓個數相同,每個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ][ 0 ]?
~hierarchy[ i ][ 3 ],分別表示后一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有
對應項,該值設置為負數。
mode表示輪廓的檢索模式
CV_RETR_EXTERNAL表示只檢測外輪廓
CV_RETR_LIST檢測的輪廓不建立等級關系
CV_RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,里面的一層為內孔的邊界信息。如果內孔內
還有一個連通物體,這個物體的邊界也在頂層。
CV_RETR_TREE建立一個等級樹結構的輪廓。具體參考contours.c這個demo
method為輪廓的近似辦法
CV_CHAIN_APPROX_NONE存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2)
,abs(y2-y1))==1
CV_CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點坐標,例
如一個矩形輪廓只需4個點來保存輪廓信息
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
offset表示代表輪廓點的偏移量,可以設置為任意值。對ROI圖像中找出的輪廓,并要在整個圖像中進行
分析時,這個參數還是很有用的。
具體應用參考sample文件夾下面的squares.cpp這個demo
findContours后會對輸入的2值圖像改變,所以如果不想改變該2值圖像,需創建新mat來存放,
findContours后的輪廓信息contours可能過于復雜不平滑,可以用approxPolyDP函數對該多邊形曲線做
適當近似
contourArea函數可以得到當前輪廓包含區域的大小,方便輪廓的篩選
findContours經常與drawContours配合使用,用來將輪廓繪制出來。其中第一個參數image表示目標圖像
,第二個參數contours表示輸入的輪廓組,每一組輪廓由點vector構成,第三個參數contourIdx指明畫
第幾個輪廓,如果該參數為負值,則畫全部輪廓,第四個參數color為輪廓的顏色,第五個參數
thickness為輪廓的線寬,如果為負值或CV_FILLED表示填充輪廓內部,第六個參數lineType為線型,第
七個參數為輪廓結構信息,第八個參數為maxLevel
得到了復雜輪廓往往不適合特征的檢測,這里再介紹一個點集凸包絡的提取函數convexHull,輸入參數
就可以是contours組中的一個輪廓,返回外凸包絡的點集
還可以得到輪廓的外包絡矩形,使用函數boundingRect,如果想得到旋轉的外包絡矩形,使用函數
minAreaRect,返回值為RotatedRect;也可以得到輪廓的外包絡圓,對應的函數為minEnclosingCircle
;想得到輪廓的外包絡橢圓,對應的函數為fitEllipse,返回值也是RotatedRect,可以用ellipse函數
畫出對應的橢圓
如果想根據多邊形的輪廓信息得到多邊形的多階矩,可以使用類moments,這個類可以得到多邊形和光柵
形狀的3階以內的所有矩,類內有變量m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,比如多邊
形的質心為 x = m10 / m00,y = m01 / m00。
如果想獲得一點與多邊形封閉輪廓的信息,可以調用pointPolygonTest函數,這個函數返回值為該點距
離輪廓最近邊界的距離,為正值為在輪廓內部,負值為在輪廓外部,0表示在邊界上。
========
?
?
實用OpenCV ?圖像中的形狀
?
形狀是當我們看到物體時最開始的印象之一,這一章我們將賦予計算機這種能力。識別圖像里的形狀是
通常是做決策時一個重要步驟。形狀是由圖像的輪廓形成的,所以理論上形狀識別是通常在邊緣或輪廓
檢測后的步驟。
所以,我們將首先討論從圖像里提取輪廓,然后再開始討論形狀。將會包含:
?霍夫變換,可以使我們檢測圖像里的常規形狀如線條和圓形。
?隨機樣本一致性(RANSAC),一個廣泛使用的可以確定數據點來匹配特定模型的框架。我們將編寫算法
代碼來檢測圖像里的橢圓。
?對象周圍的綁定盒,綁定橢圓和凸形外殼的計算。
?形狀匹配。
輪廓
輪廓和邊緣有一個顯著區別。邊緣是圖像亮度梯度的局部極大值集合。我們也看到,這些梯度極大值不
全是在物體的輪廓上而且他們是非常有噪聲的。Canny邊緣有一點不同,更像輪廓一些,因為在梯度極大
值提取后經過一些后處理步驟。輪廓,相對而言,是一系列相連的點,更可能落在物體的外框上。
OpenCV的輪廓提取基于二值圖像(像Canny邊緣檢測的輸出或對Scharr邊緣做閾值處理或者一張黑白圖)
然后提取邊緣點連接的層次結構。組織層次使得位于數結構更高的輪廓更有可能是物體的輪廓,然而低
位的輪廓更有可能是噪聲邊緣和“洞口”的輪廓以及噪聲塊。
實現這些特性的函數叫findContours()然后它使用了由S.Suzuki和K.Abe在“數字二值圖像的基于邊界跟
隨的拓撲結構分析”一文中描述的算法來提取輪廓并排列層次結構。文中描述了決定層次結構的詳細規
則,簡而言之呢,當一個輪廓圍繞著另一個輪廓的時候被認為是那個輪廓的“父親”。
為了更實際地顯示我們所說的層次結構呢,我們將編寫一個程序,見例6-1,使用了我們最喜歡的工具,
滑塊,來選擇要顯示層次結構的級別值。注意該函數僅接受一個二值圖像為輸入。從普通圖像得到二值
圖的方式有:
?通過threshold()或adaptiveThreshold()來閾值處理
?使用inRange()檢查像素值邊界
?Canny邊緣
?Scharr邊緣做閾值處理
例 6-1 程序展現層次輪廓提取
// Program to illustrate hierarchical contour extraction
// Author: Samarth Manoj Brahmbhatt, University of Pennsylvania
#include <opencv2 opencv.hpp="">
#include <opencv2 highgui="" highgui.hpp="">
#include <opencv2 imgproc="" imgproc.hpp="">
using namespace std;
using namespace cv;
Mat img;
vector<vector<point> > contours;
vector<vec4i> heirarchy;
int levels = 0;
void on_trackbar(int, void *) {
? if(contours.empty()) return;
? Mat img_show = img.clone();
? // Draw contours of the level indicated by slider
? drawContours(img_show, contours, -1, Scalar(0, 0, 255), 3, 8, heirarchy, levels);
? imshow("Contours", img_show);
}
int main() {
? img = imread("circles.jpg");
? Mat img_b;
? cvtColor(img, img_b, CV_RGB2GRAY);
? Mat edges;
? Canny(img_b, edges, 50, 100);
? // Extract contours and heirarchy
? findContours(edges, contours, heirarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
? namedWindow("Contours");
? createTrackbar("levels", "Contours", &levels, 15, on_trackbar);
? // Initialize by drawing the top level contours (as 'levels' is initialized to 0)
? on_trackbar(0, 0);
? while(char(waitKey(1)) != 'q') {}
? return 0;
}
</vec4i></vector<point></opencv2></opencv2></opencv2>
注意每個輪廓是一個STL向量里的點。所以,儲存輪廓的數據結構是一個含點向量的向量。層次結構是一
個含四整數向量的向量(注:即向量的元素也是向量)。對每個輪廓來說,它的層次結構位置表示為四
個整數值:他們是輪廓向量基于0的索引分別指示 下一位置(同等級),上一個(同等級),父級,以
及第一個子輪廓。假使其中任意一個不存在(比如,如果一個輪廓沒有父輪廓),對應的整數值則為負
值。同時注意drawContours()函數根據層次結構和繪制允許最大層次等級來通過繪制輪廓修改輸入圖片
。
圖6-1顯示了一張簡圖上的不同等級的輪廓。
\\
\
圖6-1 不同等級的輪廓層次
經常和findContours()一起使用的一個函數是approxPolyDP()。approxPolyDP()用另一條頂點較少的曲
線來逼近一條曲線或者一個多邊形,這樣兩條曲線之間的距離小于或等于指定的精度。同時也有使閉合
逼近曲線的選項(那就是說,起始點和終止點相同)
點-多邊形測試
我們先暫且來介紹一個有趣的特性:點-多邊形測試。你也許猜到了,pointPolygonTest()函數判定一個
點是否在一個多邊形內。如果你開啟 measureDist標簽的話它也會返回該點到輪廓最近點的有符號歐式
距離。如果點在曲線內,距離則為正,外則負,點在輪廓上則為零。如果標簽關閉的話,相應的距離則
被替換為+1,-1和0。
讓我們來做一個程序來演示點-多邊形和閉合曲線逼近的新知識——一個尋找圖像上用戶點擊點相近的最
小閉合輪廓的程序。同時它也演示了輪廓層次的導引。代碼見例6-2
例6-2 尋找點擊點圍繞的最小輪廓
<"http://www.2cto.com/kf/ware/vc/" target="_blank"?
class="keylink">vcD4KPHByZSBjbGFzcz0="brush:java;">// Program to find the smallest contour?
that surrounds the clicked point // Author: Samarth Manoj Brahmbhatt, University of?
Pennsylvania #include #include #include using namespace std; using namespace cv; Mat?
img_all_contours; vector > closed_contours; vector heirarchy; // Function to approximate?
contours by closed contours vector > make_contours_closed(vector > contours) { vector >?
closed_contours; closed_contours.resize(contours.size()); for(int i = 0; i < contours.size
(); i++) approxPolyDP(contours[i], closed_contours[i], 0.1, true); return closed_contours;?
} // Function to return the index of smallest contour in 'closed_contours' surrounding the?
clicked point int smallest_contour(Point p, vector > contours, vector heirarchy) { int idx?
= 0, prev_idx = -1; while(idx >= 0) { vector c = contours[idx]; // Point-polgon test double?
d = pointPolygonTest(c, p, false); // If point is inside the contour, check its children?
for an even smaller contour... if(d > 0) { prev_idx = idx; idx = heirarchy[idx][2]; } //?
...else, check the next contour on the same level else idx = heirarchy[idx][0]; } return?
prev_idx; } void on_mouse(int event, int x, int y, int, void *) { if(event !=?
EVENT_LBUTTONDOWN) return; // Clicked point Point p(x, y); // Find index of smallest?
enclosing contour int contour_show_idx = smallest_contour(p, closed_contours, heirarchy);?
// If no such contour, user clicked outside all contours, hence clear image if
(contour_show_idx < 0) { imshow("Contours", img_all_contours); return; } // Draw the?
smallest contour using a thick red line vector > contour_show; contour_show.push_back
(closed_contours[contour_show_idx]); if(!contour_show.empty()) { Mat img_show =?
img_all_contours.clone(); drawContours(img_show, contour_show, -1, Scalar(0, 0, 255), 3);?
imshow("Contours", img_show); } } int main() { Mat img = imread("circles.jpg");?
img_all_contours = img.clone(); Mat img_b; cvtColor(img, img_b, CV_RGB2GRAY); Mat edges;?
Canny(img_b, edges, 50, 100); // Extract contours and heirarchy vector > contours;?
findContours(edges, contours, heirarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); // Make?
contours closed so point-polygon test is valid closed_contours = make_contours_closed
(contours); // Draw all contours usign a thin green line drawContours(img_all_contours,?
closed_contours, -1, Scalar(0, 255, 0)); imshow("Contours", img_all_contours); // Mouse?
callback setMouseCallback("Contours", on_mouse); while(char(waitKey(1)) != 'q') {} return?
0; } 假設 idx為輪廓在點向量的向量中的索引而hierarchy代表層次的話:
? hierarchy[idx][0] 返回同等級層次結構的下一個輪廓索引
? hierarchy[idx][1] 返回同等級層次結構的上一個輪廓索引
? hierarchy[idx][2] 返回第一個子輪廓的索引
? hierarchy[idx][3] 返回父輪廓的索引
如果其中一個輪廓不存在,返回索引為負值。
程序運行的截圖如圖6-2所示
圖 6-2 最小封閉輪廓的應用
OpenCV也提供了一些函數檢查其中的一些屬性來幫助你過濾噪聲圖像的輪廓。如表6-1
表6-1 OpenCV輪廓后處理函數
函數 描述
ArcLength() 查找輪廓長度
ContourArea() 查找輪廓區域和方向
BoundingRect() 計算輪廓的垂直邊界矩形
ConvexHull() 計算輪廓圍繞的凸形殼
IsContourConvex() 測試輪廓的凸性
MinAreaRect() 計算圍繞輪廓的最小旋轉矩形
MinEnclosingCircle() 查找圍繞輪廓的最小區域圓形
FitLine() 基于輪廓匹配一條線(最小二乘)
========
?
?
opencv識別正方形(矩形)代碼
//正方形檢測源碼
//載入數張包含各種形狀的圖片,檢測出其中的正方形
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <iostream>
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[] = { "pic1.png", "pic2.png", "pic3.png",
? ? ?"pic4.png", "pic5.png", "pic6.png","pic7.png","pic8.png",
? ? ?"pic9.png","pic10.png","pic11.png","pic12.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;
}
========
?
?
OpenCV入門之線段檢測
線段檢測主要運用Hough變換,Hough變換是圖像處理中從圖像中識別幾何形狀的基本方法之一,應用很
廣泛,也有很多改進算法。主要用來從圖像中分離出具有某種相同特征的幾何形狀(如,直線,圓等)
。最基本的霍夫變換是從黑白圖像中檢測直線(線段)。
在OpenCV編程中,實現線段檢測主要使用cvHoughLines2函數。
函數原型:
CvSeq* cvHoughLines2(
? CvArr* image,
? void* line_storage,
? int method,
? double rho,
? double theta,
? int threshold,
? double param1=0, double param2=0
);
參數說明:
第一個參數表示輸入圖像,必須為二值圖像(黑白圖)。
第二個參數表示存儲容器,可以傳入CvMemStorage類型的指針。
第三個參數表示變換變量,可以取下面的值:
? CV_HOUGH_STANDARD - 傳統或標準 Hough 變換. 每一個線段由兩個浮點數 (ρ, θ) 表示,其中 ρ?
是線段與原點 (0,0) 之間的距離,θ 線段與 x-軸之間的夾角。
? CV_HOUGH_PROBABILISTIC - 概率 Hough 變換(如果圖像包含一些長的線性分割,則效率更高)。它返
回線段分割而不是整個線段。每個分割用起點和終點來表示。
? CV_HOUGH_MULTI_SCALE - 傳統 Hough 變換的多尺度變種。線段的編碼方式與 CV_HOUGH_STANDARD 的
一致。
第四個參數表示與象素相關單位的距離精度。
第五個參數表示弧度測量的角度精度。
第六個參數表示檢測線段的最大條數,如果已經檢測這么多條線段,函數返回。
第七個參數與第三個參數有關,其意義如下:
? 對傳統 Hough 變換,不使用(0).
? 對概率 Hough 變換,它是最小線段長度.
? 對多尺度 Hough 變換,它是距離精度 rho 的分母 (大致的距離精度是 rho 而精確的應該是 rho /?
param1 ).
第八個參數與第三個參數有關,其意義如下:
? 對傳統 Hough 變換,不使用 (0).
? 對概率 Hough 變換,這個參數表示在同一條線段上進行碎線段連接的最大間隔值(gap), 即當同一條
線段上的兩條碎線段之間的間隔小于param2時,將其合二為一。
? 對多尺度 Hough 變換,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精確的角度應該是?
theta / param2)。
示例程序:
hough.cpp
?#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
int main (int argc, char **argv) ?
{ ? ??
const char *pstrWindowsSrcTitle = "initial";
const char *pstrWindowsLineName = "hough";
IplImage *pSrcImage = cvLoadImage("hough.jpg", CV_LOAD_IMAGE_UNCHANGED);
IplImage *pGrayImage = ?cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); ?
cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); ?
cvCanny(pGrayImage, pCannyImage, 30, 90);
CvMemStorage *pcvMStorage = cvCreateMemStorage(); ?
double fRho = 1; ?
double fTheta = CV_PI / 180; ?
int nMaxLineNumber = 50; //最多檢測條直線
double fMinLineLen = 50; //最小線段長度
double fMinLineGap = 10; //最小線段間隔
CvSeq *pcvSeqLines = cvHoughLines2(pCannyImage, pcvMStorage, CV_HOUGH_PROBABILISTIC, fRho,?
fTheta, nMaxLineNumber, fMinLineLen, fMinLineGap);
IplImage *pColorImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);
cvCvtColor(pCannyImage, pColorImage, CV_GRAY2BGR);
int i;
for(i = 0; i < pcvSeqLines->total; i++) ?
{ ?
CvPoint* line = (CvPoint*)cvGetSeqElem(pcvSeqLines, i); ?
cvLine(pColorImage, line[0], line[1], CV_RGB(255,0,0), 2);
? }
cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); ?
cvShowImage(pstrWindowsSrcTitle, pSrcImage); ?
cvNamedWindow(pstrWindowsLineName, CV_WINDOW_AUTOSIZE); ?
cvShowImage(pstrWindowsLineName, pColorImage); ?
??
cvWaitKey(0); ?
??
cvReleaseMemStorage(&pcvMStorage); ?
cvDestroyWindow(pstrWindowsSrcTitle); ?
cvDestroyWindow(pstrWindowsLineName); ?
cvReleaseImage(&pSrcImage); ?
cvReleaseImage(&pGrayImage); ?
cvReleaseImage(&pCannyImage); ?
cvReleaseImage(&pColorImage); ?
return 0; ?
}
makefile:
?INCLUDE = $(shell pkg-config --cflags opencv) ?
LIBS = $(shell pkg-config --libs opencv) ?
SOURCES = hough.cpp ?
# 目標文件 ?
OBJECTS = $(SOURCES:.cpp=.o) ?
# 可執行文件 ?
TARGET = hough ?
$(TARGET):$(OBJECTS) ?
g++ -o $(TARGET) $(OBJECTS) -I $(INCLUDE) $(LIBS) ?
$(OBJECTS):$(SOURCES) ?
g++ -c $(SOURCES) ?
clean: ?
rm $(OBJECTS) $(TARGET) ?
# 編譯規則 $@代表目標文件 $< 代表第一個依賴文件 ?
%.o:%.cpp ?
g++ -I $(INCLUDE) -o $@ -c $<
所在文件夾上已有hough.jpg圖片,make后執行./hough hough.jpg
========
?
總結
以上是生活随笔為你收集整理的opencv形状识别学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: opencv角点检测学习总结
- 下一篇: 电力管理信息系统数据库表总结