一.圖像識別API
1.圖像識別作用
????????它常用于視覺任務、目標檢測、圖像分割等等。在 OPENCV 中通常使用 Canny
函數、findContours 函數、drawContours 函數結合在一起去做輪廓的形檢測。
2.常用的API?
- findContours 函數:用于尋找圖片的輪廓,并把所有的數據存儲在向量里面? ? ? ? ? ? ? ? ? ??
????????CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode,int method, Point offset = Point());
第一個參數:image 輸入的二值圖像,這個圖像通常是用在邊緣檢測、閾值處理等等
第二個參數:contours 輸出的輪廓集合,每一個輪廓都是由點組成,通常用vector<vector<Point>>來表示
第三個參數:hierarchy 輸出的輪廓層次結構,這通常表示輪廓之間的父子關系,這個是可選參數,通常用 vector<Vec4i> hierarchy來表示。比方說,第 i 個輪廓,hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]、hierarchy[i][3], 依次為第 i 個輪廓[Next、Pervious、First_Child,Parent], 這表示的是相同等級下的下一輪廓、前一輪廓,第一個子輪廓和父輪廓的索引號。若輪廓 i 沒有下一個,前一個或者父級輪廓,則層次相應的元素是負數。如下圖:
第四個參數:mode 輪廓檢索模式,通常有以下選項,分別是:RETR_EXTERNAL(只檢測最外層輪廓)、RETR_LIST(檢測所有輪廓,包括內圍、外圍輪廓,但都是同等級的)、RETR_CCOMP(檢測所有輪廓,但是所有的輪廓只建立兩個等級關系,一個外層,一個內層(不管內層有多少個,都屬于內層))、RETR_TREE(檢測所有輪廓并建立輪廓樹,這個模式下外圍輪廓包含內層輪廓,內層還可以繼續嵌套)。
第 五 個 參 數 : method 輪 廓 近 似 方 法 , 通 常 有 以 下 的 幾 種 方 法 , 分 別 是 CHAIN_APPROX_NONE( 存 儲 所 有 頂 點 ) 、CHAIN_APPROX_SIMPLE(僅存儲輪廓的拐點信息,并把所有輪廓拐點處的點保存到向量里面)、CHAIN_APPROX_TC89_L1(使用TEH_CHAIN 近似算法)。
第六個參數:offset 輪廓點偏移量,默認(0,0)
- drawContours 函數:用于繪制圖像的輪廓,配合findContours 函數使用,用findContours 函數獲取輪廓,用drawContours 函數畫輪廓。
?????????CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,int contourIdx, const Scalar& color,int thickness = 1, int lineType = LINE_8,InputArray hierarchy = noArray(),int maxLevel = INT_MAX, Point offset = Point() );
第一個參數:image 輸出圖像,即繪制輪廓后的圖像
第二個參數:contours 輪廓的集合,它是由一系列的點組成
第三個參數:contourIdx、輪廓索引數組,指定要繪制哪些輪廓
第四個參數:contourColor,輪廓顏色,使用 Scalar 類型表示
第五個參數:thickness,輪廓線寬,默認 1
第六個參數:lineType ,輪廓線類型,默認為 LINE_8
第七個參數:hierarchy ,輪廓層次結構,用于繪制輪廓的父子關系。默認為 noArray()
第八個參數:maxLevel ,表示繪制輪廓的最大層級數量。若 maxLevel 為 0,則只繪制指定的輪廓;若 maxLevel 為 1,則繪制輪廓極其所有嵌套輪廓;若 maxLevel 為 2,則繪制輪廓、所有嵌套輪廓、所有嵌套到嵌套的輪廓。
第九個參數:輪廓點的偏移量,默認為(0,0)?
- Canny 函數:用于邊緣檢測計算。效果如下:
? ? ? ? ?工作原理:就是一系類數學處理方法。高斯濾波(將圖像轉換為灰度圖像,高斯濾波作用是平滑圖像,讓 Canny 檢測的時候準確率更高)、梯度強度和方向的計算(計算圖像中每個像素的強度和方向、強度表示像素點的邊緣強度、梯度表示的是邊緣方向這里的梯度需要用到 sobel 因子)、非極大抑制(經過 NMS 操作后,會除去一些不是邊緣的像素點)、雙閾值處理(給出一個閾值,若超過這個閾值的邊緣則會被保留)、邊緣鏈接(經過雙閾值處理過后,強邊緣則會留下來,弱邊緣則會被抑制,并會把所有的強邊緣全部連接起來)。
?????????工作原理白話文:把3通道圖片(也就是彩色圖片)轉化成雙通道圖片(也就是灰色圖像),然后用高斯濾波去除細小雜物讓它更好識別輪廓;梯度強度和方向的計算,就是不同程度掃描輪廓,掃描很多輪廓;非極大抑制,去除邊邊角角不是邊緣的像素點;雙閾值處理,就是在之前掃描不同程度輪廓上設置兩個閾值(Max,Min),大于Max值被認為是輪廓保留下來,小于Min被認為不是輪廓去除掉,在兩值中間的是邊緣保留,不是去掉;邊緣鏈接,就是一個完整輪廓。
????????CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,double threshold1, double threshold2,int apertureSize = 3, bool L2gradient = false );?
第一個參數:image 輸入的圖像,這個圖像一定要單通道灰度圖
第二個參數:edges 輸出的邊緣圖像,這個圖像也必須是單通道黑白圖
第三個參數:threshold1 第一個滯后性閾值,低閾值,小于低閾值則認為是弱邊緣,就是需要拋棄的邊緣。
第四個參數:threshold2 第二個滯后性閾值,高閾值,大于高閾值被認為強邊緣,需要保留的邊緣
第五個參數:apertureSize 指的是 Sobel 算子大小,這個值默認為 3,代表的是 3*3 的矩陣大小。
第六個參數:L2gradient 是計算圖像梯度幅度值的情況,這個值默認為 False;若選擇 True,則使用更精確的 L2 范數進行計算
二. 代碼實戰掃描物體輪廓
1.掃描輪廓步驟
????????分別是 imread 讀取圖片(這個圖片默認是 3 通道)、利用 cvtColor 把 8VU3 的三通道圖片轉換成灰度圖(8VU1)、調用 Canny 對灰度圖像進行邊緣檢測、調用 findContours 去查找輪廓、循環輪廓數量然后調用 drawContours 進行畫框操作。
2.代碼
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace cv;
using namespace std;int main()
{//讀取圖片Mat img = imread("zjl.jpg");//彩色圖片轉化灰度圖片Mat imgGray;cvtColor(img, imgGray, COLOR_RGB2GRAY);//灰度圖片邊緣檢測輪廓Mat imgCanny;Canny(imgGray, imgCanny, 25, 75); //Canny對圖像進行邊緣檢測,弱閾值25,強閾值75//查詢輪廓vector<vector<Point>> contours; //定義輪廓vector<Vec4i> hierarchy; //定義層次結構findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE); //查詢輪廓,RETR_EXTERNAL表示只檢測外部輪廓,CHAIN_APPROX_NONE表示存儲所有的輪廓點//繪制輪廓Mat drawing = Mat::zeros(imgCanny.size(), CV_8UC3);//創建一個空白圖像,用于繪制輪廓for (int i = 0; i < contours.size(); i++) //遍歷所有輪廓{Scalar color = Scalar(255, 255, 0); //隨機生成顏色drawContours(drawing, contours, i, color, 1, 8, hierarchy); //對圖像輪廓進行畫框}//顯示圖像imwrite("zjl1.jpg", drawing);return 0;
}