如何正确分类图像中的正(亮色)圆和负(深色)圆的数量

问题描述 投票:0回答:1

长篇大论 - 请耐心等待。

为了更好地理解目标是什么以及到目前为止我做了什么,我已经发布了代码。如果需要任何进一步的信息,请告诉我。

我有一个图像(如图所示),目标是正确分类正(蓝色)和负(紫色)圆的数量。我不关心图像中的半圆。如图中所示,有29个圆圈(不包括半圆圈),其中有7个圆圈。但是我的代码只检测到1个正数。这是我到目前为止所做的:

input_image

import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import math
import cv2.cv as cv

# --------Read Images--------------------------

I = cv2.imread('input_image.jpg')

# -----------Apply Contrast---------------------

lab = cv2.cvtColor(I, cv2.COLOR_BGR2LAB)  # Converting image to LAB Color model
l, a, b = cv2.split(lab)  # Splitting the LAB image to different channels

clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))  # Applying CLAHE to L-channel
cl = clahe.apply(l)

limg = cv2.merge((cl, a, b))  # Merge the CLAHE enhanced L-channel with the a and b channel

localContrast = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)  # Converting image from LAB Color model to RGB model

print("Local Contrast shape is", localContrast.shape)
print("Local Contrast shape is", type(localContrast))

cv2.imwrite('./Output/localContrast.jpg', localContrast)

# -------------Find Circles -----------------------

input_img = cv2.imread('./Output/localContrast.jpg')  # Read Contrast Image

gray_img = cv2.cvtColor(input_img, cv2.COLOR_BGR2GRAY)
blur_img = cv2.medianBlur(gray_img, 7)

circles = cv2.HoughCircles(blur_img, cv.CV_HOUGH_GRADIENT, dp=1, minDist=20, param1=50, param2=30, minRadius=5,
                           maxRadius=36)

circles = np.uint16(np.around(circles))

no_of_circles = 0 

radii = []
cx= []
cy = []

if circles is not None:

    # convert the (x, y) coordinates and radius of the circles to integers

    circles = np.round(circles[0, :]).astype("int")
    no_of_circles = len(circles)

    # loop over the (x, y) coordinates and radius of the circles

    for (x,y,r) in circles:

        radii.append(r)
        cx.append(x)
        cy.append(y)
        centers = [cx, cy]

        # draw the circle in the output image, then draw a rectangle
        # corresponding to the center of the circle

        cv2.circle(input_img, (x, y), r, (0, 0, 255), 2)
cv2.imwrite('/home/vr1019/Notebook/Output/circle_img.jpg', input_img) 
print ('no of circles',no_of_circles)

输出如下图所示:('no of circles',30)

circle_detected

接下来,我通过取像素值的前10%来计算每个圆的强度(这是我需要计算强度的方法)。这个想法取自createCirclesMask.m

def createCircleMask(localContrast, centers, radii):

      radii = np.reshape(radii, (len(radii),1))

      centers = np.asarray(centers)
      centers = np.transpose(centers)

      xdim = localContrast.shape[0]
      ydim = localContrast.shape[1]

      x = np.arange(0, xdim)
      y = np.arange(0, ydim)

      x = np.reshape(x, (1, len(x)))
      y = np.reshape(y, (1, len(y)))

     [xx,yy]= np.meshgrid(y, x)


      xc = centers[:,0]
      xc = np.reshape(xc, (len(xc),1))

      yc = centers[:,1]
      yc = np.reshape(yc, (len(yc),1))
      circle_intensity = []
      for ii in range(len(radii)):
           r_square = np.square(radii)
           var1= (np.square(y-xc[ii,0]))
           var2 = np.square(np.transpose(x)-yc[ii,0])
           cx,cy = np.where((var1 + var2)<r_square[ii])
           i1 =[]
           i2 =[]
           i3 =[]

           npixel = cx.shape[0]

           for j in range(npixel):

               i1.append(localContrast[cx[j],cy[j],0]);
               localContrast[cx[j],cy[j],0] = 0;

               i2.append(localContrast[cx[j],cy[j],1]);
               localContrast[cx[j],cy[j],1] = 0;

               i3.append(localContrast[cx[j],cy[j],2]);
               localContrast[cx[j],cy[j],2] = 0;

           s1= sorted(i1, reverse = True)
           s2=sorted(i2, reverse = True)
           s3=sorted(i3, reverse = True)

           # top 10 percent intensity

           m1 = np.asarray(s1[0:np.int(round(abs(len(s1)*0.1)))])
           m2 = np.asarray(s1[0:np.int(round(abs(len(s2)*0.1)))])
           m3 = np.asarray(s1[0:np.int(round(abs(len(s3)*0.1)))])

           m = np.mean((m1+m2+m3)/3)

           circle_intensity.append(m)

      print("The len of circle_intensty is", len(circle_intensity))

      return circle_intensity

然后绘制circle_intensity的直方图给出:

histogram of circle_intensity

我不知道我做错了什么。谁能帮到我这里?我在网上寻找解决方案(如pyimagesearch或stackoverflow等),但找不到我想要的东西。

python opencv detection hough-transform
1个回答
2
投票

如果你不担心一个错误分类的blob,根本没有被检测到的部分blob,以及某些blob的(显然)不准确的大小,你几乎拥有了所有权利。

要解决的最后一个问题是在明亮和黑暗斑点之间获得合理的阈值。一种方法是使用自适应阈值,例如, Otsu's method或其他人。

看看更多来自scikit-learn的threshold methods

编辑:更新以更好地匹配您的要求。


简而言之,与您的代码相比,我做了以下修改:

  • 将所有代码放在函数中(这有助于我更好地推理)
  • 我已经定义了对比度增强功能,但它没有在代码中使用(因为我的结果越来越差。)
  • 定义一个生成与圆圈相关的蒙版的函数(请注意,此函数将在PyMRT中提供,参数略有不同 - 免责声明:我是它的主要作者。)
  • 使用上面的掩模对圆进行阈值处理,使用Otsu方法确定最佳阈值

(小调:我将输入图像保存为blobs.jpg)。

我就是这样做的,但我确信通过调整参数可以提高其稳健性。

import numpy as np
import cv2
import matplotlib.pyplot as plt

from skimage.filters import threshold_otsu


# based on: https://stackoverflow.com/questions/46626267/how-to-generate-a-sphere-in-3d-numpy-array/46626448#46626448
def circle(shape, radius, position):
    semisizes = (radius,) * 2
    grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
    position = np.ogrid[grid]
    arr = np.zeros(shape, dtype=float)
    for x_i, semisize in zip(position, semisizes):
        arr += (np.abs(x_i / semisize) ** 2)
    return arr <= 1.0


def enhance_contrast(
        in_img,
        save_filepath=None):
    """Enhance contrast."""
    lab_img = cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)  
    l_ch, a_ch, b_ch = cv2.split(lab_img)
    # Applying CLAHE to L-channel
    clahe_filter = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
    l_ch = clahe_filter.apply(l_ch)
    out_img = cv2.merge((l_ch, a_ch, b_ch))
    out_img = cv2.cvtColor(out_img, cv2.COLOR_LAB2BGR)
    if save_filepath:
        cv2.imwrite(save_filepath, out_img)
    return out_img


def find_circles(
        in_filepath,
        out_filepath='circles_{in_filepath}',
        enh_filepath='enh_{in_filepath}',
        hough_circles_kws=(
            ('dp', 1), ('minDist', 15), ('param1', 30), ('param2', 30),
            ('minRadius', 5), ('maxRadius', 25)),
        verbose=True):
    """Find circles in image."""
    out_filepath = out_filepath.format(**locals())
    enh_filepath = enh_filepath.format(**locals())
    hough_circles_kws = dict(hough_circles_kws) if hough_circles_kws else {}

    in_img = cv2.imread(in_filepath)
    lab_img = cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)
    l_ch, a_ch, b_ch = cv2.split(lab_img)
    blur_l_ch = cv2.medianBlur(l_ch, 1)
    circles = cv2.HoughCircles(blur_l_ch, cv2.HOUGH_GRADIENT, **hough_circles_kws)
    if circles is not None:
        values_img = l_ch
        # compute means
        if verbose:
            print('Image size: ', values_img.shape)
        circles = np.squeeze(circles)
        values = []
        for x0, y0, r in circles:
            mask = circle(values_img.shape, r, (y0, x0))
            values.append(np.percentile(values_img[mask], 90))
        circles = np.concatenate((circles, np.array(values).reshape(-1, 1)), -1)
        threshold = threshold_otsu(np.array(values))
        if verbose:
            print('Threshold: ', threshold)
        # plot circles
        for x0, y0, r, mean in circles:
            if mean > threshold:
                # good circles in green
                cv2.circle(in_img, (int(x0), int(y0)), int(r), (0, 255, 0), 2)
            else:
                # bad circles in red
                cv2.circle(in_img, (int(x0), int(y0)), int(r), (0, 0, 255), 2)
        if verbose:
            print('Circles:')
            print(circles)
            print('Num Circles: ', circles.shape[0])
            print('Good Circles: ', np.sum(values > threshold))
    if out_filepath:
        cv2.imwrite(out_filepath.format(**locals()), in_img)
    return out_filepath, circles, threshold


out_filepath, circles, threshold = find_circles('blobs.jpg')

这将生成以下输出:

Image size:  (230, 294)
Threshold:  96.1328125
Circles:
[[ 36.5        108.5         21.10000038 155.5       ]
 [170.5        124.5         24.39999962 170.        ]
 [ 43.5        156.5         21.10000038 156.5       ]
 [ 33.5         57.5         22.20000076 190.        ]
 [101.5         40.5         19.89999962  90.        ]
 [ 75.5         78.5         18.79999924  88.        ]
 [254.5        171.5         16.60000038  82.        ]
 [138.5         52.5         15.39999962  90.        ]
 [123.5        148.5         14.39999962  90.        ]
 [ 42.5        199.5         15.39999962 174.        ]
 [138.5         15.5         14.10000038  88.        ]
 [ 86.5        176.5         15.39999962  90.        ]
 [256.5         23.5         15.5        146.        ]
 [211.5        140.5         14.39999962  87.        ]
 [132.5        193.5         13.19999981  90.1       ]
 [174.5         35.5          9.60000038  93.        ]
 [ 81.5        129.5         11.          93.        ]
 [223.5         54.5          9.60000038  87.        ]
 [177.5         75.5         13.19999981 146.        ]
 [214.5        195.5         11.          90.        ]
 [259.5        126.5          9.60000038  90.        ]
 [ 62.5         22.5         11.          96.        ]
 [220.5         98.5          9.60000038  89.        ]
 [263.5         77.5         12.10000038  84.1       ]
 [116.5        101.5          9.60000038  92.        ]
 [170.5        177.5         11.          91.        ]
 [251.5        215.5         11.          91.        ]
 [167.5        215.5         11.          87.        ]
 [214.5         14.5          9.60000038  92.        ]]
Num Circles:  29
Good Circles:  7

和相应的图像:

circles_blobs.jpg

(当然,您可以调整上面的代码以更好地满足您的需求)。

编辑:包括一些代码和数字。

也可以绘制好/坏结果的条形图:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

values = circles[:, -1]
data = [np.sum(values <= threshold), np.sum(values > threshold)]

labels = ['Bad', 'Good']
colors = ['red', 'green']

ax.bar(labels, data, color=colors)
plt.show()

circles_good_bad

或绘制完整的直方图,例如:

fig, ax = plt.subplots()

hist, edges = np.histogram(values, bins=40)
widths = (edges[1:] - edges[:-1])
ax.bar(edges[:-1] + widths / 2, hist, widths)  # plots the histogram
ax.axvline(x=threshold, color='black')  # plots the threshold (optional)
plt.show()

circles_histogram

编辑:包括额外的条形图和直方图

© www.soinside.com 2019 - 2024. All rights reserved.