簡體   English   中英

OpenCV & Python - 如何使用卡爾曼濾波器從 OpenCV 檢測到的不規則多邊形中過濾噪聲?

[英]OpenCV & Python - How does one filter noise from an irregular shaped polygon detected by OpenCV using a Kalman filter?

我有一個正在處理的小型跟蹤項目。 我有我的逐幀檢測方案設置和工作。 當我運行時,即使場景是 static,我也會在提取的多邊形中得到相當多的噪聲。 由於我希望實時運行,因此卡爾曼濾波似乎是解決此問題的最佳方法; 但是實現細節很少。 我通過 google 看到了一些示例,但它們通常處理邊界框或規則形狀,僅用少量信息進行描述。 我不確定這種方法是否有效。

我有興趣跟蹤下面更不規則幾何的演變。 描述多邊形需要大約 100 個點或更多。 如何調整 OpenCV 卡爾曼工具來處理此任務?

提前致謝。

**更新**

所以額外的細節。 我需要對 object 進行准確的分析以進行下游分析,因此不能選擇邊界框。 我的相機可以以 30 fps 的速度產生幀,但我不需要處理那么快,盡管我也不想每秒只處理 1 個。 進行快速去噪操作太慢了。 我的圖像是 4024x3036 單色圖像。 我附上了我的場景的六個鏡頭的 jpeg 版本。 樣本是圖像底部三分之一的兩個盤子中心的小塊。 我還附上了我希望從每一幀中提取的不規則多邊形,該多邊形與形狀的 2d 輪廓准確匹配。 我更喜歡准確性和穩定性而不是速度,但我想每秒處理幾幀。

我將 go 捕捉一些有代表性的圖像或小電影,並將很快發布。

提前致謝。

示例圖像

第 1 槍

射擊 2

射擊 3

第 4 槍

第 5 槍

第 6 槍

目標

我正在尋找的示例

這個概念

請注意,在圖像的列中,紫色線應該 go 最黑的列如何? 我們可以通過首先檢測具有至少一定數量黑色的第一列和最后一列來檢測 ROI (感興趣區域) 然后檢測 2 個檢測到的列之間的行,其中白色首先開始並首先在 2 列結束。

編碼

import cv2
import numpy as np

files = [f"img{i}.jpg" for i in range(1, 6)]

for file in files:
    img = cv2.imread(file)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
    sum_cols = thresh.sum(0)
    indices = np.where(sum_cols < sum_cols.min() + 40000)[0]
    x1, x2 = indices[0] - 50, indices[-1] + 50
    diff1, diff2 = np.diff(thresh[:, [x1, x2]].T, 1)
    y1_1, y2_1 = np.where(diff1)[0][:2]
    y1_2, y2_2 = np.where(diff2)[0][:2]
    y1, y2 = min(y1_1, y1_2), max(y2_1, y2_2)
    img_canny = cv2.Canny(thresh[y1: y2, x1: x2], 50, 50)
    contours, _ = cv2.findContours(img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cv2.line(img, (x1, y1_1), (x2, y1_2), (255, 0, 160), 5)
    cv2.line(img, (x1, y2_1), (x2, y2_2), (255, 0, 160), 5)
    cv2.drawContours(img[y1: y2, x1: x2], contours, -1, (0, 0, 255), 10)
    cv2.imshow("Image", img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

Output

以下是程序將為您提供的每個不同圖像提供的 output:

在此處輸入圖像描述

在此處輸入圖像描述

在此處輸入圖像描述

在此處輸入圖像描述

在此處輸入圖像描述

說明

  1. 導入必要的庫:
import cv2
import numpy as np
  1. 由於您有相機,顯然您將使用cv2.VideoCapture()方法。 由於我只有您提供的圖像,因此我將讓程序讀取每張圖像。 因此,將每個圖像文件名存儲到一個列表中(我有img1.jpgimg1.jpg ,... img5.jpg ,遍歷名稱並讀取每個圖像:
files = [f"img{i}.jpg" for i in range(1, 6)]

for file in files:
    img = cv2.imread(file)
  1. 將每張圖片轉換為灰度圖,使用cv2.threshold()方法將灰度圖轉換為只有2個值; 小於或等於127的每個像素為0 ,大於127的每個像素為255
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
  1. 為了找到0最多的列(這意味着黑色最多) ,我們需要找到每列的總和,其中最小的總和來自0最多的列。 通過每列的總和,我們可以使用np.where()方法來查找閾值圖像中每一列的索引,其總和接近於檢測到的最小總和。 然后,我們可以將檢測到的列的第一個索引和最后一個索引作為 ROI 的x1x2 (以及50像素的填充)
    sum_cols = thresh.sum(0)
    indices = np.where(sum_cols < sum_cols.min() + 40000)[0]
    x1, x2 = indices[0] - 50, indices[-1] + 50
  1. 為了找到頂部行的y1y2 ,我們需要檢測在檢測到的列的第一個邊緣和檢測到的列的最后一個邊緣中第一次出現從0255的變化的索引。 同樣,為了找到底線的y1y2 ,我們需要在檢測到的列的第一個邊緣和檢測到的列的最后一個邊緣檢測從2550的第一次變化的索引。列。 最后,使用我們的 4 個y坐標,我們可以通過獲取第一行中最小的y坐標和第二行中最大的y坐標來獲得 ROI 的y1y2
    diff1, diff2 = np.diff(thresh[:, [x1, x2]].T, 1)
    y1_1, y2_1 = np.where(diff1)[0][:2]
    y1_2, y2_2 = np.where(diff2)[0][:2]
    y1, y2 = min(y1_1, y1_2), max(y2_1, y2_2)
  1. 現在我們有了投資回報率。 我們可以使用 Canny 邊緣檢測器檢測出 ROI 內對象的邊緣,並使用cv2.findContours()方法檢測邊緣的輪廓:
    img_canny = cv2.Canny(thresh[y1: y2, x1: x2], 50, 50)
    contours, _ = cv2.findContours(img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  1. 最后,我們可以在非二進制圖像上繪制線條和輪廓,並顯示圖像:
    cv2.line(img, (x1, y1_1), (x2, y1_2), (255, 0, 160), 5)
    cv2.line(img, (x1, y2_1), (x2, y2_2), (255, 0, 160), 5)
    cv2.drawContours(img[y1: y2, x1: x2], contours, -1, (0, 0, 255), 10)
    cv2.imshow("Image", img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

您可以嘗試此解決方案,看看輪廓是否仍然跳動,讓卡爾曼先生 rest 安靜:)以下代碼將僅生成部分屬於您的 object 部分屬於板的上側和下側的輪廓。 您將不得不做更多的處理來連接兩條線以獲得整個 object 輪廓。 代碼中的假設是子圖像將始終包含 ROI。 順便說一句,我非常懷疑您是否可以在這里使用卡爾曼,因為您沒有固定的可跟蹤/可識別輪廓點。 處理速度應該相當高效,以便每秒可以處理多個圖像。

gw1,gs1,gw2,gs2 = (5,1,7,3)

rgb = cv2.imread('/your/test/image/so_kalman.jpeg')
gray = cv2.cvtColor(rgb, cv2.COLOR_BGR2GRAY)
wk_img = gray[2000:2500, 500:2000] # Work on a sub-image
min_ctr_area = 30000
max_ctr_area = 70000

g1 = cv2.GaussianBlur(wk_img, (gw1, gw1), gs1)
g2 = cv2.GaussianBlur(wk_img, (gw2, gw2), gs2)
ret, th = cv2.threshold(g2-g1, 200, 255, cv2.THRESH_BINARY)

h, w = th.shape
cv2.rectangle(th, (0, 0), (w, h), 255, 5)

contours, hier = cv2.findContours(th.copy(),cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
out_img = rgb[2000:2500, 500:2000].copy()

for i in range(len(contours)):
    if hier[0][i][3] == -1:
        continue

    x,y,w,h = cv2.boundingRect(contours[i])
    if min_ctr_area < w*h < max_ctr_area:
        cv2.drawContours(out_img, [contours[i]], -1, (255, 0, 0), 2)

plt.imshow(out_img)

這是您的一張測試圖像的結果 這是一個簡單的解決方案,但希望能滿足您的期望。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM