簡體   English   中英

通過 python opencv 找到齒輪的齒

[英]Finding Teeth of Gear by python opencv

我正在學習 OpenCv。 我有一個斜齒輪圖像來尋找牙齒。

到目前為止,我一直試圖找到輪廓,然后數牙齒。 我能夠找到輪廓也輪廓的坐標。 但我堅持數牙齒。 因為我是 OpenCV 的新手,所以我試圖找到牙齒的方式可能不正確。

我的代碼:

import cv2
import numpy as np
import scipy as sp
import imutils
from skimage.morphology import reconstruction

import csv

raw_image = cv2.imread('./Gear Image/new1.jpg')
#cv2.imshow('Original Image', raw_image)
#cv2.waitKey(0)

bilateral_filtered_image = cv2.bilateralFilter(raw_image, 5, 175, 175)
#cv2.imshow('Bilateral', bilateral_filtered_image)
#cv2.waitKey(0)

edge_detected_image = cv2.Canny(bilateral_filtered_image, 75, 200)
#cv2.imshow('Edge', edge_detected_image)
#cv2.waitKey(0)



contours, hierarchy = cv2.findContours(edge_detected_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)





contour_list = []
for contour in contours:
    approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
    area = cv2.contourArea(contour)
    if ((len(approx) > 5) & (len(approx) < 25) & (area > 50) ):
        contour_list.append(contour)



cv2.drawContours(raw_image, contour_list,  -1, (255,0,0), 2)


c = max(contours, key = cv2.contourArea)
M = cv2.moments(c)

cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])

cv2.circle(raw_image, (cX, cY), 5, (142, 152, 100), -1)
cv2.putText(raw_image, "centroid", (cX - 25, cY - 25),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (142, 152, 100), 2)

contour_length = "Number of contours detected: {}".format(len(contours))
cv2.putText(raw_image,contour_length , (20,40),  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (142, 152, 100), 2)

for c in range(len(contours)):
        n_contour = contours[c]
        for d in range(len(n_contour)):
            XY_Coordinates = n_contour[d]


print(len(coordinates))
print(XY_Coordinates)
print(type(XY_Coordinates))
print(XY_Coordinates[0,[0]])
print(XY_Coordinates[0,[1]])



cv2.imshow('Objects Detected',raw_image)
cv2.waitKey(0)

輸入圖像: 輸入圖像

我得到的 Output 圖像: 輸出圖像

在這個階段之后,我如何計算牙齒? 我可以使用坐標來計算間隔並計算牙齒。

或者在這個階段之后還有另一種計算牙齒的方法嗎?

我的解決方案的第一部分類似於@HansHirse發布的答案,但我使用了一種不同的方法來計算牙齒。 我的完整代碼可以在這里找到: 鏈接到python3 opencv4的完整代碼 在繼續之前檢查齒輪的外輪廓是否被正確檢測。 如果未正確檢測到齒輪,則其余部分將無效。

在計算牙齒之前,我'打開'齒輪。 我通過掃描齒輪並計算從齒輪中心到齒輪外側的距離來做到這一點。 掃過齒輪

這是我用來掃過齒輪並找到從齒輪中心到齒輪外部的距離的代碼:

# Start at angle 0, and increment the angle 1/200 rad
angle = 0
increment = 1/200
# Create a list for the distances from the centroid to the edge of the gear tooth
distances = []
# Create an image for display purposes
display_image = raw_image.copy()
# Sweep around the circle (until one full revolution)
while angle < 2*math.pi:
    # Compute a ray from the center of the circle with the current angle
    img_size = max(raw_image.shape)
    ray_end = int(math.sin(angle) * img_size + cX), int(math.cos(angle) * img_size + cY)
    center = cX, cY
    # Create mask
    mask = np.zeros((raw_image.shape[0], raw_image.shape[1]), np.uint8)
    # Draw a line on the mask
    cv2.line(mask, center, ray_end, 255, 2)
    # Mask out the gear slice (this is the portion of the gear the us below the line)
    gear_slice = cv2.bitwise_and(raw_image, raw_image, mask = mask)
    # Threshold the image
    _, thresh = cv2.threshold(cv2.cvtColor(gear_slice, cv2.COLOR_BGR2GRAY), 0 , 255, 0)
    # Find the contours in the edge_slice
    _, edge_slice_contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # Get the center of the edge slice contours
    M = cv2.moments(max(edge_slice_contours, key = cv2.contourArea))
    edge_location = int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])
    cv2.circle(display_image, edge_location, 0, (0,255,0), 4)
    # Find the distance from the center of the gear to the edge of the gear...at this specific angle
    edge_center_distance = distance(center, edge_location)
    # Find the xy coordinates for this point on the graph - draw blue circle
    graph_point = int(angle*0.5*raw_image.shape[1]/math.pi), int(edge_center_distance+ 1.5*gear_radius)
    cv2.circle(display_image, graph_point, 0, (0,255,0), 2)    
    # Add this distance to the list of distances
    distances.append(-edge_center_distance)
    # Create a temporary image and draw the ray on it
    temp = display_image.copy()
    cv2.line(temp, ray_end, (cX,cY), (0,0,255), 2)
    # Show the image and wait
    cv2.imshow('raw_image', temp)
    vid_writer.write(temp)
    k = cv2.waitKey(1)
    if k == 27: break
    # Increment the angle
    angle += increment
# Clean up
cv2.destroyAllWindows()

其結果是距離齒輪中心的齒距作為角度的函數。

import matplotlib.pyplot as plt
plt.plot(distances)
plt.show()

齒輪齒作為角度的函數

現在,計算牙齒要容易得多,因為它們是函數中的峰值(或者在這種情況下是山谷 - 后面會更多)。 為了計算峰值,我采用了齒距函數的傅里葉變換

import scipy.fftpack
# Calculate the Fourier transform
yf = scipy.fftpack.fft(distances)
fig, ax = plt.subplots()
# Plot the relevant part of the Fourier transform (a gear will have between 2 and 200 teeth)
ax.plot(yf[2:200])
plt.show()

傅里葉變換 傅立葉變換的峰值出現在37處。因此,有37個谷和38個齒輪齒。

num_teeth = list(yf).index(max(yf[2:200])) - 1
print('Number of teeth in this gear: ' + str(num_teeth))

在此處輸入圖像描述

import numpy as np
import cv2


img=cv2.imread('in2.jpg')
img_copy=img.copy()

bilated=cv2.bilateralFilter(img,5,175,175)
median=cv2.medianBlur(bilated, 5)

edges=cv2.Canny(median,75,200)


contours,_=cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
c=max(contours,key=cv2.contourArea)

cv2.drawContours(img_copy,[c],-1,(255,0,0),2)


M=cv2.moments(c)
cX=int(M['m10']/M['m00'])
cY=int(M['m01']/M['m00'])


hull=cv2.convexHull(c,clockwise=True,returnPoints=False)
defects=cv2.convexityDefects(c,hull)

inner=[]
outter=[]

for i in range(defects.shape[0]):
    start_index,end_index,farthest_index,distance=defects[i,0]
    start,end,far=[tuple(c[x][0]) for x in (start_index,end_index,farthest_index)]

    cv2.circle(img_copy,start,15,(0,0,255),1)
    cv2.circle(img_copy,far,10,[0,255,0],1)

    inner.append(far)
    inner.append(start)

distance=lambda x,y:np.sqrt(pow(x[0]-y[0],2)+pow(x[1]-y[1],2))

inner_min=min([distance((cX,cY),x) for x in inner])
outter_max=max([distance((cX,cY),x) for x in inner])

for radius in (inner_min,outter_max):
    cv2.circle(img_copy,(int(cX),int(cY)),int(radius),(0,0,255),2)

mid_radius=(inner_min+outter_max)//2
cv2.circle(img_copy,(int(cX),int(cY)),int(mid_radius),(0,255,0),2)


mask=np.zeros_like(edges)
cv2.drawContours(mask,[c],-1,255,-1)
cv2.circle(mask,(int(cX),int(cY)),int(mid_radius),0,-1)

contours,_=cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
mask=cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)

center_info=[]
for cnt in contours:
    (px,py),r=cv2.minEnclosingCircle(cnt)
    dx=px-cX
    dy=py-cY

    cv2.circle(mask,(int(px),int(py)),3,(0,0,255),-1)
    angle=(180/np.pi)*np.arctan2(dy,dx)
    center_info.append([px,py,angle])


center_info=sorted(center_info,key=lambda x:x[2])
min_angle=min([x[-1] for x in center_info])


for i,(x,y,angle) in enumerate(center_info):
    angle=((angle-min_angle)/180)*np.pi
    
    text=f'{i}'
    font=cv2.FONT_HERSHEY_SIMPLEX
    fontScale=0.6
    thickness=1
    (w,h),_=cv2.getTextSize(text,font,fontScale,thickness)
    scale=40
    tcenter=(int(x-scale*np.cos(angle)-w/2),int(y-scale*np.sin(angle)+h/2))
    cv2.putText(mask,f'{i+1}',tcenter,font,fontScale,(0,0,255),thickness)


cv2.imwrite('result.jpg',np.hstack((img_copy,mask)))


cv2.imshow('img_copy',img_copy)
cv2.imshow('mask',mask)

cv2.waitKey()
cv2.destroyAllWindows()

也許以下解決方案適合您。

  • 我在雙邊濾波后添加了一些輕微的中值模糊,以改善后續邊緣檢測(邊緣較小)。
  • findContours ,我從RETR_TREE切換到RETR_EXTERNAL以僅獲得最外部的輪廓。
  • 為此,我確定了輪廓的凸包,並確保每個齒只有一個凸包殼點。
  • 這些“稀疏”凸包船點的最終數量是齒數。

(我刪除了一些不必要的代碼以保持答案簡短。)

import cv2
import numpy as np

raw_image = cv2.imread('images/vChAL.jpg')

bilateral_filtered_image = cv2.bilateralFilter(raw_image, 5, 175, 175)

# Added median blurring to improve edge detection
median_blurred_images = cv2.medianBlur(bilateral_filtered_image, 5)

edge_detected_image = cv2.Canny(median_blurred_images, 75, 200)

# Switched from RETR_TREE to RETR_EXTERNAL to only extract most outer contours
contours, _ = cv2.findContours(edge_detected_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contour_list = []
for contour in contours:
    approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
    area = cv2.contourArea(contour)
    if ((len(approx) > 5) & (len(approx) < 25) & (area > 50) ):
        contour_list.append(contour)

cv2.drawContours(raw_image, contour_list, -1, (255, 0, 0), 2)

c = max(contours, key = cv2.contourArea)

contour_length = "Number of contours detected: {}".format(len(contours))
cv2.putText(raw_image,contour_length , (20, 40),  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (142, 152, 100), 2)

# Determine convex hull of largest contour
hull = cv2.convexHull(c, clockwise = True, returnPoints = False)

# Debug: Draw "raw" convex hull points (green)
cv2.drawContours(raw_image, c[hull], -1, (0, 255, 0), 3)

# Determine convex hull, such that nearby convex hull points are "grouped"
sparsehull = []
for idx in hull:
    if (len(sparsehull) == 0):
        sparsehull.append(idx)
    else:
        last = sparsehull[-1]
        diff = c[idx] - c[last]
        if (cv2.norm(diff) > 40):
            sparsehull.append(idx)
sparsehull = np.asarray(sparsehull)

# Debug: Draw "sparse2 convex hull points (red)
cv2.drawContours(raw_image, c[sparsehull], -1, (0, 0, 255), 3)

# Additional output on image
teeth_length = "Number of teeth detected: {}".format(len(sparsehull))
cv2.putText(raw_image, teeth_length , (20, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (142, 152, 100), 2)

cv2.imshow('Objects Detected', raw_image)
cv2.waitKey(0)

產量

免責聲明:我是Python的新手,特別是OpenCV的Python API(獲勝的C ++)。 評論,改進,突出Python no-gos非常受歡迎!

暫無
暫無

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

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