Johan hvn
Johan hvn

Reputation: 370

Keep only outer edges of object in an image

I have a number of grayscale images as the left one below. I only want to keep the outer edges of the object in the image using python, preferably OpenCV. I have tried OpenCV erosion and then get the right image below. However, as seen in the image there is still brighter areas inside of the object. I want to remove those and only keep the outer edge. What would be an efficient way to achieve this? Simple thresholding will not work, because the bright "stripes" inside the object may sometimes be brighter than the edges. Edge detection will detect edges also inside the "main" edges.

The right image - "eroded_areas" is achieved by the following code:

   im = cv2.imread(im_path, cv2.IMREAD_GRAYSCALE)
   im_eroded = copy.deepcopy(im)
   kernel_size=3
   kernel = np.ones((kernel_size, kernel_size), np.uint8)
   im_eroded = cv2.erode(im_eroded, kernel, iterations=1)
   eroded_areas = im - im_eroded
   plt.imshow(eroded_areas)

enter image description here

Upvotes: 0

Views: 1451

Answers (2)

fmw42
fmw42

Reputation: 53089

Here is one approach in Python/OpenCV.

  • Read the input as grayscale
  • Do Otsu thresholding
  • Get contours and filter out very small defects
  • Draw the contours as white on a black background
  • Save the results

Input:

enter image description here

import cv2
import numpy as np

# read the input as grayscale
img = cv2.imread('long_blob.png', cv2.IMREAD_GRAYSCALE)

# threshold
thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# get contours and filter out small defects
result = np.zeros_like(img)
contours = cv2.findContours(thresh , cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for cntr in contours:
    area = cv2.contourArea(cntr)
    if area > 20:
        cv2.drawContours(result, [cntr], 0, 255, 1)
        
# save results
cv2.imwrite('long_blob_contours.png', result)

# show results
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)

Result:

enter image description here

Upvotes: 1

Raie
Raie

Reputation: 81

Did you try canny edge detection on it? It has a non-max suppression step in that algorithm that removes those lighter tones. I believe the canny edge detector is built into open cv but if not, heres a guide of it https://en.wikipedia.org/wiki/Canny_edge_detector

Open cv document https://docs.opencv.org/4.x/da/d22/tutorial_py_canny.html

Code ex

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

This should work from the first image since the grey scale gradually changes to the lighter and darker tones.


If this still doesnt work, maybe you can try first making the high tones higher while low tones lower using just pixel wise multiplications, then running a threshold after doing the 'im_eroded = cv2.erode(im_eroded, kernel, iterations=1)' step.


I just had a more simple solution that probably only works for your current example image. Since you have it masked perfectly, you could just change everything that is not pure black to pure white, and then run an edge detector on it. It should be able to get the main outline and those little holes and ignore those lighter stripes.


There might be a better way to do this, but this is the best Ive got for now.

Upvotes: 0

Related Questions