Search code examples
pythonopencvimage-processingedge-detectionimage-thresholding

How to find the exact edges/binary threshold of a metallic part like drill bit?


I am new to OpenCV and python, so kindly help me like a 12 grader. My problem is that I want to detect the right threshold or edge of the drill bit for measurement but what I have done gives a lot of noise in the image due to which I cannot find the correct contour of the object.

I have tried removing glare in the image then histogram equalization after which I tried adaptive thresholding.

gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h,s,v=cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))

bgi=cv2.GaussianBlur(gray, (3, 3), 1.0)
rn_gr = cv2.fastNlMeansDenoising(bgi,None,10,7,21)

equ = cv2.equalizeHist(rn_gr)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(rn_gr)

nonSat = s < 40
disk = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))    
nonSat = cv2.erode(nonSat.astype(np.uint8), disk) 
v2 = v.copy()    
v2[nonSat == 0] = 0;  
glare = v2 > 200;
glare = cv2.dilate(glare.astype(np.uint8), disk);
glare = cv2.dilate(glare.astype(np.uint8), disk);    
corrected = cv2.inpaint(img, glare, 5, cv2.INPAINT_NS)
object=corrected[485:1665,225:335]
gray_co=cv2.cvtColor(object, cv2.COLOR_BGR2GRAY)
bgi_co=cv2.GaussianBlur(gray_co, (3, 3), 1.0)
rn_gr_co = cv2.fastNlMeansDenoising(bgi_co,None,10,7,21)
cl2 = clahe.apply(rn_gr_co)

v=np.median(cl2)
lower=int(max(0,(1.0-sigma)*v))
upper=int(min(255,(1.0+sigma)*v))
print(lower,upper)
edged = cv2.Canny(cl2,lower,upper)
th3_o = cv2.adaptiveThreshold(obj,upper,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)
        th3_o=~th3_o

#kernel = np.ones((5,5),np.uint8)
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
morph = cv2.morphologyEx(th3_o, cv2.MORPH_GRADIENT, kernel)
closing = cv2.morphologyEx(th3_o, cv2.MORPH_CLOSE, kernel)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)

contours_o, hierarchy = cv2.findContours(th3_o,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt_o in contours_o:
   epsilon = 0.1*cv2.arcLength(cnt_o,True)
   approx = cv2.approxPolyDP(cnt_o,epsilon,True)
   con_o = cv2.drawContours(th3_o, contours_o, -1, (0,255,0), 3)
plt.imshow(con_o)
plt.show()

my expected result should look like the pic I draw with boundaries required image but what I am getting is something like this not desired image


Solution

  • I think you're using too many operations and overthinking the approach to detecting the contours. You're using too many sequential operations without realizing the goal of each step. Typically, preprocessing is done on the image to remove noise or smooth out images (Gaussian/median/bilateral blur). Then some sort of binary segmentation is done on the image to isolate contours (thresholding, Canny edge detection). From here, morphological transformations can be done to further filter or enhance such as eroding or dilating. Then you can find contours and do additional filtering (contour area, proximity, aspect ratio). For this problem, the idea is to keep it simple with a strategic approach in order to isolate the outer contour


    Here's a potential approach

    • Convert to grayscale and median blur image to remove noise and smooth image
    • Threshold image
    • Find contours
    import cv2
    
    image = cv2.imread('1.jpg')
    
    blur = cv2.medianBlur(image, 7)
    gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray,160,255, cv2.THRESH_BINARY_INV)[1]
    
    cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    for c in cnts:
        cv2.drawContours(image, [c], -1, (36, 255, 12), 2)
    
    cv2.imshow('thresh', thresh)
    cv2.imshow('image', image)
    cv2.imwrite('image.png', image)
    cv2.waitKey()