与 Scikit-image 匹配的多模板

Multiple template matching with Scikit-image

我想找到一枚硬币的中间并求出半径。半径可以告诉我硬币是 5 美分还是 50 美分。找到图像后,我必须能够检测到圆圈的中间,这样我才能给它们上色

我已经开始为霍夫变换编写代码

希望有人能帮我解决这个问题。

霍夫变换代码:

image = img_as_ubyte(image)
edges = feature.canny(image, sigma=1.5, low_threshold=10, high_threshold=25)

# Detect two radii
hough_radii = np.arange(17, 35, 2)
hough_res = hough_circle(edges, hough_radii)

# Select the 11 coins
accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii,total_num_peaks=11)

# Draw them
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 4))
image = color.gray2rgb(image)
count = 0
for center_y, center_x, radius in zip(cy, cx, radii):
    circy, circx = circle_perimeter(center_y, center_x, radius,
                                    shape=image.shape)
    image[circy, circx] = (220, 20, 20)
    count += 1


ax.imshow(image)
plt.figure(figsize=(25,25))
print("In this image we can detect", count, "coins")
plt.show()

您有 2 个选择可以找到匹配的圈子。

第一个:滑动window

smaller windows 是您在输入图像中寻找的模板,这里有两个不同半径的图像分别为 19 和 21。首先,找到模板和输入图像的边缘,然后将图像的裁剪部分与模板相乘。每个靠近模板的区域都应该有更高的值。

def correlation_coefficient(patch1, patch2):
    product = np.mean((patch1 - patch1.mean()) * (patch2 - patch2.mean()))
    stds = patch1.std() * patch2.std()
    if stds == 0:
        return 0
    else:
        product /= stds
        return product


im1 =  cv2.imread("Original.jpg")
im1 = cv2.cvtColor(np.float32(im1), cv2.COLOR_BGR2GRAY)


im2 = cv2.imread("bigger.png")
im2 = cv2.cvtColor(np.float32(im2), cv2.COLOR_BGR2GRAY)


sh_row, sh_col = im1.shape
correlation = np.zeros_like(im1)

for i in range(sh_row - im2.shape[1]):
    for j in range(sh_col - im2.shape[0]):
        temp1 = im1[i : i + im2.shape[1], j : j + im2.shape[0]]
        if(temp1.shape != im2.shape):
          correlation[i, j] = 0
          continue
        correlation[i, j] = correlation_coefficient(temp1, im2)


fig = plt.figure(figsize=(10, 7))
plt.imshow(correlation, cmap=plt.cm.gray)
plt.show()

这里的模板是小一点的还是大一点的。更大的输出是

如您所见,一些点突出显示为圆心。或者通过使用第二个匹配计算:

im1 =  cv2.imread("Original.jpg")
im1 = cv2.cvtColor(np.float32(im1), cv2.COLOR_BGR2GRAY)

im1 = canny(im1, sigma=3, low_threshold=5, high_threshold=40)
im1 = im1.astype(np.uint8)

im2 = cv2.imread("bigger.png")
im2 = cv2.cvtColor(np.float32(im2), cv2.COLOR_BGR2GRAY)

im2 = canny(im2, sigma=3, low_threshold=5, high_threshold=40)
im2 = im2.astype(np.uint8)
sh_row, sh_col = im1.shape

d = 1

correlation = np.zeros_like(im1)

for i in range(sh_row - im2.shape[1]):
    for j in range(sh_col - im2.shape[0]):
        temp1 = im1[i : i + im2.shape[1], j : j + im2.shape[0]]
        if(temp1.shape != im2.shape):
          correlation[i, j] = 0
          continue
        correlation[i, j] = np.sum(np.multiply(temp1, im2))

io.imshow(correlation, cmap='gray')
io.show()

此外,我们有相同的结果:

第一种方法无法帮助我们找到指定的圈子。因为你需要为两个模板都设置一个门槛,解决越来越多的挑战。让我们研究另一种方法。

第一个:霍夫变换

首先,运行canny边缘检测器,然后找出所有半径范围为1到100的圆。然后删除近圆(中心彼此靠近的圆):

import numpy as np
import matplotlib.pyplot as plt
import cv2
from skimage import data, color
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte
from skimage import io, feature
from scipy import ndimage
import imutils
from scipy import signal
from skimage import io, feature



image = cv2.imread("Original.jpg")
fig = plt.figure(figsize=(10, 7))

fig.add_subplot(1, 2, 1)
plt.imshow(image)
plt.axis('off')
plt.title("Original Image")

image = cv2.cvtColor(np.float32(image), cv2.COLOR_BGR2GRAY)
edges = canny(image, sigma=3, low_threshold=5, high_threshold=40)

fig.add_subplot(1, 2, 2)
plt.imshow(edges, cmap=plt.cm.gray)
plt.axis('off')
plt.title("After Run Canny Edge Detector")

# which raddii?
hough_radii = np.arange(1, 100)
hough_res = hough_circle(edges, hough_radii)

accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii, total_num_peaks=100)
output = np.column_stack((cx, cy, radii))
output = output[(output[:,2] > 10)]

output = output[np.argsort(output[:, 1])]
output = output[np.argsort(output[:, 0])]

print(f"Circles Before Edit")
print(f"cx={output[:,0]}")
print(f"cy={output[:,1]}")
print(f"radii={output[:,2]}")

index = 0
flag = False
while (index < output.shape[0] - 1):

  if(abs (output[index][0] - output[index+1][0]) < 5 ):
    if(abs (output[index][1] - output[index+1][1]) < 5 ):
      # print(f"del index {index+1}")
      output = np.delete(output, (index+1), axis=0)
      flag = True
    else:
      flag = False
  else:
    flag = False
  if(flag is not True):
    index += 1
print(f"Circles After Edit")
print(f"cx={output[:,0]}")
print(f"cy={output[:,1]}")
print(f"radii={output[:,2]}")


plt.show()


red = output[(output[:,2] <= 20)]
print(f"Red Circles")
print(f"cx={red[:,0]}")
print(f"cy={red[:,1]}")
print(f"radii={red[:,2]}")

green = output[(output[:,2] >= 20)]
print(f"Green Circles")
print(f"cx={green[:,0]}")
print(f"cy={green[:,1]}")
print(f"radii={green[:,2]}")

检查结果:

Circles Before Edit
cx=[ 96  96  97  98 105 106 146 165 188 189 196 196 202 203 204 216 264 265]
cy=[137 138 136 138 232 232 356 229 102 102 166 166 222 221 286 322 116 116]
radii=[22 23 23 21 22 21 19 21 21 22 19 18 19 18 22 19 18 19]
Circles After Edit
cx=[ 96 105 146 165 188 196 202 204 216 264]
cy=[137 232 356 229 102 166 222 286 322 116]
radii=[22 22 19 21 21 19 19 22 19 18]

完成所有必要的计算后:

Red Circles
cx=[146 196 202 216 264]
cy=[356 166 222 322 116]
radii=[19 19 19 19 18]
Green Circles
cx=[ 96 105 165 188 204]
cy=[137 232 229 102 286]
radii=[22 22 21 21 22]

如结果中所述,5 个圆圈 被提议为 红色 5 个圆圈 被提议到 绿色.

更新#1

对此类问题进行预处理前要谨慎。这些处理如腐蚀、膨胀和中值滤波器改变圆的半径。

画圆:

输入半径为19和21,半径大于20为绿色,半径小于20为红色。为简单起见,我将半径设置为黄色 = 20。在最后一步中,您应该删除丰度圆。下面我将所有这些放在一起:

def remove_redundance(output):
  
  print(f"output.shape={output.shape}")
  index = 0
  del_first = False
  index_first = 0
  while (index_first < output.shape[0]):
    index_second = index_first + 1
    del_second = False
    while (index_second < output.shape[0]):

      if( (abs(output[index_first][0] - output[index_second][0]) < 10) and 
         (abs(output[index_first][1] - output[index_second][1]) < 10) ):
        
        if(output[index_first][3] > output[index_second][3]):
          output = np.delete(output, (index_second), axis=0)
          del_second = True
        else:
          output = np.delete(output, (index_first), axis=0)
          del_first = True
          break

      else:
        del_second = False
        del_first = False

      if (del_second == False):
        index_second += 1
    
    
    if (del_first == False):
      index_first += 1
    else:
      del_first = False

  print(f"output.shape={output.shape}")
  return output
  

def draw_circles(circle, coordinate, green = 0):
  for cx, cy, radii in zip(coordinate[:,0], coordinate[:,1], coordinate[:,2]):
    # Center coordinates
    center_coordinates = (int(cx), int(cy))
    
    # Radius of circle
    radius = int(radii)
    
    if(green == 1):
      color = (0, 255, 0)
    elif(green == 0):
      color = (0, 0, 255)
    elif(green == -1):
      color = (0, 255, 255)
      
    thickness = 1#-1
      
    circle = cv2.circle(circle, center_coordinates, radius, color, thickness)

  return circle



image = cv2.imread("Original.jpg")
# image = cv2.medianBlur(image, 3)
fig = plt.figure(figsize=(10, 7))

fig.add_subplot(1, 2, 1)
plt.imshow(image)
plt.axis('off')
plt.title("Original Image")

image = cv2.cvtColor(np.float32(image), cv2.COLOR_BGR2GRAY)
edges = canny(image, sigma=3, low_threshold=5, high_threshold=40)

fig.add_subplot(1, 2, 2)
plt.imshow(edges, cmap=plt.cm.gray)
plt.axis('off')
plt.title("After Run Canny Edge Detector")

# which raddii?
hough_radii = np.arange(1, 500)
hough_res = hough_circle(edges, hough_radii)

accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii, total_num_peaks=500)
output = np.column_stack((cx, cy, radii, accums))#
output = output[(output[:,2] > 10)]

output = output[np.argsort(output[:, 1])]
output = output[np.argsort(output[:, 0])]

print(f"Circles Before Edit")
print(f"cx={output[:,0]}")
print(f"cy={output[:,1]}")
print(f"radii={output[:,2]}")


output = remove_redundance(output)


print(f"Circles After Edit")
print(f"cx={output[:,0]}")
print(f"cy={output[:,1]}")
print(f"radii={output[:,2]}")


plt.show()


red = output[(output[:,2] < 20)]
if(red.shape[0]>0):
  print(f"Red Circles")
  print(f"cx={red[:,0]}")
  print(f"cy={red[:,1]}")
  print(f"radii={red[:,2]}")

green = output[(output[:,2] > 20)]
if(green.shape[0]>0):
  print(f"Green Circles")
  print(f"cx={green[:,0]}")
  print(f"cy={green[:,1]}")
  print(f"radii={green[:,2]}")


yellow = output[(output[:,2] == 20)]
if(yellow.shape[0]>0):
  print(f"yellow Circles")
  print(f"cx={yellow[:,0]}")
  print(f"cy={yellow[:,1]}")
  print(f"radii={yellow[:,2]}")


circle = cv2.imread("Original.jpg")

if(red.shape[0]>0):
  circle  = draw_circles(circle, red, green = 0)
if(green.shape[0]>0):
  circle  = draw_circles(circle, green, green = 1)
if(yellow.shape[0]>0):
  circle  = draw_circles(circle, yellow, green = -1)

circle = cv2.cvtColor(circle, cv2.COLOR_BGR2RGB)

fig = plt.figure(figsize=(10, 7))
plt.imshow(circle)
plt.axis('off')
plt.title("Circles") 

结果: