准确检测图像的轮廓 opencv_图像处理案例实战
1. 切邊
源圖像:
需求:掃描儀掃描到的法律文件,需要切邊,去掉邊緣空白,這樣看上去才真實,人工操作成本與時間花費高,希望程序自動實現(xiàn),高效、準確。 實現(xiàn)思路:邊緣檢測 + 輪廓發(fā)現(xiàn)或直線檢測最大外接矩形。
例子代碼:
#include效果圖
總結(jié):先利用 Canny 算子檢測圖像的輪廓,再利用 findContours 發(fā)現(xiàn)輪廓,因為這時候會得到很多輪廓,而我們只需要找到最大的輪廓,這時候可以根據(jù)源圖像的情況設(shè)置過濾的條件(如本例中寬高設(shè)為不小于0.75)。找到這個矩形后,再把它畫出來即可。
2. 直線檢測
源圖像:
需求:尋找英語試卷填空題的下劃線,這個對后期的切圖與自動識別都比較重要。 實現(xiàn)思路:通過圖像形態(tài)學(xué)操作來尋找直線,霍夫獲取位置信息與顯示
例子代碼:
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; Mat srcImg, roiImg; void detectLine(); void test() {srcImg = imread("test2.jpg",IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL); //CV_WINDOW_NORMAL 鼠標控制顯示窗口的大小imshow("Original image", srcImg);//因為是截圖,所以要去掉白邊,用ROI圖像的方法Rect roi = Rect(10, 10, srcImg.cols - 15, srcImg.rows - 15); //切掉白邊roiImg = srcImg(roi);namedWindow("Roi image", CV_WINDOW_NORMAL);imshow("Roi image", roiImg);detectLine(); }void detectLine() { Mat binaryImg, morhpImg;threshold(roiImg, binaryImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); //二值化namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形態(tài)學(xué)操作Mat kernel = getStructuringElement(MORPH_RECT, Size(60, 1), Point(-1, -1)); morphologyEx(binaryImg, morhpImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Morphology Result", CV_WINDOW_NORMAL);imshow("Morphology Result", morhpImg);//膨脹,使得直線更加明顯kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));dilate(morhpImg, morhpImg, kernel);namedWindow("Dilate Result", CV_WINDOW_NORMAL);imshow("Dilate Result", morhpImg);//霍夫變換標定直線vector<Vec4i> lines; //每條線有兩個點,把四個點的坐標存儲起來HoughLinesP(morhpImg, lines, 1, CV_PI / 180.0, 30, 20.0, 0); // 1 為極坐標方向上的步長Mat resultImg = roiImg.clone(); //輸出結(jié)果cvtColor(resultImg, resultImg, COLOR_GRAY2BGR);for (int i = 0; i < lines.size(); i++){Vec4i ln = lines[i]; //取出直線line(resultImg, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0); //Point(ln[0], ln[1]), Point(ln[2], ln[3])兩個坐標}namedWindow("Hough Lines-Final Result", CV_WINDOW_NORMAL);imshow("Hough Lines-Final Result", resultImg); } int main() {test();waitKey(0);return 0; }效果圖
總結(jié):因為源圖像是截圖,所以最外圍有白邊,通常這種情況下可以利用 ROI 圖像方法去掉不需要的部分,再通過二值化和形態(tài)學(xué)操作來尋找直線,最后利用霍夫概率變換獲取直線的準確位置。
3. 對象提取
源圖像:
需求:對圖像中對象(圓形)進行提取,獲取這樣的對象,去掉其它干擾和非目標對象。并獲取圓形的面積和周長。 實現(xiàn)思路:二值分割 + 形態(tài)學(xué) + 橫縱比計算。
步驟: - 找到對象的輪廓 - 通過面積、橫縱比過濾點其它不需要的對象
第一步:發(fā)現(xiàn)輪廓 例子代碼:
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; //第一步 Mat srcImg, binaryImg, dstImg; void test() {srcImg = imread("case3.png", IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL);imshow("Original image", srcImg);//二值化threshold(srcImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形態(tài)學(xué)操作,開操作,去掉小的對象,閉操作,連接里面的洞(開閉操作要先獲得結(jié)構(gòu)元素)Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); //Point(-1, -1)是中心點,這里是 2 x 2位置morphologyEx(binaryImg, dstImg, MORPH_CLOSE, kernel, Point(-1, -1));namedWindow("Close Result", CV_WINDOW_NORMAL);imshow("Close Result", dstImg);kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(dstImg, dstImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Open Result", CV_WINDOW_NORMAL);imshow("Open Result", dstImg);//輪廓發(fā)現(xiàn)vector<vector<Point>> contours; //存儲輪廓vector<Vec4i> hireachy;findContours(dstImg, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());Mat resultImg = Mat::zeros(srcImg.size(), CV_8SC3);for (int i = 0; i < contours.size(); i++){drawContours(resultImg, contours, i, Scalar(0, 0, 255), 2, 8, Mat(), 0, Point()); //畫出來}namedWindow("Final Result", CV_WINDOW_NORMAL);imshow("Final Result", resultImg); } int main() {test();waitKey(0);return 0; }效果圖
第二步:過濾并計算面積和周長 例子代碼:
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; //第二步 通過面積過濾,通過縱橫比的測量,圓形的縱橫比應(yīng)該在1:1左右,如果不是1:1,就把它過濾掉 Mat srcImg, binaryImg, dstImg; void test() {srcImg = imread("case3.png", IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL); //CV_WINDOW_NORMAL 使得鼠標可以控制顯示窗口的大小imshow("Original image", srcImg);//二值化threshold(srcImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形態(tài)學(xué)操作,開操作,去掉小的對象,閉操作,連接里面的洞(開閉操作要先獲得結(jié)構(gòu)元素)Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); //Point(-1, -1)是中心點,這里是 2 x 2位置morphologyEx(binaryImg, dstImg, MORPH_CLOSE, kernel, Point(-1, -1));namedWindow("Close Result", CV_WINDOW_NORMAL);imshow("Close Result", dstImg);kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(dstImg, dstImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Open Result", CV_WINDOW_NORMAL);imshow("Open Result", dstImg);//輪廓發(fā)現(xiàn)vector<vector<Point>> contours; //存儲輪廓vector<Vec4i> hireachy;findContours(dstImg, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());//通過面積過濾,通過縱橫比的測量,圓形的縱橫比應(yīng)該在1:1左右,如果不是1:1,就把它過濾掉Mat resultImg = Mat::zeros(srcImg.size(), CV_8SC3);Point cc;for (int i = 0; i < contours.size(); i++){//面積過濾double area = contourArea(contours[i]); //循環(huán)獲取指定的面積if (area < 100) //通過循環(huán)過濾掉小于100的面積continue;//橫縱比過濾Rect rect = boundingRect(contours[i]);float ratio = float(rect.width) / float(rect.height);if (ratio < 1.1 && ratio> 0.9) //把滿足條件的保留畫出來{drawContours(resultImg, contours, i, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point()); //畫出來 第五個參數(shù)改為 -1 ,使得整個圓形填充cout << "circle area: " << area << endl; //面積和周長打印出來(像素度量)cout << "circle length: " << arcLength(contours[i],true) << endl;//找中心點int x = rect.x + rect.width / 2;int y = rect.y + rect.height / 2;cc = Point(x, y);circle(resultImg, cc, 2, Scalar(0, 0, 255), 2, 8, 0); //畫出中心點}}namedWindow("Final Result", CV_WINDOW_NORMAL);imshow("Final Result", resultImg);//在原圖上定位顯示中心點Mat circleImg = srcImg.clone();cvtColor(circleImg, circleImg, COLOR_GRAY2BGR); circle(circleImg, cc, 2, Scalar(0, 0, 255), 2, 8, 0);namedWindow("Center Point", CV_WINDOW_NORMAL);imshow("Center Point", circleImg); }int main() {test();waitKey(0);return 0; }效果圖
總結(jié):首先把圖像二值化,通過閉操作把中間小的洞連接上,再通過開操作把周圍小的點去掉,再通過輪廓發(fā)現(xiàn)找到對象的輪廓。再通過面積、縱橫比過濾點其它不需要的對象,圓形的縱橫比應(yīng)該在1:1左右,如果不是1:1,就把它過濾掉。然后再計算面積和周長。
歡迎關(guān)注我的微信公眾號“OpenCV圖像處理算法”,主要是記錄自己學(xué)習(xí)圖像處理算法的歷程,包括特征提取、目標跟蹤、定位、機器學(xué)習(xí)和深度學(xué)習(xí),每一個例子都會提供源碼和例子所用的資料,歡迎同行的同學(xué)關(guān)注我和我一起虛度光陰吧!!!
總結(jié)
以上是生活随笔為你收集整理的准确检测图像的轮廓 opencv_图像处理案例实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 医疗险重疾险区别
- 下一篇: 我国个人的工资、薪金所得缴纳个人所得税采