Search code examples
pythonopencvdistance

Distance transform - the function does not work properly


I am trying to get the distance transform image, but I am getting a bad result.

Image:

enter image description here

Code:

import cv2
import imutils
import numpy as np

photo = 'dog.jpg'

img = cv2.imread(photo)
ybprc = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
inrangeHsv = cv2.inRange(ybprc, np.array([0, 135, 85]), np.array([255, 180, 135]))
retval, otsu = cv2.threshold(inrangeHsv, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cnts = cv2.findContours(otsu.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)
mask = cv2.drawContours(otsu, [c], -1, (0,255,0), 2)
out = cv2.distanceTransform(mask, distanceType=cv2.DIST_L2, maskSize=5)

cv2.imshow("distance_transform", out)
cv2.waitKey(0)
cv2.destroyAllWindows()

And result is: enter image description here

But the result should be more like:

enter image description here

How should I fix it?


Solution

  • Your issue is that imshow() will not present the image properly. You need to do imwrite().

    This works for me in Python/OpenCV.

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # read input
    img = cv2.imread('dog.jpg')
    
    # convert to YCbCr
    ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
    
    # use inRange thresholding
    thresh = cv2.inRange(ycrcb, np.array([0, 135, 85]), np.array([255, 180, 135]))
    
    # get outer contour
    cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    big_contour = max(cnts, key=cv2.contourArea)
    
    # draw filled contour on black background
    filled = np.zeros_like(thresh)
    cv2.drawContours(filled, [big_contour], -1, (255), cv2.FILLED)
    
    # get distance transform
    result = filled.copy()
    result = cv2.distanceTransform(result, distanceType=cv2.DIST_L2, maskSize=3, dstType=cv2.CV_8U)
    
    # stretch to full dynamic range
    result2 = cv2.normalize(result, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
    
    # save result
    cv2.imwrite('dog_thresh.png', thresh)
    cv2.imwrite('dog_filled_contour.png', filled)
    cv2.imwrite('dog_distance.png', result)
    cv2.imwrite('dog_distance_normalized.png', result2)
    
    # show results
    # note result image will look binary
    cv2.imshow("thresh", thresh)
    cv2.imshow("filled", filled)
    cv2.imshow("result", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    


    Threshold image:

    enter image description here

    Filled contour:

    enter image description here

    Distance Result:

    enter image description here

    Normalized Distance Result:

    enter image description here