Search code examples
pythonopencvimage-processingmathematical-morphology

How can I extract only the middle bone in this image using only erosion, dilation, and, xor and thresholding?


I am using opencv to extract the middle big bone from this image and I can't seem to workout how to do itInput Image

Where I am right now

As you see, all 3 bones are showing but I only want the middle bone Here is the code

b =  cv.imread('content/B.tif', cv.IMREAD_GRAYSCALE)

plt.figure(figsize=(5,5))
plt.imshow(b, cmap='gray')
plt.axis('off')
plt.show()

_, binary_b = cv.threshold(b, 200, 255, cv.THRESH_BINARY)
b_e = cv.erode(binary_b, np.ones((1,1), dtype=np.uint8))
b_d = cv.dilate(b_e, np.ones((5,5), dtype=np.uint8),iterations = 3)

xor = cv.bitwise_and(b_d,b)

plt.figure(figsize=(5,5))
plt.imshow(xor, cmap='gray')
plt.axis('off')
plt.show()

Can someone help please?


Solution

  • I tried to find the center bone as it's largest using contour. Please see code below with explanation in comments. Hope this helps.

    import cv2
    import numpy as np
    
    #Read image and convert to gray scale
    image = cv2.imread("bone.png")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    ret, thresholded_image = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
    
    #Dilate image to close gaps
    #Erode image to remove additional edges created using dilation
    #number of iteration found by trial and error
    kernel = np.ones((5,5),np.uint8)
    thresholded_image = cv2.dilate(thresholded_image, kernel,iterations=5)
    thresholded_image = cv2.erode(thresholded_image, kernel,iterations=5)
    
    #get the contour of dilate eroded image
    contours, hierarchy = cv2.findContours(thresholded_image, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    
    #Since center bone is largest
    #Find contour with max length
    max_length=0
    max_length_ctr=None
    for ctr in contours:
        x,y,w,h = cv2.boundingRect(ctr)
        if h > max_length:
            max_length = h
            max_length_ctr = ctr
    
    #Use the found contour to create mask and get the bone
    if max_length_ctr is not None:
        big_bone_mask = np.zeros_like(thresholded_image)
        cv2.drawContours(big_bone_mask, [max_length_ctr], -1, (255, 255, 255), -1)
        cv2.imwrite("big_bone_mask.png",big_bone_mask)
    
        # Get the graph from original image from the mask
        bone_image = cv2.bitwise_and(image, image, mask=big_bone_mask)
        cv2.imwrite("bone_image.png", bone_image)
    
    

    Output enter image description here