OpenCv VS C++ 影象處理(下)
繼續OpenCv的影象處理
對於上一節的inRange得到兩幅影象等情況,可以使用addWeighted處理。
(1).然後講形態學濾波
#include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\opencv.hpp> using namespace cv; int main() { VideoCapture capture(0); //【1】從攝像頭讀入視訊 while (1) { Mat frame; //定義一個Mat變數,用於儲存每一幀的影象 capture >> frame; //讀取當前幀 Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //自定義卷積核 morphologyEx(frame, frame, MORPH_OPEN, element); imshow("A",frame); waitKey(30); //延時30ms } return 0; }
getStructuringElement函式會返回指定形狀和尺寸的結構元素。
這個函式的第一個引數表示核心的形狀,有三種形狀可以選擇。矩形:MORPH_RECT;
交叉形:MORPH_CORSS;
橢圓形:MORPH_ELLIPSE;
第二和第三個引數分別是核心的尺寸以及錨點的位置。
然後就可以使用形態學濾波(morphologyEx)
表示形態學運算的型別:
MORPH_OPEN – 開運算(Opening operation)
MORPH_CLOSE – 閉運算(Closing operation)
MORPH_GRADIENT - 形態學梯度(Morphological gradient)
MORPH_TOPHAT - 頂帽(Top hat)
MORPH_BLACKHAT - 黑帽(Black hat)
(2).然後再說說輪廓識別
影象的邊緣檢測的原理是檢測出影象中所有灰度值變化較大的點,而且這些點連線起來就構成了若干線條,這些線條就可以稱為影象的邊緣#include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\opencv.hpp> using namespace cv; int alpha_slider1, alpha_slider2; void on_trackbar(int, void*) {} int main() { namedWindow("[總]"); createTrackbar("Cmin", "[總]", &alpha_slider1, 255, on_trackbar); createTrackbar("Cmax", "[總]", &alpha_slider2, 255, on_trackbar); VideoCapture capture(0); //【1】從攝像頭讀入視訊 while (1) { Mat frame; //定義一個Mat變數,用於儲存每一幀的影象 capture >> frame; //讀取當前幀 cvtColor(frame,frame,COLOR_RGB2GRAY); //轉化為灰度圖 Canny(frame,frame, alpha_slider1, alpha_slider2); imshow("A",frame); waitKey(30); //延時30ms } return 0; }
函式原型:
void cvCanny(
const CvArr* image, //第一個引數表示輸入影象,必須為單通道灰度圖
CvArr* edges, //第二個引數表示輸出的邊緣影象,為單通道黑白圖
double threshold1,
double threshold2, //第三個引數和第四個引數表示閾值,這二個閾值中當中的小閾值用來控制邊緣連線,大的閾值用來控制強邊緣的初始分割即如果一個畫素的梯度大與上限值,則被認為 是邊緣畫素,如果小於下限閾值,則被拋棄。如果該點的梯度在兩者之間則當這個點與高於上限值的畫素點連線時我們才保留,否則刪除。
int aperture_size=3 //第五個引數表示Sobel 運算元大小,預設為3即表示一個3*3的矩陣。Sobel 運算元與高斯拉普拉斯運算元都是常用的邊緣運算元
);
(3).輪廓提取
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int alpha_slider1=50, alpha_slider2=50;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[總]");
createTrackbar("Cmin", "[總]", &alpha_slider1, 255, on_trackbar);
createTrackbar("Cmax", "[總]", &alpha_slider2, 255, on_trackbar);
VideoCapture capture(0); //【1】從攝像頭讀入視訊
while (1)
{
Mat frame; //定義一個Mat變數,用於儲存每一幀的影象
Mat dst;
capture >> frame; //讀取當前幀
cvtColor(frame,frame,COLOR_RGB2GRAY); //轉化為灰度圖
Canny(frame,frame, alpha_slider1, alpha_slider2);
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
/*cv::RETR_EXTERNAL:表示只提取最外面的輪廓;
cv::RETR_LIST:表示提取所有輪廓並將其放入列表;
cv::RETR_CCOMP:表示提取所有輪廓並將組織成一個兩層結構,其中頂層輪廓是外部輪廓,第二層輪廓是“洞”的輪廓;
cv::RETR_TREE:表示提取所有輪廓並組織成輪廓巢狀的完整層級結構。*/
cv::CHAIN_APPROX_TC89_L1
/*cv::CHAIN_APPROX_NONE:將輪廓中的所有點的編碼轉換成點;
cv::CHAIN_APPROX_SIMPLE:壓縮水平、垂直和對角直線段,僅保留它們的端點;
cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC89_KCOS:應用Teh-Chin鏈近似演算法中的一種風格*/
);
dst = frame.clone();
dst = cv::Scalar::all(0); //建立全黑影象
cv::drawContours(dst, contours, -1, cv::Scalar::all(255)); //繪製白色輪廓
imshow("A",frame); //原canny濾波影象
imshow("B", dst); //對frame進行輪廓查詢並重新繪製的影象
waitKey(30); //延時30ms
}
return 0;
}
輪廓的查詢——cv::findContours()
函式cv::findContour是從二值影象中來計算輪廓的,它可以使用cv::Canny()函式處理的影象,因為這樣的影象含有邊緣畫素;也可以使用cv::threshold()或者cv::adaptiveThreshold()處理後的影象,其邊緣隱含在正負區域的交界處。
- void cv::findContours(
- cv::InputOutputArray image, // 輸入的8位單通道“二值”影象
- cv::OutputArrayOfArrays contours, // 包含points的vectors的vector
- int mode, // 輪廓檢索模式
- int method, // 近似方法
- cv::Point offset = cv::Point() // (可選) 所有點的偏移
- );
輸入影象的所有非零畫素之間都是相等的
輪廓的繪製——cv::drawContours()。
(4).最後說說通過分析資訊定位
在上一步的程式中定義了vector< vector< Point> > contours;,其中儲存了影象的輪廓資訊,vector<Point>表示一系列的角點,函式findContours會將每個輪廓的點按順序壓入。使用arcLength(,true);可以得到一個輪廓周長。
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
vector<Point> point_1; //定義二維點向量
point_1.push_back(Point(0, 0));
point_1.push_back(Point(1, 0));
point_1.push_back(Point(1, 1));
point_1.push_back(Point(0, 1)); //壓入值
vector< vector < Point > > point; //定義二維向量,元素為點
point.push_back(point_1); //壓入二維點向量
//計算輪廓的矩
double dstLength = arcLength(point[0],true); //計算剛壓入輪廓的周長
cout << "【原始輪廓的長度為:】" << dstLength << endl;
getchar();
return 0;
}
↑這個程式說明了vector<Point>的儲存及使用。
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int alpha_slider1=30, alpha_slider2=100;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[總]");
createTrackbar("Cmin", "[總]", &alpha_slider1, 500, on_trackbar);
createTrackbar("Cmax", "[總]", &alpha_slider2, 500, on_trackbar);
VideoCapture capture(0); //【1】從攝像頭讀入視訊
while (1)
{
Mat frame; //定義一個Mat變數,用於儲存每一幀的影象
capture >> frame; //讀取當前幀
Mat dst = frame.clone();
cvtColor(frame, frame, COLOR_RGB2GRAY); //轉化為灰度圖
GaussianBlur(frame, frame, Size(9, 9),0); //高斯濾波
Canny(frame, frame, alpha_slider1, alpha_slider2); //邊緣檢測
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE
); //獲取輪廓
for (int i=0;i<contours.size();i++)
{
RotatedRect rect = minAreaRect(contours[i]); //返回最小矩形
Size s = rect.size;
Point2f P[4];
rect.points(P);
if ((contourArea(contours[i], true) / s.area())- 0.7853<0.1&& (contourArea(contours[i], true) / s.area()) - 0.7853>-0.1) //(圓)輪廓面積除以最小矩形面積約為
{
circle(dst, rect.center, 3, Scalar(255, 0, 0));
circle(dst, rect.center, 5, Scalar(255, 0, 0));
}
}
imshow("A", frame);
imshow("B",dst);
waitKey(30); //延時30ms
}
return 0;
}
↑這個程式包含影象灰度轉換、濾波、邊緣檢測、輪廓獲取、以及圓形識別。相比與霍夫圓變換,速度更快,不過效果沒有它好。