涉及API:ios
cv::imread(); //讀取 cv::imshow(); //顯示 cv::cvtColor(); //修改 cv::imwrite(); //保存
擴展圖像窗口建立API:cv::namedWindow();
cv::namedWindow須要兩個參數,第一個參數是窗口名稱,第二個參數是關於窗口操做的關鍵字(包含:WINDOW_AUTOSIZE會根據圖像大小自動設置窗口大小而且生成的窗口大小不能修改;WINDOW_NORMAL此關鍵字通常使用在跟QT集成之後的程序中,表示容許修改窗口大小)算法
cv::imread:
參數兩個,第一個參數是圖像存儲的絕對路徑,第二個參數讀取圖像類型(包含:IMREAD_UNCANGED表示加載原圖;IMREAD_GRAYSCALE表示將圖像做爲灰度圖像加載進來;IMREAD_COLOR表示原圖做爲RGB圖像加載進來)數組
cv::imshow:
兩個參數,第一個參數是圖像窗口名稱(能夠自動建立),第二個參數是Mat對象名安全
cv::cvtColor:
三個參數,第一個是需更改的Mat對象名,第二個是用於保存更改後的Mat對象名,第三個參數是修改使用的源和目標色彩空間(如:COLOR_BGR2GRAY表示修改爲灰度圖像)數據結構
cv::imwrite:
使用時包含兩個參數,第一個參數是保存圖像的絕對路徑,第二個參數是須要保存的Mat對象名dom
獲取圖像像素指針:函數
CV_Assert(image.depth()==CV_8U)
Mat.ptr(int i = 0)獲取像素矩陣的指針,其中索引 i 表示第幾行,從0開始計行數
獲取當前行指針語句:測試
const uchar* current = image.ptr<uchar>(row);
獲取當前像素點P(row,col)的像素值語句:字體
p(row,col) = current[col];
像素範圍處理saturate_cast<uchar>
(重要函數)ui
saturate_cast<uchar>(-100) //返回0 saturate_cast<uchar>(299) //返回255 saturate_cast<uchar>(100) //返回100
說明:此函數的功能是確保RGB值的範圍在0~255之間
什麼是圖像掩膜操做,掩膜操做實現的是圖像對比度調整
opencv提供的掩膜操做(對比度提升)API:filter2D
定義掩膜矩陣:
Mat kernel = (Mat_<char>(3,3)<< 0,-1,0,-1,5,-1,0,-1,0);
API調用舉例:filter2D(img, dst, img.depth(), kernel);
說明:第一個參數是操做對象名,第二個參數保存操做後對象名,第三個參數圖像深度(使用depth()函數獲取了原圖像深度),第三個參數是掩膜方法(對應掩膜矩陣)
如何初始化一個零時Mat對象用於存儲原圖像?
代碼:
Mat dst = Mat::zeros(img.size(), img.type()); //zeros方法表明建立RGB爲0的純黑圖像,大小和類型與原圖像相同
拓展:執行時間的顯示
代碼:
double t = getTickCount(); /* 代碼部分 */ double time = (getTickCount() - t)/getTickFrequency(); // cout << "執行時間: " << time << endl;
Mat對象構造函數:
Mat() Mat(int rows, int cols, int type) Mat(Size size, int type) Mat(int rows, int cols, int type, const Scaler &s) // 說明:前兩個參數分別表示行和列,第三個參數是類型參數(好比CV_8UC3中8表示每一個通道佔8位,U表示無符號,C表示Char類型,3表示三個通道數),第四個參數是向量表示初始化每一個像素值爲多少,向量長度對應通道數目一致。 Mat(Size size, int type, const Scaler &s) //Scaler()用來給像素賦值 Mat(int ndims, const int *sizes, int type) Mat(int ndims, const int *sizes, int type, const Scaler &s)
說明:拷貝構造函數只會賦值對象頭部,使用API–>demo = mat.clone() or mat.copyTo(demo)
才能進行徹底複製(包括數據部分)
經常使用方法:
void copyTo(Mat mat); void convertTo(Mat dst, int type); Mat clone(); int channels(); int depth(); bool empty(); uchar* ptr(i = 0); //備註:查資料詳學(讀取像素值) cv::Mat::create(size, type) //create方法建立對象(可指定對象尺寸大小)
兩種用法:
M.create(img.size(), img.type()); M.create(4,3,CV_8UC2); M = Scaler(123,123);
定義小數組:(掩膜運用)
Mat kernel = (Mat_<char>(3,3)<< 0,-1,0,-1,5,-1,0,-1,0);
初始化全0圖像有多種方法,其中比較特殊的是Mat::zero(size, type);
用法:
Mat m = Mat::zero(img.size(), img.type()); Mat m = Mat::zero(2, 2, CV_8UC1);
拓展:Mat::eye(……)
方法,初始化對角線爲一的圖像矩陣。
讀取像素:
· 讀取一個gray像素點的像素值(CV_8UC1)
Scalar intensity = img.at<uchar>(row, col); or Scalar intensity = img.at<uchar>(Point(row, col));
· 讀取一個RGB像素點的像素值
Vec3f intensity = img.at<Vec3f>(row, col); float blue = intensity.val[0]; float green = intensity.val[1]; float red = intensity.val[2]; for(int row = o; row < img.rows; row++) { for(int col = 0; col < img.cols; col++) { int b = img.at<Vec3b>(row, col)[0]; int g = img.at<Vec3b>(row, col)[1]; int r = img.at<Vec3b>(row, col)[2]; } }
說明:Vec3b是一種數據結構,放置BGR像素點,3b表示3bit讀取,也能夠用Vec3f,3f表示以float類型讀取,如第一種讀取方法。
修改像素:
· 灰度圖像
img.at<uchar>(row, col) = 123;
· RGB圖像
img.at<Vec3b>(row, col)[0] = 123; //修改參數B img.at<Vec3b>(row, col)[1] = 123; //修改參數G img.at<Vec3b>(row, col)[2] = 123; //修改參數R
· 空白圖
img = Scalar(100); //將每一個像素點賦值爲100
· ROI選擇
Rect r(10, 10, 100, 100); Mat smallimg = img(r);
Vec3b與Vec3f
· Vec3b對應三通道順序blue、green、red的uchar類型數據
· Vec3f對應三通道float類型數據
· 把CV_8UC1轉換到CV32F1實現以下:
img.convertTo(dst, CV_32F1); //使用API-->convertTo(dst, type);
· 理論-線性混合操做
g(x) = (1-a)f0(x)+af1(x)
說明:f0(x)表示一個圖像,f1(x)表示另外一個圖像,其中a的取值範圍爲0~1之間,g(x)表示混合後獲得的圖像(注意:對圖像每一個像素的操做)
相關API
cv::addWeighted(inputArray src1, //參數1:輸入圖像Mat - src1 double alpha, //參數2:輸入圖像src1的alpha值(alpha表示表達式中的a) inputArray src2, //參數3:輸入圖像Mat - src2 double beta, //參數4:輸入圖像src2的alpha值 double gamma, //參數5:gamma值(校驗值,使其獲得正常圖像) OutputArray dst, //參數6:輸出混合圖像 int dtpye = -1 //dtpye默認不用帶入 )
注意:兩張圖像大小和類型必須一致纔可使用此API混合
dst(I) = saturate(src1(I) * alpha +src2(I) * beta + gamma);
拓展API:
add(img, dst, dst1); //直接疊加兩個圖像像素 multiply(img, dst, dst1, 1.0); //兩個圖像像素相乘
圖像變換能夠看做像素變換(點操做)和領域操做(區域操做),調整圖像亮度和對比度屬於像素變換
g(i,j) = af(i,j) + β (其中a>0,β是增益變量)
再次回顧重要API:
//像素範圍處理函數
saturate_cast<uchar>(-100),返回0
saturate_cast<uchar>(299),返回255
saturate_cast<uchar>(100),返回100
//圖像數據類型轉換
img.convertTo(dst, CV_32F);
說明:若是圖像默認bit類型,咱們把數據轉換成浮點型,提升圖像精度可使處理效果提升。
· 使用cv::Point
與cv::Scalar
Point表示2D平面上一個點,座標(x,y)
以下:
Point p; p.x = 10; p.y = 8; Point p1 = Point(x, y );
Scalar表示四個元素的向量
Scalar(a, b, c); //a = Blue, b = green, c = Red表示BGR三個通道
· 繪製線、矩形、圓、橢圓等基本幾何形狀
API使用:
線–> cv::line(LINE_4\LINE_8\LINE_AA) //參數表示繪製線的類型,LINE_AA表示反鋸齒
橢圓–> cv::ellipse
橢圓API說明:使用語句示例–>
ellipse(img, Point(img.rows/2, img.cols/2), Size(img.rows/5, img.cols/6), 90, 0, 360, color, 2, 8); /* Point表示橢圓中心座標,size表示橢圓尺寸,其中兩個參數表示長短軸,angle = 90表示順時針方向旋轉角,startAngle = 0表示繪製的起始角度,endAngle = 360表示繪製的終止角度。*/
矩形–> cv::rectangle //五個參數,第一個參數是Mat對象,第二個參數矩形類型,第三個參數顏色,第四個參數線寬(默認1),第五個參數線的類型(默認LINE_8)
圓–> cv::circle
填充–> cv::fillPoly
fillPoly各參數說明:用法–>
Point pts[1][5] = { Point(100,100), Point(100,200), Point(200,200), Point(200,100), Point(100,100) }; const Point* ppts[] = { pts[0] }; int npt[] = { 5 }; fillPoly(img, ppts, npt, 1, Scalar(255,0,255), 8); //ppts表示多邊形各頂點集合(靜態點對象指針數組),npt表示多邊形頂點個數, //ncontours = 1表示填充個數
對象:
Rect rect = Rect(x, y, w_len, h_len); //後兩個參數分別是寬高
· 隨機生成與繪製文本
繪製文本API:cv::putText(Mat&, string, Point, int_fontFace, double_fontScale, Scalar, thickness, lineType, bottomLeftOrigin = false);
代碼示例:
putText(img, "Hello OpenCV", Point(100,100), CV_FONT_HERSHEY_COMPLEX, 1.0, Scalar(0,0,255), 1, 8);
說明:int_fontFace表示字體類型,double_fontScale表示字體縮放比例,bottomLeftOrigin默認爲FALSE不用管。
隨機生成圖像(以畫線爲例)
代碼以下:
void RandomLineDemo(Mat& img) { RNG rng(12345); Point pt1, pt2; Mat dst = Mat::zeros(img.size(), img.type()); namedWindow("test7", CV_WINDOW_AUTOSIZE); for (int i = 0; i < 10000; i++) { pt1.x = rng.uniform(0, img.cols); pt2.x = rng.uniform(0, img.cols); pt1.y = rng.uniform(0, img.rows); pt2.y = rng.uniform(0, img.rows); Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); if (waitKey(50) > 0) break; imshow("test7", dst); line(dst, pt1, pt2, color); } }
說明:RNG是opencv中的隨機數類,構造函數指明隨機數範圍或種子個數,使用uniform(正態分佈隨機數)方法指定隨機數範圍,一樣的也可使用gaussian(double sigma)方法生成高斯隨機數
· 圖像的模糊原理
· 相關API
均值模糊:
blur(Mat src, Mat dst, Size(xradius, yradius), Point(-1, -1)); //Point表示中心像素在哪裏,(-1, -1)表示默認中心像素
高斯濾波:
GaussianBlur(Mat src, Mat dst, Size(11, 11), sigmax, sigmay); //sigmax, sigmay是用於調節正態分佈圖像的參數
注意:其Size(x, y)中x和y必須是正數並且是奇數,size表示窗口大小
· 中值濾波
API:medianBlur (Mat src, Mat dst, ksize)
注意:中值模糊的ksize大小必須是大於1並且爲奇數 --> ksize表示卷積核大小
· 雙邊濾波
API:bilateralFilter (src, dst, d=15, 150, 3)
說明:d=15爲計算半徑,半徑以內的像素都會被歸入計算,若是該參數提供-1則會根據sigma space參數取計算半徑
150表示sigma color,決定多少差值以內像素會被計算
3表示sigma space若是d值大於0則聲明無效
· 膨脹
說明:膨脹操做跟卷積操做相似,假設有圖像A和機構元素B,結構元素B在A上移動,其中B定義其中心爲錨點,計算B覆蓋下A的最大像素值用來替換錨點像素其中B做爲結構體能夠是任意形狀。而腐蝕跟膨脹操做相似,惟一不一樣的是以最小值替換錨點重疊下圖像的像素值。
相關API:
· getStructuringElement(int shape, Size ksize, Point anchor) //獲取結構形狀
說明:三個參數分別表明形狀(MORPH_RECT \ MORPH_CROSS \ MORPH_ELLIPSE)、大小(要求奇數)、錨點(默認是Point(-1,-1)意思就是中心像素)
· dilate(src, dst, kernel) //膨脹
· erode(src, dst, kernel) //腐蝕
拓展:
動態調整結構元素大小 (GUI函數)
TrackBar -> createTrackbar(constString & trackbarname, const String winName, int* value, int count, Trackbarcallback func, void* userdata = 0) //其中最重要的是callback函數功能,若是設置爲NULL就是說只有值update,可是不會調用callback的函數。
· 開操做 - open
說明:圖像先腐蝕後膨脹的操做即稱爲圖像的開操做,圖像開操做能夠去掉小的對象。
· 閉操做 - close
說明: 先膨脹後腐蝕稱之爲閉操做,能夠講大面積圖像中小的缺口給填充。
· 形態學梯度 - Morphological Gradient
說明:圖像膨脹後減去原圖的腐蝕圖像,此種方法又稱基本梯度,獲得的圖像具備梯度效果。
· 頂帽 - top hat
說明:頂帽是原圖像與開操做之間的差值圖像
· 黑帽 - black hat
說明:黑帽是閉操做圖像與源圖像的差值圖像
API:morphologyEx(src, dst, CV_MOP_BLACKHAT, kernel);
參數說明:
- int OP --> CV_MOP_OPEN/ CV_MOP_CLOSE/ CV_MOP_GRADIENT/ CV_MOP_TOPHAT/ CV_MOP_BLACKHAT (表示形態學操做類型)
- Mat kernel --> 結構元素(選取大小取決於去掉的對象大小)
- int Iteration=1 --> 迭代次數,默認爲1
使用實例(開操做):
Mat kernal = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(src, dst, CV_MOP_OPEN, kernal); imshow("dst", dst); waitKey(0);
· 原理方法
圖像形態學操做的時候,能夠經過自定義的結構元素實現結構元素對輸入圖像一些對象敏感、另一些對象不敏感,這樣就會讓敏感的對象改變而不敏感的對象保留輸出。
經過使用兩個最基本的形態學操做:膨脹與腐蝕。使用不一樣的結構元素實現對輸入圖像的操做並獲得想要的結果。
知識回顧:
· 結構元素
膨脹與腐蝕過程是可使用任意的結構元素,常見的形狀:矩形、圓、直線、磁盤形狀、磚石形狀等各類自定義形狀。
· 提取步驟
1 - 輸入圖像
2 - 灰度變換
3 - 二值化 --> adaptiveThreshold
4 - 定義結構元素
5 - 開操做提取水平與垂直線
相關API - adaptiveThreshold(src, dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C);
參數說明:
double maxValue - 輸入圖像最大灰度值
int adaptiveMethod - 自適應閾值方法(ADAPTIVE_THRESH_MEAN_C / ADAPTIVE_THRESH_GAUSSIAN_C)
int thresholdType - 閾值類型(經常使用 THRESH_BINARY)
int blockSize - 子塊大小
double C - 常量,可爲正數、負數、0
結構元素定義實例代碼:
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1)); Mat wline = getStructruingElement(MORPH_RECT, Size(1, src.row . 16), Point(-1, -1));
拓展API:bitwist_not(src, src);
--> 圖像二進制數據「非」操做
· 圖像金字塔概念說明
咱們在圖像處理中經常會調整圖像大下,最多見的就是放大和縮小,儘管幾何變換也能夠實現圖像的放大和縮小,可是這裏所說的
大小調整是指圖像金字塔從下到上分辨率的縮小。一個圖像金字塔是一系列圖像組成,最底下一張是圖像尺寸最大,最上方圖像尺
寸最小,從空間上由上向下看就像古埃及金字塔。
圖像金字塔分爲高斯金字塔和拉普拉斯金字塔,高斯金字塔用來對圖像進行降採樣,拉普拉斯金字塔用來重建一張圖片根據它的上
層降採樣圖片
高斯金字塔:
高斯金字塔是從底向上,逐層採樣獲得的
降採樣以後圖像大小是原圖像 M × N 的 M/2 × N/2,就是對原圖像刪除偶數行與列,即獲得採樣後的上層圖像
高斯金字塔的生成過程分爲兩步:
1 - 對當前層進行高斯模糊
2 - 刪除當前層的偶數行與列
便可獲得上一層圖像,這樣上一層和下一層相比,都只有它大小的1/4
{1, 4, 6,4,1; 4,16,24,16,4; 1/16 × 6,24,36,24,6; 4,16,24,16,4; 1, 4, 6, 4,1;}
高斯不一樣(DOG):
定義:把同一張圖像在不一樣的參數下作高斯模糊以後的結果相減,獲得的輸出圖像稱之爲高斯不一樣。
高斯不一樣是圖像的內在特徵,在灰度圖像加強、角點檢測中常用
代碼示例:
cvtColor(src, gray_src, CV_BGR2GRAY); GaussianBlur(gray_src, g1_dst, Size(3, 3), 0, 0); GaussianBlur(g1_dst, g2_dst, Size(3, 3), 0, 0); subtract(g1_dst, g2_dst, DOGimage, Mat()); normalize(DOGimage, DOGimage, 255, 0, NORM_MINMAX); // 還原灰度範圍 imshow("DOG image", DOGimage); waitKey(0);
相關API:
· 上採用 --> cv::pyrUp(Mat src, Mat dst, Size(src.cols*2, src.rows*2))
效果:生成的圖像是原圖在寬和高各放大兩倍
· 降採樣 --> cv::pyrDown(Mat src, Mat dst, Size(src.col/2, src.rows/2))
效果:生成的圖像是原圖在寬與高各縮小1/2
閾值操做:二值化、反二值化、截斷、閾值取零、閾值反取零
API–>cv::threshold(img, dst, thresh, max_value, type);
說明:type參數表示閾值操做類型,可填寫cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_TNV等。
·OpenCV中經常使用的邊緣填充函數爲copyMakeBorder();
函數原型:void copyMakeBorder(const Mat &src, Mat& dst, int top, int bottom, int left, int right, int borderType, const Scalar &value=Scalar());
功能:擴充src的邊緣,將圖像變大,而後以各類外插方式自動填充圖像邊界,這個函數實際上調用了函數cv::borderInterpolate,這個函數最重要的功能就是爲了處理邊界,好比均值濾波或者中值濾波中,使用copyMakeBorder將原圖稍微放大,而後咱們就能夠處理邊界的狀況。
參數說明:
src,dst:原圖與目標圖像
top,bottom,left,right分別表示在原圖四周擴充邊緣的大小
borderType:擴充邊緣的類型,就是外插的類型,OpenCV中給出如下幾種方式
BORDER_REPLICATE
BORDER_REFLECT
BORDER_REFLECT_101
BORDER_WRAP
BORDER_CONSTANT
BORDER_REPLICATE:邊緣像素複製法
BORDER_REFLECT_101:對稱法,以最邊緣像素爲軸,對稱
BORDER_CONSTANT:常量法
API:
cv::Sobel(img, dst, depth, dx, dy); // dx和dy分別表示x方向和y方向上的差分階數
cv::Laplacian(img, dst, depth);
· Canny算法五步走:
一、高斯模糊 - GaussianBlur
二、灰度轉換 - cvtColor
三、計算梯度 - Sobel / Scharr
四、非最大信號抑制
五、高低閾值鏈接輸出二值圖像
什麼是非最大信號抑制:
圖像梯度幅值矩陣中的元素值越大,說明圖像中該點的梯度值越大,但這不不能說明該點就是邊緣(這僅僅是屬於圖像加強的過程)。在Canny算法中,非極大值抑制是進行邊緣檢測的重要步驟,通俗意義上是指尋找像素點局部最大值,將非極大值點所對應的灰度值置爲0,這樣能夠剔除掉一大部分非邊緣的點。
根據圖可知,要進行非極大值抑制,就首先要肯定像素點C的灰度值在其8值鄰域內是否爲最大。圖中藍色的線條方向爲C點的梯度方向,這樣就能夠肯定其局部的最大值確定分佈在這條線上,也即出了C點外,梯度方向的交點dTmp1和dTmp2這兩個點的值也可能會是局部最大值。所以,判斷C點灰度與這兩個點灰度大小便可判斷C點是否爲其鄰域內的局部最大灰度點。若是通過判斷,C點灰度值小於這兩個點中的任一個,那就說明C點不是局部極大值,那麼則能夠排除C點爲邊緣。這就是非極大值抑制的工做原理。
注意如下兩點:
1)中非最大抑制是回答這樣一個問題:「當前的梯度值在梯度方向上是一個局部最大值嗎?」 因此,要把當前位置的梯度值與梯度方向上兩側的梯度值進行比較;
2)梯度方向垂直於邊緣方向。
但實際上,咱們只能獲得C點鄰域的8個點的值,而dTmp1和dTmp2並不在其中,要獲得這兩個值就須要對該兩個點兩端的已知灰度進行線性插值,也即根據圖中的g1和g2對dTmp1進行插值,根據g3和g4對dTmp2進行插值,這要用到其梯度方向,這是Canny算法中要求解梯度方向矩陣Thita的緣由。
完成非極大值抑制後,會獲得一個二值圖像,非邊緣的點灰度值均爲0,可能爲邊緣的局部灰度極大值點可設置其灰度爲128。根據下文的具體測試圖像能夠看出,這樣一個檢測結果仍是包含了不少由噪聲及其餘緣由形成的假邊緣。所以還須要進一步的處理。
高低閾值鏈接:
· API介紹
cv::Canny(InputArray src, // 8bit輸入圖像 OutputArray edges, // 輸出邊緣圖像,通常都是二值圖像,背景爲黑色 double threshold1, // 低閾值,常取高閾值的1/2或者1/3 double threshold2, // 高閾值 int aptertureSize, // Sobel算子size,一般爲3×3,取值3 bool L2gradient // 選擇true表示是L2歸一化方法,不然使用L1方法 )
· 相關代碼
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using std::cout; using std::endl; const int t1_value = 50, max_value = 255; Mat src, dst, gray_src; void Canny_Demo(int, void*) { Mat edge_output; blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT); Canny(gray_src, edge_output, t1_value, t1_value*2, 3, false); dst.create(src.size(), src.type()); src.copyTo(dst, edge_output); // imshow("output", dst); imshow("out", edge_output); } int main(int argc, char** argv) { src = imread("Path"); if(!src.data) { cout << "could not load image" << endl; return -1; } imshow("input", src); cvtColor(src, gray_src, CV_BGR2GRAY); createTrackbar("Threshold Value", "Result", &t1_value, max_value, Canny_Demo); Canny_Demo(0, 0); waitKey(0); return 0; }
霍夫變換算法簡介:霍夫變換運用兩個座標空間之間的變換,將在一個空間中具備相同形狀的曲線或直線映射到另外一個座標空間的一個點上造成峯值,從而把檢測任意形狀的問題轉化爲統計峯值問題,在第二空間出現的峯值點經過反算方法再映射回原有空間中就獲得須要檢測圖像上的像素點。
算法實現前提:邊緣檢測已經完成
霍夫直線檢測使用的第二空間是極座標空間,映射圖示以下:
一、圖像座標系到極座標系參數空間轉化過程
說明:從上面能夠看到,參數空間的每一個點(ρ,θ)都對應了圖像空間的一條直線,或者說圖像空間的一個點在參 數空間中就對應爲一條曲線。參數空間採用極座標系,這樣就能夠在參數空間表示原始空間中的全部直線了。 此時圖像空間(直角座標系x-y)上的一個點對應到參數空間(極座標系ρ-θ)上是一條曲線,確切的說是一條 正弦曲線。
二、圖像空間到極座標空間的轉換過程:
這樣就把在圖像空間中檢測直線的問題轉化爲在極座標參數空間中找經過點(r,θ)的最多正弦曲線數的問題。霍 夫空間中,曲線的交點次數越多,所表明的參數越肯定,畫出的圖形越飽滿。
相關API介紹
· 標準的霍夫變換cv::HoughLines
從平面座標轉換到霍夫空間,最終輸出是(θ, r)表示極座標空間
· 霍夫變換直線機率cv::HoughLinesP
最終輸出是直線的兩個點(x0, y0, x1, y1)
cv::HoughLines(InputArray src, // 輸入圖像,必須是8-bit灰度圖 OutputArray lines, // 輸出的極座標來表示直線 double rho, // 生成極座標時候的像素掃描步長 double theta, // 生成極座標時候角度步長,通常取值CV_PI/180 int threshold, // 閾值,只有得到足夠交點的極座標點才被當作是直線 double srn=0, // 是否應用多尺度霍夫變換,默認值爲0表示經典霍夫變換 double stn=0, // 同上 double min_theta=0, // 表示多角度掃描範圍0~180之間,默認便可 double max_theta=CV_PI ) // 通常狀況是有經驗的開發者使用,須要本身反變換到平面空間
cv::HoughLinesP(InputArray src, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, // 最小直線長度 double maxLineGap=0 // 最大間隔 ) // 經常使用API
代碼演示
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using std::cout; using std::endl; Mat src, src_gray, dst; int main(int argc, char** argv) { src = imread("Path"); if(!src.data) { cout << "could not load image" << endl; return -1; } imshow("input", src); // 邊緣檢測及灰度轉換 Canny(src, src_gray, 150, 200); cvtColor(src_gray, dst, CV_GRAY2BGR); imshow("edge", src_gray); // 檢測並標註直線 vector<Vec4f> pLines; HoughLinesP(src_gray, pLines, 1, CV_PI/180.0, 10, 0, 10); Scalar color = Scalar(0, 0, 255); for(size_t i = 0; i < pLines.size(); i++) { Vec4f hline = pLines[i]; line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA); } imshow("output", dst); waitKey(0); return 0; }