OpenCV形狀檢測(cè)的示例詳解
1.基于OpenCV的形狀檢測(cè)Python版本
目錄結(jié)構(gòu)
1.1.定義我們的形狀檢測(cè)器類ShapeDetector
開始定義我們的 ShapeDetector 類。我們將跳過這里的 init 構(gòu)造函數(shù),因?yàn)椴恍枰跏蓟魏螙|西。
# 導(dǎo)入必要的包 import cv2 class ShapeDetector: def __init__(self): pass def detect(self, c): # 初始化形狀名稱并近似輪廓 shape = "unidentified" peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.04 * peri, True)
我們的檢測(cè)方法,它只需要一個(gè)參數(shù) c,即我們?cè)噲D識(shí)別的形狀的輪廓。為了進(jìn)行形狀檢測(cè),我們將使用輪廓逼近。
顧名思義,輪廓近似是一種算法,用于減少曲線中點(diǎn)的數(shù)量,并減少點(diǎn)集——因此稱為近似。
輪廓近似是基于曲線可以由一系列短線段近似的假設(shè)。這導(dǎo)致生成的近似曲線由原始曲線定義的點(diǎn)的子集組成。
輪廓近似實(shí)際上已經(jīng)在 OpenCV 中通過 cv2.approxPolyDP 方法實(shí)現(xiàn)。
為了進(jìn)行輪廓逼近,我們首先計(jì)算輪廓的周長(zhǎng),然后構(gòu)建實(shí)際的輪廓逼近。 cv2.approxPolyDP 的第二個(gè)參數(shù)的常用值通常在原始輪廓周長(zhǎng)的 1%-5% 范圍內(nèi)。
給定我們的近似輪廓,我們可以繼續(xù)執(zhí)行形狀檢測(cè):
# 如果形狀是一個(gè)三角形,它將有3個(gè)頂點(diǎn) if len(approx) == 3: shape = "triangle" # 如果形狀有4個(gè)頂點(diǎn),它要么是正方形,要么是矩形 elif len(approx) == 4: # 計(jì)算輪廓的包圍框,并使用包圍框計(jì)算高寬比 (x, y, w, h) = cv2.boundingRect(approx) ar = w / float(h) # 正方形的長(zhǎng)寬比大約等于1,否則,形狀就是矩形 shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle" # 如果形狀是一個(gè)五邊形,它將有5個(gè)頂點(diǎn) elif len(approx) == 5: shape = "pentagon" # 否則,我們假設(shè)形狀是一個(gè)圓 else: shape = "circle" # 返回形狀的名稱 return shape
重要的是要了解輪廓由頂點(diǎn)列表組成。我們可以檢查此列表中的數(shù)目以確定對(duì)象的形狀。例如,如果近似輪廓有三個(gè)頂點(diǎn)
那么它一定是一個(gè)三角形。如果一條輪廓有四個(gè)頂點(diǎn),那么它一定是正方形或矩形。為了確定這一點(diǎn),我們計(jì)算形狀的長(zhǎng)寬比
也就是輪廓邊界框的寬度除以高度。如果長(zhǎng)寬比是~1.0,那么我們正在檢查一個(gè)正方形(因?yàn)樗械倪叾加写蠹s相等的長(zhǎng)度)。否則,形狀就是矩形。如果一條等高線有五個(gè)頂點(diǎn),我們可以將其標(biāo)記為五邊形。否則,我們可以假設(shè)我們正在檢查的形狀是一個(gè)圓。
最后,將標(biāo)識(shí)好的形狀返回給調(diào)用方法。
我們已經(jīng)定義了一個(gè)utils模塊。在這個(gè)模塊中,我們有shapedetector .py,它將存儲(chǔ)ShapeDetector類的實(shí)現(xiàn)。
最后,我們有detect_shapes.py腳本,我們將使用它從磁盤加載圖像,分析它的形狀,然后通過ShapeDetector類執(zhí)行形狀檢測(cè)和識(shí)別。
1.2.基于OpenCV的形狀檢測(cè)器
現(xiàn)在我們的 ShapeDetector 類已經(jīng)定義好了,讓我們創(chuàng)建 detect_shapes.py 腳本:
# 導(dǎo)入必要的庫(kù) from utils.shapedetector import ShapeDetector import argparse import imutils import cv2 # 構(gòu)造參數(shù)解析并解析參數(shù) ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default="inpaint.jpg", help="path to the input image") args = vars(ap.parse_args())
首先導(dǎo)入我們已經(jīng)實(shí)現(xiàn)的ShapeDetector類,然后解析參數(shù),下一步,我們開始預(yù)處理我們的圖像
# 加載圖像并將其調(diào)整圖像大小,以便更好地近似形狀 image = cv2.imread(args["image"]) resized = imutils.resize(image, width=300) ratio = image.shape[0] / float(resized.shape[0]) # 將調(diào)整后的圖像轉(zhuǎn)換為灰度,稍微模糊它,并閾值化 gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 在閾值化圖像中找到輪廓并初始化形狀檢測(cè)器 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) sd = ShapeDetector()
首先,我們從磁盤加載圖像,并調(diào)整其大小。然后,我們跟蹤舊高度與調(diào)整后的新高度的比率——我們將在本教程的后面部分找到這樣做的確切原因。
將調(diào)整大小后的圖像轉(zhuǎn)換為灰度,平滑它以減少高頻噪聲,最后閾值化它以顯示圖像中的形狀。
閾值之后,我們的圖像應(yīng)該是這樣的:
請(qǐng)注意我們的圖像是如何被二值化的——形狀顯示為黑色背景下的白色前景。
最后,在二值圖像中找到輪廓,基于OpenCV版本的cv2.findContours獲取正確的元組值,并最終初始化我們的ShapeDetector。
最后一步是識(shí)別每個(gè)輪廓:
# 遍歷所有輪廓 for c in cnts: # 計(jì)算輪廓的中心,然后僅使用輪廓檢測(cè)形狀的名稱 M = cv2.moments(c) cX = int((M["m10"] / M["m00"]) * ratio) cY = int((M["m01"] / M["m00"]) * ratio) shape = sd.detect(c) # 將輪廓(x, y)坐標(biāo)乘以調(diào)整比例,然后在圖像上繪制輪廓和形狀的名稱 c = c.astype("float") c *= ratio c = c.astype("int") cv2.drawContours(image, [c], -1, (0, 255, 0), 2) cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) # 顯示輸出圖像 cv2.imshow("Image", image) cv2.waitKey(0)
我們開始在每個(gè)單獨(dú)的輪廓上循環(huán)。對(duì)于每一個(gè),我們計(jì)算輪廓的中心,然后執(zhí)行形狀檢測(cè)和識(shí)別。
由于我們正在處理從調(diào)整大小后的圖像中提取的輪廓(而不是原始圖像),我們需要將輪廓和中心(x, y)坐標(biāo)乘以調(diào)整比率。
這將為我們提供原始圖像的輪廓和質(zhì)心的正確(x, y)坐標(biāo)。
最后,我們?cè)趫D像上繪制輪廓和識(shí)別的形狀,然后顯示我們的結(jié)果。
2.基于OpenCV的形狀檢測(cè)C++版本
在本教程中,讓我們看看如何使用 OpenCV 的輪廓來識(shí)別對(duì)象的形狀和位置。
使用OpenCV的輪廓,你可以得到每個(gè)白斑的頂點(diǎn)的點(diǎn)序列(白斑被認(rèn)為是多邊形)。例如,對(duì)于三角形你會(huì)得到3個(gè)點(diǎn)(頂點(diǎn)),
對(duì)于四邊形你會(huì)得到4個(gè)點(diǎn)。你可以通過多邊形的頂點(diǎn)數(shù)來識(shí)別任何多邊形。
你甚至可以通過計(jì)算和比較頂點(diǎn)之間的距離來識(shí)別多邊形的特征,如凸性、凹性、等邊等。
我們看看如何使用 OpenCV 來完成。您所需要的只是一個(gè)二進(jìn)制圖像,其中您的對(duì)象應(yīng)該是白色的,背景應(yīng)該是黑色的。
現(xiàn)在我將使用OpenCV C++應(yīng)用程序來識(shí)別上圖中的三角形、四邊形和七邊形。我將沿著每個(gè)確定的多邊形的周長(zhǎng)畫一條線,
三角形顏色為藍(lán)色,四邊形顏色為綠色,七邊形顏色為紅色。
2.1代碼實(shí)現(xiàn)
#include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { Mat img = imread("FindingContours.png"); //show the original image namedWindow("Raw"); imshow("Raw", img); //converting the original image into grayscale Mat imgGrayScale = Mat(img.size(), CV_8UC1); cvtColor(img, imgGrayScale, COLOR_BGR2GRAY); //thresholding the grayscale image to get better results threshold(imgGrayScale, imgGrayScale, 128, 255, THRESH_BINARY); //finding all contours in the image vector<vector<Point> > contours; vector<Vec4i> hierarchy; findContours(imgGrayScale, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); //iterating through each contour for (size_t i = 0; i < contours.size(); i++) { vector<Point> approx; //obtain a sequence of points of contour, pointed by the variable 'contour' approxPolyDP(contours[i], approx, arcLength(contours[i], true) * 0.02, true); //if there are 3 vertices in the contour(It should be a triangle) if (approx.size() == 3) { //drawing lines around the triangle for (int i = 0; i < 3; i++) { line(img, approx[i], approx[(i+1)%3], Scalar(255, 0, 0), 4); } } //if there are 4 vertices in the contour(It should be a quadrilateral) else if (approx.size() == 4) { //drawing lines around the quadrilateral for (int i = 0; i < 4; i++) { line(img, approx[i], approx[(i + 1) % 4], Scalar(0, 255, 0), 4); } } //if there are 7 vertices in the contour(It should be a heptagon) else if (approx.size() == 7) { //drawing lines around the heptagon for (int i = 0; i < 7; i++) { line(img, approx[i], approx[(i + 1) % 7], Scalar(0, 0, 255), 4); } } } //show the image in which identified shapes are marked namedWindow("Tracked"); imshow("Tracked", img); waitKey(0); //wait for a key press //cleaning up destroyAllWindows(); return 0; }
如您所見,三角形用藍(lán)色標(biāo)記, 四邊形用綠色標(biāo)記,七邊形用紅色標(biāo)記。所以,現(xiàn)在很明顯,這種方法能夠識(shí)別形狀。 首先將原始圖像轉(zhuǎn)換為灰度。
這是因?yàn)檫@種方法只適用于單通道的灰度圖像。為了獲得更好的結(jié)果,我使用“threshold”函數(shù)對(duì)灰度圖像進(jìn)行閾值處理。
您可以使用自己的方式對(duì)圖像進(jìn)行閾值處理。然后我找到閾值圖像中的所有輪廓,并識(shí)別和跟蹤所有三角形、四邊形和七邊形。
2.2主要函數(shù)解析
讓我們討論一下這個(gè)應(yīng)用程序中的OpenCV 函數(shù)。
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type);
src:源圖像(8位單通道)
dst:與src具有相同大小相同類型的目標(biāo)圖像
thresh:閾值
maxval:滿足條件的像素替換為這個(gè)值
type:閾值化方法,
THRESH_BINARY
- dst(x,y)=max, if src(x,y) > ThreshVal
- dst(x,y)=0, if src(x,y) < ThreshVal
THRESH_BINARY_INV
- dst(x,y)=0,如果 src(x,y) > ThreshVal
- dst(x,y)=max,如果 src(x,y) < ThreshVal
THRESH_TOZERO
- dst(x,y)=src(x,y), 如果 src(x,y) > ThreshVal
- dst(x,y)=0, 如果 src(x,y) < ThreshVal
THRESH_TOZERO_INV
- dst(x,y)=0,如果 src(x,y) > ThreshVal
- dst(x,y)=src(x,y),如果 src(x,y) < ThreshVal
THRESH_TRUNC
- dst(x,y)=threshVal,如果 src(x,y) > ThreshVal
- dst(x,y)=src(x,y), if src(x,y) < ThreshVal
在上面的應(yīng)用程序中,我使用了“ THRESH_BINARY”,因?yàn)槲蚁朐趯?duì)象所在的位置分配 255(白色其他是0(黑色)。
findContours( InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());
image: 8位單通道圖像。非零像素被視為1。零像素保持為0,因此圖像被視為二值。你可以使用#compare #inRange #threshold,#adaptiveThreshold,
#Canny,以及其他參數(shù)來創(chuàng)建灰度或彩色圖像的二值圖像。
contours: 發(fā)現(xiàn)的所有輪廓
hierarchy:輪廓之間的層次結(jié)構(gòu)
int mode - 從圖像中檢索輪廓的模式,您必須選擇以下之一
- RETR_LIST - 檢索所有輪廓并將它們放入列表中
- RETR_EXTERNAL - 僅檢索外輪廓
- RETR_CCOMP - 檢索所有輪廓并將它們組織成兩級(jí)層次結(jié)構(gòu):
- RETR_TREE - 檢索所有輪廓并重建嵌套輪廓的完整層次結(jié)構(gòu)
int method - 近似方法,您必須選擇以下之一
- CHAIN_APPROX_NONE - 將鏈碼中的所有點(diǎn)轉(zhuǎn)換為點(diǎn)
- CHAIN_APPROX_SIMPLE - 壓縮水平、垂直和對(duì)角線段,只留下它們的端點(diǎn)
- CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS - 應(yīng)用 Teh-Chin 鏈近似算法的一種風(fēng)格。
Point offset:每個(gè)輪廓點(diǎn)應(yīng)移動(dòng)的偏移量。當(dāng)我們?cè)趫D像中設(shè)置 ROI(感興趣區(qū)域)時(shí),這很有用。通常我們將偏移量設(shè)置為 ‘Point(0,0)’
void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed );
- curve: 存儲(chǔ)2D點(diǎn)的std::vector或Mat
- approxCurve: 多邊形近似的結(jié)果。類型應(yīng)該與輸入類型相匹配。
- epsilon: 指定近似精度的參數(shù)。這是原始輪廓與其近似值之間的最大距離。
- closed: 如果為真,逼近曲線是閉合的(它的第一個(gè)和最后一個(gè)頂點(diǎn)是連接的)。否則不閉合。
2.3結(jié)果展示
以上就是OpenCV形狀檢測(cè)的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于OpenCV形狀檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python?selenium實(shí)現(xiàn)登錄豆瓣示例詳解
大家好,本篇文章主要講的是python?selenium登錄豆瓣示例詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01使用Python讀取Excel數(shù)據(jù)并寫入到CSV、XML和文本
Excel工作簿是常用的表格格式,許多數(shù)據(jù)呈現(xiàn)、數(shù)據(jù)分析和數(shù)據(jù)匯報(bào)都是以Excel工作表的形式進(jìn)行,本文將演示如何運(yùn)用Python編程語言,將Excel工作表中的豐富數(shù)據(jù)導(dǎo)入到CSV、XML或文本中,需要的朋友可以參考下2024-03-03Jupyter Notebook折疊輸出的內(nèi)容實(shí)例
這篇文章主要介紹了Jupyter Notebook折疊輸出的內(nèi)容實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04Pycharm新建項(xiàng)目時(shí)報(bào)錯(cuò)解決辦法
pycharm可以很方便的管理Python的解釋器(如果安裝了多個(gè)的話),以及第三方模塊,包,下面這篇文章主要給大家介紹了關(guān)于Pycharm新建項(xiàng)目時(shí)報(bào)錯(cuò)解決的相關(guān)資料,需要的朋友可以參考下2023-06-06關(guān)于python的bottle框架跨域請(qǐng)求報(bào)錯(cuò)問題的處理方法
這篇文章主要介紹了關(guān)于python的bottle框架跨域請(qǐng)求報(bào)錯(cuò)問題的處理方法,需要的朋友可以參考下2017-03-03使用python計(jì)算方差方式——pandas.series.std()
這篇文章主要介紹了使用python計(jì)算方差方式——pandas.series.std(),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05Python的Flask開發(fā)框架簡(jiǎn)單上手筆記
這篇文章主要介紹了Python的Flask開發(fā)框架的入門知識(shí)整理,Flask是一款極輕的Python web開發(fā)框架,需要的朋友可以參考下2015-11-11