Search code examples
pythonopencvimage-processingcomputer-visionimage-preprocessing

How to remove border edge noise from an image using python?


I am trying to preprocess a photo of the eye vessels by removing the black border and extraneous non-eye features in the image (Ex. see below for text and "clip") by replacing the black areas with the average pixel values from 3 random squares.

crop1 = randomCrop(image2, 50, 50) #Function that finds random 50x50 area
crop2 = randomCrop(image2, 50, 50)
crop3 = randomCrop(image2, 50, 50)

mean1 = RGB_Mean(crop1)
mean2 = RGB_Mean(crop2)
mean3 = RGB_Mean(crop3)

#RGB Mean
result = [statistics.mean(k) for k in zip(mean1, mean2, mean3)]

for i in range(len(image2[:,0, 0])):
    for j in range(len(image2[0,:,0])):
        thru_pixel = image2[i, j]
        if (thru_pixel[0] < 50 and thru_pixel[1] < 50 and thru_pixel[2] < 50):
            image2[i,j, :] = result
        if (thru_pixel[0] > 190 and thru_pixel[1] > 190 and thru_pixel[2] > 190):
            image2[i,j, :] = result

However, there is leftover noise around the border of the image, as well as leftover text and a clip at the bottom left.

Here's an example image.

Original :

enter image description here

and Post-Processing

enter image description here

You can see there's still left over black-gray border noise as well as text at the bottom right and a "clip" at the bottom left. Is there anything I could try to get rid of any of these artifacts while maintaining the integrity of the eye blood vessels?

Thank you for your time and help!


Solution

  • Assuming you want to isolate the eye blood vessels, here's an approach which can be broken down into two stages, one to remove the artifacts and another to isolate blood vessels

    • Convert image to grayscale
    • Otsu's threshold to obtain binary image
    • Perform morphological operations to remove artifacts
    • Adaptive threshold to isolate blood vessels
    • Find contours and filter using maximum threshold area
    • Bitwise-and to get final result

    Beginning from your original image, we convert to grayscale and Otsu's threshold to obtain a binary image

    enter image description here

    Now we perform morph open to remove the artifacts (left). We inverse this mask to obtain the white border and then do a series of bitwise operations to get the removed artifacts image (right)

    enter image description here enter image description here

    From here we adaptive threshold to get the veins

    enter image description here

    Note there is the unwanted border so we find contours and filter using a maximum threshold area. If a contour passes the filter, we draw it onto a blank mask

    enter image description here

    Finally we perform bitwise-and on the original image to get our result

    enter image description here

    Note we could have performed additional morph open after the adaptive threshold to remove the small particles of noise but the tradeoff is that it will remove vein details. I'll leave this optional step up to you

    import cv2
    import numpy as np
    
    # Grayscale, Otsu's threshold, opening
    image = cv2.imread('1.png')
    blank_mask = np.zeros(image.shape, dtype=np.uint8)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,15))
    opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)
    
    inverse = 255 - opening
    inverse = cv2.merge([inverse,inverse,inverse])
    removed_artifacts = cv2.bitwise_and(image,image,mask=opening)
    removed_artifacts = cv2.bitwise_or(removed_artifacts, inverse)
    
    # Isolate blood vessels
    veins_gray = cv2.cvtColor(removed_artifacts, cv2.COLOR_BGR2GRAY)
    adaptive = cv2.adaptiveThreshold(veins_gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,3)
    
    cnts = cv2.findContours(adaptive, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 5000:
            cv2.drawContours(blank_mask, [c], -1, (255,255,255), 1)
    
    blank_mask = cv2.cvtColor(blank_mask, cv2.COLOR_BGR2GRAY)
    final = cv2.bitwise_and(image, image, mask=blank_mask)
    # final[blank_mask==0] = (255,255,255) # White version
    
    cv2.imshow('thresh', thresh)
    cv2.imshow('opening', opening)
    cv2.imshow('removed_artifacts', removed_artifacts)
    cv2.imshow('adaptive', adaptive)
    cv2.imshow('blank_mask', blank_mask)
    cv2.imshow('final', final)
    cv2.waitKey()