一、前言
在OpenCV中,邊緣檢測和輪廓查找是兩個不同的圖像處理任務,它們有不同的目標和應用。
1.1 邊緣檢測和輪廓查找的區(qū)別是什么
1.1.1 邊緣檢測:
- 定義: 邊緣檢測是指尋找圖像中灰度級別變化明顯的地方,即圖像中物體之間的界限。這些變化通常表示圖像中的邊緣或輪廓。
- 方法: 常用的邊緣檢測算法包括Sobel、Canny、Laplacian等。這些算法通過在圖像中尋找灰度級別變化最大的地方來標記邊緣。
import cv2
# 讀取圖像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 使用Canny邊緣檢測
edges = cv2.Canny(image, 100, 200)
# 顯示邊緣檢測結果
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.1.2 輪廓查找:
- 定義: 輪廓是圖像中連續(xù)的邊界線,表示相同顏色或灰度的區(qū)域的邊界。輪廓查找的目標是找到圖像中對象的外形。
-
方法: OpenCV提供了
findContours
函數來查找圖像中的輪廓。這個函數返回輪廓的坐標點,然后可以通過繪制這些坐標點來可視化輪廓。
import cv2
# 讀取圖像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 使用Canny邊緣檢測
edges = cv2.Canny(image, 100, 200)
# 查找輪廓
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原圖上繪制輪廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)
# 顯示結果
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
總結:
邊緣檢測強調的是圖像中灰度級別的變化,而輪廓查找強調的是圖像中相鄰區(qū)域的邊界。在實際應用中,這兩種技術通??梢越Y合使用,先進行邊緣檢測,然后通過輪廓查找來識別和分析圖像中的對象。
1.2 邊緣檢測和輪廓查找在圖像處理中的關系和流程
邊緣檢測用于發(fā)現圖像中灰度變化明顯的區(qū)域,但這些邊緣通常是不連續(xù)的。為了構成完整的對象輪廓,需要將這些邊緣連接在一起。
為了進行輪廓查找,首先需要將圖像轉換為二值圖像,其中對象是白色,背景是黑色。這可以通過預先進行閾值分割或者邊緣檢測處理來實現。
輪廓查找通常會修改原始圖像,因此為了保留原始圖像的完整性,我們通常會在原始圖像的一份拷貝上進行操作。
在OpenCV中,默認情況下假設對象是白色,背景是黑色。因此,在進行輪廓查找時,確保對象是白色,背景是黑色,以確保正確識別圖像中的對象輪廓。
二、查找并繪制輪廓
cv2.findContours()
cv2.drawContours()
在OpenCV中,cv2.findContours() 是用于查找圖像輪廓的函數,
而 cv2.drawContours() 則是用于將查找到的輪廓繪制到圖像上的函數。
2.1 cv2.findContours():
- 這個函數用于在二值圖像中查找對象的輪廓。
- 返回輪廓的坐標點列表和層次結構(hierarchy)信息。
import cv2
# 讀取圖像并轉換為灰度圖
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 進行閾值分割,得到二值圖像
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找輪廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# contours 包含了所有輪廓的坐標點
# hierarchy 包含了輪廓的層次結構信息
2.1.1 詳細介紹:
cv2.findContours()
是 OpenCV 中用于查找圖像輪廓的函數。它的基本用法如下:
image, contours, hierarchy = cv2.findContours(image, mode, method)
其中各參數的含義如下:
-
image: 輸入的二值圖像(通常是經過閾值處理的圖像)。要確保輸入圖像是單通道的(灰度圖像)且為二值圖像,可以使用
cv2.cvtColor()
和cv2.threshold()
進行轉換和閾值處理。 -
mode: 輪廓檢索模式。指定輪廓的檢索模式,有以下幾個可選值:
-
cv2.RETR_EXTERNAL
:只檢索最外層的輪廓。 -
cv2.RETR_LIST
:檢索所有的輪廓,并將其保存到列表中。 -
cv2.RETR_CCOMP
:檢索所有輪廓,并將其組織為兩層的層次結構(目前不常用)。
【建立兩個等級的輪廓,上面的一層為外邊界,里面的一層為內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層?!?/p>
-
cv2.RETR_TREE
:檢索所有輪廓,并重構輪廓之間的完整層次結構。
建立一個等級樹結構的輪廓。
-
-
method: 輪廓逼近方法。指定輪廓的逼近方法,有以下幾個可選值
-
cv2.CHAIN_APPROX_NONE
:保存所有的輪廓點。
存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,max (abs (x1-x2) , abs (y2-y1) ) ==1
-
cv2.CHAIN_APPROX_SIMPLE
:壓縮水平、垂直和對角方向,只保留端點。
只保留該方向的終點坐標,例如一個矩形輪廓只需4個點來保存輪廓信息[節(jié)約空間]
cv2.CHAIN_APPROX_NONE 存儲的輪廓,保存
了輪廓中的每一個點;右圖是使用參數值 cv2.CHAIN_APPROX_SIMPLE 存儲的輪廓,僅僅保
存了邊界上的四個點。-
cv2.CHAIN_APPROX_TC89_L1
:使用 Teh-Chin 鏈逼近算法。 -
cv2.CHAIN_APPROX_TC89_KCOS
:使用 Teh-Chin 鏈逼近算法。
-
函數返回三個值:
- image: 輸入圖像,通常不會改變。
- contours: 包含輪廓坐標的列表。每個輪廓由一系列坐標點表示。
- hierarchy: 輪廓的層次結構信息,用于表示輪廓之間的嵌套關系。
示例代碼:
import cv2
# 讀取圖像并轉換為灰度圖
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 進行閾值分割,得到二值圖像
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找輪廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# contours 包含了所有輪廓的坐標點
# hierarchy 包含了輪廓的層次結構信息
這個函數在圖像處理中常用于對象檢測、形狀分析等任務。
2.1.2 注意事項:
遇到的錯誤 “not enough values to unpack (expected 3, got 2)” 可能是因為在OpenCV 4.x中,cv.findContours 函數只返回兩個值:輪廓(contours)和層次結構(hierarchy)。在你的代碼中,你試圖解包三個值 (img, contours, hierachy),這導致了錯誤
在OpenCV的3.x版本和4.x版本之間,主要的變化之一是cv2.findContours()
函數的返回值。在3.x版本中,該函數返回三個值,而在4.x版本中,只返回兩個值。下面是一個簡要對比:
OpenCV 3.x:
# OpenCV 3.x
img, contours, hierarchy = cv2.findContours(image, mode, method)
-
img
: 原始圖像 -
contours
: 輪廓坐標的列表 -
hierarchy
: 輪廓的層次結構信息
OpenCV 4.x:
# OpenCV 4.x
contours, hierarchy = cv2.findContours(image, mode, method)
-
contours
: 輪廓坐標的列表 -
hierarchy
: 輪廓的層次結構信息
如上所示,主要的變化是在4.x版本中去除了原始圖像的返回,使得函數的返回結果更加簡潔。如果你從3.x版本遷移到4.x版本,需要注意修改相關代碼以適應新的函數返回形式。
2.2 cv2.drawContours():
- 這個函數用于將查找到的輪廓繪制到圖像上。
- 可以選擇繪制所有輪廓或者僅繪制特定的輪廓。
# 繪制所有輪廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)
# 顯示繪制輪廓后的圖像
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
這兩個函數通常一起使用,cv2.findContours()
用于獲取輪廓信息,然后 cv2.drawContours()
用于將輪廓繪制到圖像上,以便進一步分析或可視化。
2.2.1 詳細介紹:
cv2.drawContours()
是 OpenCV 中用于在圖像上繪制輪廓的函數。它的基本用法如下:
result = cv2.drawContours(image, contours, contourIdx, color, thickness)
其中各參數的含義如下:
-
image: 要繪制輪廓的圖像。
-
contours: 包含輪廓坐標的列表,通常是由
cv2.findContours()
函數返回的輪廓。 -
contourIdx: 要繪制的輪廓在
contours
列表中的索引。如果為負數(默認值為 -1),則繪制所有的輪廓。 -
color: 繪制輪廓的顏色,通常是一個包含三個整數值的元組,表示BGR顏色。
-
thickness: 繪制輪廓的線條厚度。如果為負數,表示填充輪廓。
函數返回一個新的圖像,包含了繪制了輪廓的結果。
示例代碼:
import cv2
import numpy as np
# 創(chuàng)建一張空白圖像
image = np.zeros((300, 300, 3), dtype=np.uint8)
# 創(chuàng)建一個包含輪廓坐標的列表
contours = np.array([[[50, 50]], [[150, 50]], [[100, 150]]], dtype=np.int32)
# 繪制輪廓
result = cv2.drawContours(image, [contours], -1, (0, 255, 0), 2)
# 顯示繪制輪廓后的圖像
cv2.imshow('Contours', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
這個函數在圖像處理中用于可視化檢測到的輪廓,使得我們能夠直觀地觀察到圖像中對象的形狀和邊界。
2.3 實際運用
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取圖像
image = cv.imread('img8/ss.jpg')
# 轉換為灰度圖
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 閾值處理得到二值圖像
ret, binary = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)
# 尋找輪廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
# 在原始圖像的副本上繪制輪廓
image_with_contours = image.copy()
cv.drawContours(image_with_contours, contours, -1, (122, 55, 215), 10)
# 使用 matplotlib 顯示原圖和帶有輪廓的圖像
plt.figure(figsize=(12, 6))
# 顯示原圖
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB))
plt.axis('off')
# 顯示帶有輪廓的圖像
plt.subplot(1, 2, 2)
plt.title('Image with Contours')
plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB))
plt.axis('off')
plt.show()
2.4 標記記數,再說先前函數參數
import cv2 as cv
import matplotlib.pyplot as plt
image = cv.imread('img8/ss.jpg')
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)
# 尋找輪廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 在原始圖像的副本上繪制輪廓并標注序號
image_with_contours = image.copy()
for i, contour in enumerate(contours):
cv.drawContours(image_with_contours, [contour], -1, (122, 55, 215), 2)
# 標注輪廓序號
cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 使用 matplotlib 顯示結果
plt.subplot(121), plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB)), plt.title('Original Image')
plt.subplot(122), plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB)), plt.title('Image with Contours')
plt.show()
2.4.1 分析代碼的走向:
-
cv.imread('img8/ss.jpg')
: 從文件中讀取一張圖像(文件路徑為'img8/ss.jpg'
)。 -
cv.cvtColor(image, cv.COLOR_BGR2GRAY)
: 將彩色圖像轉換為灰度圖像。 -
cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY)
: 對灰度圖像進行閾值處理,得到二值圖像。 -
cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
: 尋找二值圖像中的輪廓,并返回輪廓的坐標和層次結構。 -
image_with_contours = image.copy()
: 復制原始圖像,用于在其上繪制輪廓。 -
for i, contour in enumerate(contours):
: 對于每個輪廓,使用enumerate
獲取輪廓的索引和輪廓本身。a.
cv.drawContours(image_with_contours, [contour], -1, (122, 55, 215), 2)
: 在圖像副本上繪制輪廓,顏色為(122, 55, 215)
,線條粗細為2。b.
cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
: 標注輪廓序號,以文本形式顯示在圖像上,顏色為(0, 255, 0)
(綠色),字體大小為0.5,線條粗細為2。 -
plt.subplot(121), plt.imshow(cv.cvtColor(image, cv.COLOR_BGR2RGB)), plt.title('Original Image')
: 創(chuàng)建Matplotlib子圖,顯示原始圖像。 -
plt.subplot(122), plt.imshow(cv.cvtColor(image_with_contours, cv.COLOR_BGR2RGB)), plt.title('Image with Contours')
: 創(chuàng)建Matplotlib子圖,顯示帶有輪廓的圖像。 -
plt.show()
: 顯示Matplotlib繪制的原始圖像和帶有輪廓的圖像。
總體而言,這段代碼的目的是可視化圖像處理中的輪廓查找過程,通過標注輪廓序號,使用戶能夠更清晰地理解圖像中檢測到的對象的形狀和位置。
2.4.2 在給輪廓標注序號的過程中,使用了OpenCV的 cv.putText()
函數。這個函數用于在圖像上繪制文本,具體的用法如下:
cv.putText(img, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)
各參數含義如下:
-
img
: 需要繪制文本的圖像。 -
text
: 要繪制的文本內容。 -
org
: 文本的起始坐標,即文本左下角的坐標。 -
fontFace
: 字體類型,例如cv.FONT_HERSHEY_SIMPLEX
。 -
fontScale
: 字體縮放因子。 -
color
: 文本的顏色。 -
thickness
: 文本線條的粗細。 -
lineType
: 線條類型。 -
bottomLeftOrigin
: 如果為真,文本起始坐標將被認為是左下角;如果為假(默認),則為左上角。
在給輪廓標注序號的代碼中,這個函數的具體應用如下:
# 標注輪廓序號
cv.putText(image_with_contours, str(i+1), tuple(contour[0][0]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
這一行代碼的作用是將輪廓的序號以文本的形式標注在原始圖像的副本上。str(i+1)
將輪廓的索引加1轉換為字符串,tuple(contour[0][0])
是輪廓的第一個點的坐標,cv.FONT_HERSHEY_SIMPLEX
是字體類型,0.5
是字體縮放因子,(255, 0, 0)
是文本顏色(藍色),2
是文本線條的粗細。這樣,每個輪廓的序號就被標注在了圖像上。
(1)findContours函數的contours參數
這個呢,是標記的時候字段+1了,所以和下標錯位1的
print (type(contours))
print (len(contours[0]))
print (len(contours[1]))
print (len(contours[2]))
print (len(contours))
print (contours[0])
print(contours[1].shape)
print(contours[2].shape)
(2)findContours函數的hierarchy參數
[Next,Previous,First_Child,Parent]
? Next:后一個輪廓的索引編號。
? Previous:前一個輪廓的索引編號。
? First_Child:第 1 個子輪廓的索引編號。
? Parent:父輪廓的索引編號。
如果上述各個參數所對應的關系為空時,也就是沒有對應的關系時,則將該參數所對應的
值設為“-1”。
2.4.3 分開顯示
import cv2
import numpy as np
import matplotlib.pyplot as plt
o = cv2.imread('img8/ss.jpg')
plt.imshow(cv2.cvtColor(o, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.show()
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
n = len(contours)
contoursImg = []
for i in range(n):
temp = np.zeros(o.shape, np.uint8)
contoursImg.append(temp)
contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, (255, 255, 255), 5)
# Display each contour using plt
plt.imshow(cv2.cvtColor(contoursImg[i], cv2.COLOR_BGR2RGB))
plt.title(f'Contour {i + 1}')
plt.show()
逐步解釋:
以下是對上述代碼每一步的分析:
-
讀取原始圖像:
o = cv2.imread('contours.bmp') plt.imshow(cv2.cvtColor(o, cv2.COLOR_BGR2RGB)) plt.title('Original Image') plt.show()
- 使用 OpenCV 讀取名為 ‘contours.bmp’ 的圖像。
- 將圖像轉換為 RGB 格式,并使用 Matplotlib 顯示原始圖像。
-
灰度轉換和二值化:
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
- 將原始圖像轉換為灰度圖。
- 對灰度圖進行二值化處理。
-
查找輪廓:
image, contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- 使用
cv2.findContours()
函數查找二值圖像中的輪廓。 -
cv2.RETR_EXTERNAL
參數表示只檢測外部輪廓。 -
cv2.CHAIN_APPROX_SIMPLE
參數表示使用簡化的輪廓表示。
- 使用
-
繪制并顯示每個輪廓:文章來源:http://www.zghlxwxcb.cn/news/detail-771790.html
n = len(contours) contoursImg = [] for i in range(n): temp = np.zeros(o.shape, np.uint8) contoursImg.append(temp) contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, (255, 255, 255), 5) # Display each contour using plt plt.imshow(cv2.cvtColor(contoursImg[i], cv2.COLOR_BGR2RGB)) plt.title(f'Contour {i + 1}') plt.show()
- 遍歷所有檢測到的輪廓,每個輪廓都被繪制在
contoursImg
的相應元素中。 - 使用 Matplotlib 顯示每個繪制了輪廓的圖像,標題顯示輪廓的序號。
- 遍歷所有檢測到的輪廓,每個輪廓都被繪制在
總體而言,這段代碼的目的是在原始圖像中找到輪廓,然后將每個輪廓在圖像上繪制出來并使用 Matplotlib 逐個顯示,以便用戶更好地理解輪廓檢測的結果。文章來源地址http://www.zghlxwxcb.cn/news/detail-771790.html
到了這里,關于我在Vscode學OpenCV 圖像處理四(輪廓查找 cv2.findContours() cv2.drawContours())-- 待補充的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!