您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“OpenCV執(zhí)行連通分量標記的方法是什么”,內容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“OpenCV執(zhí)行連通分量標記的方法是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
在本教程的第一部分,我們將回顧 OpenCV 提供的用于執(zhí)行連通分量標記和分析的四個函數(shù)。這些函數(shù)中最受歡迎的是cv2.connectedComponentsWithStats。
首先,我們將配置我們的開發(fā)環(huán)境并查看我們的項目目錄結構。
接下來,我們將實現(xiàn)兩種形式的連通分量分析:
一種方法將演示如何使用 OpenCV 的連通分量標記和分析函數(shù),計算每個連通分量的統(tǒng)計數(shù)據(jù),然后單獨提取/可視化每個連通分量。
第二種方法顯示了連接分量分析的實際示例。我們對車牌進行閾值化,然后使用連通分量分析僅提取車牌字符。
OpenCV 提供了四種連通分量分析函數(shù):
cv2.connectedComponents
cv2.connectedComponentsWithStats
cv2.connectedComponentsWithAlgorithm
cv2.connectedComponentsWithStatsWithAlgorithm
最流行的方法是 cv2.connectedComponentsWithStats,它返回以下信息:
連通分量的邊界框
連通分量的面積(以像素為單位)
連通分量的質心/中心 (x, y) 坐標
第一種方法,cv2.connectedComponents,和第二種方法一樣,只是不返回上面的統(tǒng)計信息。在絕大多數(shù)情況下,您將需要統(tǒng)計信息,因此簡單地使用 cv2.connectedComponentsWithStats 即可。
第三種方法 cv2.connectedComponentsWithAlgorithm 實現(xiàn)了更快、更有效的連通分量分析算法。
如果您使用并行處理支持編譯 OpenCV,則 cv2.connectedComponentsWithAlgorithm 和 cv2.connectedComponentsWithStatsWithAlgorithm 將比前兩個運行得更快。
但一般來說,堅持使用 cv2.connectedComponentsWithStats 直到您熟悉連通分量標記。
在我們使用 OpenCV 實現(xiàn)連通分量標記和分析之前,讓我們先來看看我們的項目目錄結構。
我們將應用連通分量分析來自動過濾車牌 (license_plate.png) 中的字符。
為了完成這項任務并了解有關連通分量分析的更多信息,我們將實現(xiàn)兩個 Python 腳本:
basic_connected_components.py:演示如何應用連通分量標記,提取每個組件及其統(tǒng)計數(shù)據(jù),并在我們的屏幕上可視化它們。
filtering_connected_components.py:應用連通分量標記,通過檢查每個連通分量的寬度、高度和面積(以像素為單位)過濾掉非牌照字符。
讓我們開始使用 OpenCV 實現(xiàn)連通分量分析。
打開項目文件夾中的 basic_connected_components.py 文件,讓我們開始工作:
# 導入相關包 # 導入必要的包 import argparse import cv2 # 解析構建的參數(shù)解析器 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to input image") ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected analysis") args = vars(ap.parse_args()) # 將參數(shù)轉為字典格式
我們有兩個命令行參數(shù)
–image:輸入圖像路徑
–connectivity:4連通或者8連通
接下來,進行圖像預處理操作
# 加載輸入圖像,將其轉換為灰度,并對其進行閾值處理 image = cv2.imread(args["image"]) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
閾值處理以后,將得到如下圖像:
請注意車牌字符在黑色背景上顯示為白色。但是,輸入圖像中也有一堆噪聲也顯示為前景(白色)。我們的目標是應用連通分量分析來過濾掉這些噪聲區(qū)域,只留下車牌字符。
但在我們開始之前,讓我們先學習如何使用 cv2.connectedComponentsWithStats 函數(shù):
output = cv2.connectedComponentsWithStats(thresh, args["connectivity"], cv2.CV_32S) (numLabels, labels, stats, centroids) = output
使用OpenCV的cv2.connectedComponentsWithStats 執(zhí)行連通分量分析。我們在這里傳入三個參數(shù):
閾值化后的圖像
4連通還是8連通
數(shù)據(jù)類型(應該使用cv2.CV_32S)
然后 cv2.connectedComponentsWithStats 返回一個 4 元組:
檢測到的唯一標簽總數(shù)(即總連通分量數(shù))
一個名為labels的掩碼, 掩碼與我們的輸入閾值圖像具有相同的空間維度。對于labels中的每個位置,我們都有一個整數(shù) ID 值,該值對應于像素所屬的連通分量。您將在本節(jié)后面學習如何過濾labels矩陣。
stats:每個連通分量的統(tǒng)計信息,包括邊界框坐標和面積(以像素為單位)。
每個連通分量的質心(即中心)(x,y)坐標。
讓我們開始解析這些數(shù)值:
# 遍歷每個連通分量 for i in range(0, numLabels): # 0表示的是背景連通分量,忽略 if i == 0: text = "examining component {}/{} (background)".format( i + 1, numLabels) # otherwise, we are examining an actual connected component else: text = "examining component {}/{}".format(i + 1, numLabels) # 打印當前的狀態(tài)信息 print("[INFO] {}".format(text)) # 提取當前標簽的連通分量統(tǒng)計信息和質心 x = stats[i, cv2.CC_STAT_LEFT] y = stats[i, cv2.CC_STAT_TOP] w = stats[i, cv2.CC_STAT_WIDTH] h = stats[i, cv2.CC_STAT_HEIGHT] area = stats[i, cv2.CC_STAT_AREA] (cX, cY) = centroids[i]
if/else語句說明:
第一個連通分量,即ID 為 0,始終是背景。我們通常會忽略背景,但如果您需要它,請記住 ID=0 包含它。
否則,如果 i > 0,那么我們知道該連通分量值得進一步探索。
解析我們的統(tǒng)計數(shù)據(jù)和質心列表:
連通分量的起始x坐標
連通分量的起始y坐標
連通分量的寬(w)
連通分量的高(h)
連通分量的質心坐標(x,y)
# 可視化邊界框和當前連通分量的質心 # clone原始圖,在圖上畫當前連通分量的邊界框以及質心 output = image.copy() cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 3) cv2.circle(output, (int(cX), int(cY)), 4, (0, 0, 255), -1)
創(chuàng)建一個我們可以繪制的輸出圖像。然后我們將當前的連通分量的邊界框繪制為綠色矩形,將質心繪制為紅色圓圈。
我們的最終代碼塊演示了如何為當前連通分量創(chuàng)建掩碼:
# 創(chuàng)建掩碼 componentMask = (labels == i).astype("uint8") * 255 # 顯示輸出圖像和掩碼 cv2.imshow("Output", output) cv2.imshow("Connected Component", componentMask) cv2.waitKey(0)
首先在labels中找到與當前組件 ID 相等的所有位置。然后我們將結果轉換為一個無符號的 8 位整數(shù),其中背景值為 0,前景值為 255。最后顯示原始圖以及掩碼圖。
第一個連通分量實際上是我們的背景。我們通常會跳過,因為通常不需要背景。 然后顯示其余連通分量。對于每個連通分量,我們繪制邊界框(綠色矩形)和質心/中心(紅色圓圈)。 您可能已經(jīng)注意到,其中一些連接的組件是車牌字符,而另一些則只是“噪音”。我們將在下一部分解決這個問題。
# 導入必要的包 import argparse import cv2 # 解析構建的參數(shù)解析器 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default="plate.jpg", help="path to input image") ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected analysis") args = vars(ap.parse_args()) # 將參數(shù)轉為字典格式 # 加載輸入圖像,將其轉換為灰度,并對其進行閾值處理 image = cv2.imread(args["image"]) cv2.imshow("src", image) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv2.imshow("threshold", thresh) # 對閾值化后的圖像應用連通分量分析 output = cv2.connectedComponentsWithStats(thresh, args["connectivity"], cv2.CV_32S) (numLabels, labels, stats, centroids) = output # 遍歷每個連通分量 for i in range(0, numLabels): # 0表示的是背景連通分量,忽略 if i == 0: text = "examining component {}/{} (background)".format( i + 1, numLabels) # otherwise, we are examining an actual connected component else: text = "examining component {}/{}".format(i + 1, numLabels) # 打印當前的狀態(tài)信息 print("[INFO] {}".format(text)) # 提取當前標簽的連通分量統(tǒng)計信息和質心 x = stats[i, cv2.CC_STAT_LEFT] y = stats[i, cv2.CC_STAT_TOP] w = stats[i, cv2.CC_STAT_WIDTH] h = stats[i, cv2.CC_STAT_HEIGHT] area = stats[i, cv2.CC_STAT_AREA] (cX, cY) = centroids[i] # 可視化邊界框和當前連通分量的質心 # clone原始圖,在圖上畫當前連通分量的邊界框以及質心 output = image.copy() cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 3) cv2.circle(output, (int(cX), int(cY)), 4, (0, 0, 255), -1) # 創(chuàng)建掩碼 componentMask = (labels == i).astype("uint8") * 255 # 顯示輸出圖像和掩碼 cv2.imshow("Output", output) cv2.imshow("Connected Component", componentMask) cv2.waitKey(0)
我們之前的代碼示例演示了如何使用 OpenCV 提取連接的組件,但沒有演示如何過濾它們。
import numpy as np import argparse import cv2 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default="plate.jpg", help="path to image") ap.add_argument("-c", "--connectivity", type=int, default=4, help="connectivity for connected component analysis") args = vars(ap.parse_args()) # 加載圖像,轉為灰度,二值化 image = cv2.imread(args["image"]) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY) # 應用連通分量分析 output = cv2.connectedComponentsWithStats(thresh, connectivity=args["connectivity"], ltype=cv2.CV_32S) (numLabels, labels, stats, centriods) = output mask = np.zeros(gray.shape, dtype="uint8") for i in range(1, numLabels): # 忽略背景 x = stats[i, cv2.CC_STAT_LEFT] # [i, 0] y = stats[i, cv2.CC_STAT_TOP] # [i, 1] w = stats[i, cv2.CC_STAT_WIDTH] # [i, 2] h = stats[i, cv2.CC_STAT_HEIGHT] # [i, 3] area = stats[i, cv2.CC_STAT_AREA] # [i, 4] # 確保寬高以及面積既不太大也不太小 keepWidth = w > 50 and w < 500 keepHeight = h > 150 and h < 650 keepArea = area > 500 and area < 25000 # 我使用print語句顯示每個連接組件的寬度、高度和面積, # 同時將它們單獨顯示在屏幕上。我記錄了車牌字符的寬度、高度和面積,并找到了它們的最小/最大值, # 對于您自己的應用程序也應該這樣做。 if all((keepWidth, keepHeight, keepArea)): print("[INFO] keep connected component '{}'".format(i)) componentMask = (labels == i).astype("uint8") * 255 mask = cv2.bitwise_or(mask, componentMask) cv2.imshow("Image", image) cv2.imshow("Chracters", mask) cv2.waitKey(0)
如果我們正在構建一個自動牌照/車牌識別(ALPR/ANPR)系統(tǒng),我們將獲取這些字符,然后將它們傳遞給光學字符識別(OCR)算法進行識別。但這一切都取決于我們是否能夠將字符二值化并提取它們,連通分量分析使我們能夠做到這一點!
#include <opencv2/core/utility.hpp> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include <iostream> using namespace cv; using namespace std; Mat img; int threshval = 100; static void on_trackbar(int, void*) { Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); Mat labelImage(img.size(), CV_32S); int nLabels = connectedComponents(bw, labelImage, 8); std::vector<Vec3b> colors(nLabels); colors[0] = Vec3b(0, 0, 0);//background for(int label = 1; label < nLabels; ++label){ colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) ); } Mat dst(img.size(), CV_8UC3); for(int r = 0; r < dst.rows; ++r){ for(int c = 0; c < dst.cols; ++c){ int label = labelImage.at<int>(r, c); Vec3b &pixel = dst.at<Vec3b>(r, c); pixel = colors[label]; } } imshow( "Connected Components", dst ); } int main( int argc, const char** argv ) { CommandLineParser parser(argc, argv, "{@image|stuff.jpg|image for converting to a grayscale}"); parser.about("\nThis program demonstrates connected components and use of the trackbar\n"); parser.printMessage(); cout << "\nThe image is converted to grayscale and displayed, another image has a trackbar\n" "that controls thresholding and thereby the extracted contours which are drawn in color\n"; String inputImage = parser.get<string>(0); img = imread(samples::findFile(inputImage), IMREAD_GRAYSCALE); if(img.empty()) { cout << "Could not read input image file: " << inputImage << endl; return EXIT_FAILURE; } imshow( "Image", img ); namedWindow( "Connected Components", WINDOW_AUTOSIZE); createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar ); on_trackbar(threshval, 0); waitKey(0); return EXIT_SUCCESS; }
讀到這里,這篇“OpenCV執(zhí)行連通分量標記的方法是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。