簡體   English   中英

OpenCV 檢測圖像上的斑點

[英]OpenCV detect blobs on the image

我需要找到(並在周圍繪制矩形)/獲取圖像上的最大和最小半徑斑點。 (以下樣本)

問題是為圖像找到正確的過濾器,以允許CannyThreshold轉換來突出顯示斑點。 然后我將使用findContours來查找矩形。

我試過:

  • Threshold - 不同級別

  • blur->erode->erode->grayscale->canny

  • 用各種“線條”改變圖像色調

等等。 更好的結果是檢測到一塊 (20-30%) 的斑點。 並且此信息不允許在 blob 周圍繪制矩形。 此外,感謝陰影,檢測到與斑點無關的陰影,因此也可以防止檢測該區域。

據我了解,我需要找到具有強烈對比度的計數器(不像陰影中那樣平滑)。 有沒有辦法用openCV做到這一點?

更新

分情況:圖1圖2圖3圖4圖5圖6圖7圖8圖9圖10圖11圖12

又一更新

我相信斑點在邊緣有對比區域。 所以,我試圖讓邊緣更強大:我創建了 2 個gray scale Mat: A and B ,為第二個應用Gaussian blur - B (減少一點噪音) ,然后我做了一些計算:去圍繞每個像素並找到“A”的Xi,Yi和來自“B”的附近點之間的最大差異:

在此處輸入圖片說明

並將max差異應用於Xi,Yi 所以我得到了這樣的東西:

在此處輸入圖片說明

我在正確的路上嗎? 順便說一句,我可以通過 OpenCV 方法達到這樣的目的嗎?

更新圖像去噪有助於減少噪音, Sobel - 突出顯示輪廓,然后threshold + findContourscustome convexHull與我正在尋找的相似,但它對某些斑點不利。

由於輸入圖像之間存在較大差異,算法應該能夠適應這種情況。 由於 Canny 基於檢測高頻,我的算法將圖像的清晰度視為用於預處理自適應的參數。 我不想花一周時間找出所有數據的函數,所以我基於 2 張圖像應用了一個簡單的線性函數,然后用第三張圖像進行了測試。 這是我的結果:

第一個結果

第二個結果

第三個結果

請記住,這是一種非常基本的方法,只是證明了一點。 它將需要實驗、測試和改進。 這個想法是使用 Sobel 並對獲取的所有像素求和。 那個,除以圖像的大小,應該給你一個高頻的基本估計。 圖像的反應。 現在,通過實驗,我發現 CLAHE 濾波器的 clipLimit 值在 2 個測試用例中起作用,並發現了一個連接高頻的線性函數 使用 CLAHE 濾波器響應輸入,產生良好的結果。

sobel = get_sobel(img)
clip_limit = (-2.556) * np.sum(sobel)/(img.shape[0] * img.shape[1]) + 26.557

這就是自適應部分。 現在是輪廓。 我花了一段時間才找到過濾噪音的正確方法。 我解決了一個簡單的技巧:使用兩次輪廓查找。 首先,我用它來過濾掉不必要的、嘈雜的輪廓。 然后我繼續使用一些形態魔法,最終為被檢測的對象生成正確的 blob(代碼中的更多細節)。 最后一步是根據計算的平均值過濾邊界矩形,因為在所有樣本上,斑點的大小相對相似。

import cv2
import numpy as np


def unsharp_mask(img, blur_size = (5,5), imgWeight = 1.5, gaussianWeight = -0.5):
    gaussian = cv2.GaussianBlur(img, (5,5), 0)
    return cv2.addWeighted(img, imgWeight, gaussian, gaussianWeight, 0)


def smoother_edges(img, first_blur_size, second_blur_size = (5,5), imgWeight = 1.5, gaussianWeight = -0.5):
    img = cv2.GaussianBlur(img, first_blur_size, 0)
    return unsharp_mask(img, second_blur_size, imgWeight, gaussianWeight)


def close_image(img, size = (5,5)):
    kernel = np.ones(size, np.uint8)
    return cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)


def open_image(img, size = (5,5)):
    kernel = np.ones(size, np.uint8)
    return cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)


def shrink_rect(rect, scale = 0.8):
    center, (width, height), angle = rect
    width = width * scale
    height = height * scale
    rect = center, (width, height), angle
    return rect


def clahe(img, clip_limit = 2.0):
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(5,5))
    return clahe.apply(img)


def get_sobel(img, size = -1):
    sobelx64f = cv2.Sobel(img,cv2.CV_64F,2,0,size)
    abs_sobel64f = np.absolute(sobelx64f)
    return np.uint8(abs_sobel64f)


img = cv2.imread("blobs4.jpg")
# save color copy for visualizing
imgc = img.copy()
# resize image to make the analytics easier (a form of filtering)
resize_times = 5
img = cv2.resize(img, None, img, fx = 1 / resize_times, fy = 1 / resize_times)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# use sobel operator to evaluate high frequencies
sobel = get_sobel(img)
# experimentally calculated function - needs refining
clip_limit = (-2.556) * np.sum(sobel)/(img.shape[0] * img.shape[1]) + 26.557

# don't apply clahe if there is enough high freq to find blobs
if(clip_limit < 1.0):
    clip_limit = 0.1
# limit clahe if there's not enough details - needs more tests
if(clip_limit > 8.0):
    clip_limit = 8

# apply clahe and unsharp mask to improve high frequencies as much as possible
img = clahe(img, clip_limit)
img = unsharp_mask(img)

# filter the image to ensure edge continuity and perform Canny
# (values selected experimentally, using trackbars)
img_blurred = (cv2.GaussianBlur(img.copy(), (2*2+1,2*2+1), 0))
canny = cv2.Canny(img_blurred, 35, 95)

# find first contours
_, cnts, _ = cv2.findContours(canny.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# prepare black image to draw contours
canvas = np.ones(img.shape, np.uint8)
for c in cnts:
    l = cv2.arcLength(c, False)
    x,y,w,h = cv2.boundingRect(c)
    aspect_ratio = float(w)/h

    # filter "bad" contours (values selected experimentally)
    if l > 500:
        continue
    if l < 20:
        continue
    if aspect_ratio < 0.2:
        continue
    if aspect_ratio > 5:
        continue
    if l > 150 and (aspect_ratio > 10 or aspect_ratio < 0.1):
        continue
    # draw all the other contours
    cv2.drawContours(canvas, [c], -1, (255, 255, 255), 2)

# perform closing and blurring, to close the gaps
canvas = close_image(canvas, (7,7))
img_blurred = cv2.GaussianBlur(canvas, (8*2+1,8*2+1), 0)
# smooth the edges a bit to make sure canny will find continuous edges
img_blurred = smoother_edges(img_blurred, (9,9))
kernel = np.ones((3,3), np.uint8)
# erode to make sure separate blobs are not touching each other
eroded = cv2.erode(img_blurred, kernel)
# perform necessary thresholding before Canny
_, im_th = cv2.threshold(eroded, 50, 255, cv2.THRESH_BINARY)
canny = cv2.Canny(im_th, 11, 33)

# find contours again. this time mostly the right ones
_, cnts, _ = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# calculate the mean area of the contours' bounding rectangles
sum_area = 0
rect_list = []
for i,c in enumerate(cnts):
    rect = cv2.minAreaRect(c)
    _, (width, height), _ = rect
    area = width*height
    sum_area += area
    rect_list.append(rect)
mean_area = sum_area / len(cnts)

# choose only rectangles that fulfill requirement:
# area > mean_area*0.6
for rect in rect_list:
    _, (width, height), _ = rect
    box = cv2.boxPoints(rect)
    box = np.int0(box * 5)
    area = width * height

    if(area > mean_area*0.6):
        # shrink the rectangles, since the shadows and reflections
        # make the resulting rectangle a bit bigger
        # the value was guessed - might need refinig
        rect = shrink_rect(rect, 0.8)
        box = cv2.boxPoints(rect)
        box = np.int0(box * resize_times)
        cv2.drawContours(imgc, [box], 0, (0,255,0),1)

# resize for visualizing purposes
imgc = cv2.resize(imgc, None, imgc, fx = 0.5, fy = 0.5)
cv2.imshow("imgc", imgc)
cv2.imwrite("result3.png", imgc)
cv2.waitKey(0)

總的來說,我認為這是一個非常有趣的問題,有點太大了,無法在這里回答。 我提出的方法應被視為路標,而不是完整的解決方案。 基本思想是:

  1. 自適應預處理。

  2. 兩次查找輪廓:用於過濾,然后用於實際分類。

  3. 根據平均大小過濾斑點。

謝謝你的樂趣和祝你好運!

這是我使用的代碼:

import cv2
from sympy import Point, Ellipse
import numpy as np
x1='C:\\Users\\Desktop\\python\\stack_over_flow\\XsXs9.png'    
image = cv2.imread(x1,0)
image1 = cv2.imread(x1,1)
x,y=image.shape
median = cv2.GaussianBlur(image,(9,9),0)
median1 = cv2.GaussianBlur(image,(21,21),0)
a=median1-median
c=255-a
ret,thresh1 = cv2.threshold(c,12,255,cv2.THRESH_BINARY)
kernel=np.ones((5,5),np.uint8)
dilation = cv2.dilate(thresh1,kernel,iterations = 1)
kernel=np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(dilation, cv2.MORPH_OPEN, kernel)
cv2.imwrite('D:\\test12345.jpg',opening)
ret,contours,hierarchy =    cv2.findContours(opening,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
c=np.size(contours[:])
Blank_window=np.zeros([x,y,3])
Blank_window=np.uint8(Blank_window)
for u in range(0,c-1):
    if (np.size(contours[u])>200):
        ellipse = cv2.fitEllipse(contours[u])
        (center,axes,orientation) =ellipse
        majoraxis_length = max(axes)
        minoraxis_length = min(axes)
        eccentricity=(np.sqrt(1-(minoraxis_length/majoraxis_length)**2))
        if (eccentricity<0.8):
             cv2.drawContours(image1, contours, u, (255,1,255), 3)
cv2.imwrite('D:\\marked.jpg',image1)

這是輸出圖像

這里的問題是找到一個接近圓形的物體。 這個簡單的解決方案基於找到每個輪廓的偏心率。 這種被檢測到的物體是水滴。

我有一個部分解決方案。

第一的

我最初將圖像轉換為 HSV 顏色空間並修改了通道。 在這樣做時,我遇到了一些獨特的東西。 在幾乎每幅圖像中,水滴都有微小的光反射。 這在價值渠道中得到了明顯的強調。

將其反轉后,我能夠獲得以下結果:

示例 1:

在此處輸入圖片說明

示例 2:

在此處輸入圖片說明

示例 3:

在此處輸入圖片說明

第二

現在我們必須提取這些點的位置。 為此,我對獲得的反轉值通道進行了異常檢測。 我所說的異常是指它們中存在的黑點。

為了做到這一點,我計算了倒置值通道的值。 我將中位數上下 70% 內的像素值分配為正常像素。 但是每一個超出這個范圍的像素值都是異常的。 黑點在那里完美契合。

示例 1:

在此處輸入圖片說明

示例 2:

在此處輸入圖片說明

示例 3:

在此處輸入圖片說明

對於少數圖像,結果並不理想。

如您所見,黑點是由於水滴特有的光反射造成的。 圖像中可能存在其他圓形邊緣,但反射將液滴與這些邊緣區分開來。

第三

現在既然我們有了這些黑點的位置,我們就可以進行高斯差分(DoG)(在問題的更新中也提到過)並獲得相關的邊緣信息。 如果獲得的黑點位置位於發現的邊緣內,則稱其為水滴。

免責聲明:此方法不適用於所有圖像。 您可以為此添加您的建議。

美好的一天,我正在研究這個主題,我給你的建議是; 首先,在使用了許多去噪濾波器(例如高斯濾波器)之后,再對圖像進行處理。 您可以不使用計數器對這些圓圈進行斑點檢測。

暫無
暫無

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

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