Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to segment blood vessels python opencv

I am trying to segment the blood vessels in retinal images using Python and OpenCV. Here is the original image:

enter image description here

Ideally I want all the blood vessels to be very visible like this (different image):

enter image description here

Here is what I have tried so far. I took the green color channel of the image.

img = cv2.imread('images/HealthyEyeFundus.jpg')
b,g,r = cv2.split(img)

Then I tried to create a matched filter by following this article and this is what the output image is:

enter image description here

Then I tried doing max entropy thresholding:

def max_entropy(data):
    # calculate CDF (cumulative density function)
    cdf = data.astype(np.float).cumsum()

    # find histogram's nonzero area
    valid_idx = np.nonzero(data)[0]
    first_bin = valid_idx[0]
    last_bin = valid_idx[-1]

    # initialize search for maximum
    max_ent, threshold = 0, 0

    for it in range(first_bin, last_bin + 1):
        # Background (dark)
        hist_range = data[:it + 1]
        hist_range = hist_range[hist_range != 0] / cdf[it]  # normalize within selected range & remove all 0 elements
        tot_ent = -np.sum(hist_range * np.log(hist_range))  # background entropy

        # Foreground/Object (bright)
        hist_range = data[it + 1:]
        # normalize within selected range & remove all 0 elements
        hist_range = hist_range[hist_range != 0] / (cdf[last_bin] - cdf[it])
        tot_ent -= np.sum(hist_range * np.log(hist_range))  # accumulate object entropy

        # find max
        if tot_ent > max_ent:
            max_ent, threshold = tot_ent, it

    return threshold


img = skimage.io.imread('image.jpg')
# obtain histogram
hist = np.histogram(img, bins=256, range=(0, 256))[0]
# get threshold
th = max_entropy.max_entropy(hist)
print th

ret,th1 = cv2.threshold(img,th,255,cv2.THRESH_BINARY)

This is the result I'm getting, which is obviously not showing all the blood vessels:

enter image description here

I've also tried taking the matched filter version of the image and taking the magnitude of its sobel values.

img0 = cv2.imread('image.jpg',0)
sobelx = cv2.Sobel(img0,cv2.CV_64F,1,0,ksize=5)  # x
sobely = cv2.Sobel(img0,cv2.CV_64F,0,1,ksize=5)  # y
magnitude = np.sqrt(sobelx**2+sobely**2)

This makes the vessels pop out more:

enter image description here

Then I tried Otsu thresholding on it:

img0 = cv2.imread('image.jpg',0)
# # Otsu's thresholding
ret2,th2 = cv2.threshold(img0,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img0,(9,9),5)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

one = Image.fromarray(th2).show()
one = Image.fromarray(th3).show()

Otsu doesn't give adequate results. It ends up including noise in the results:

enter image description here

Any help is appreciated on how I can segment the blood vessels successfully.

like image 701
Shalin Shah Avatar asked Jul 15 '16 18:07

Shalin Shah


1 Answers

I worked on retina vessel detection for a bit few years ago, and there are different ways to do it:

  • If you don't need a top result but something fast, you can use oriented openings, see here and here.
  • Then you have an other version using mathematical morphology version here.

For better results, here are some ideas:

  • Personally, I used combination of Gabor filters, and results where pretty good. See the segmentation result here on the first image of drive.
  • And Gabor can be combined with learning for a good result, or here.
  • Few years ago, they claimed to have the best algorithm, but I've never had the opportunity to test it. I was sceptic about the performance gap and the way they thresholded the line detector results, it was kind of obscure.
  • But I know that nowadays, many people try to tackle the problem using CNN, but I've not heard about significant improvements.
like image 81
FiReTiTi Avatar answered Sep 23 '22 17:09

FiReTiTi