How to remove internal and border contours from an image using OpenCV?

Question:

I used the findContours() function from OpenCv to draw contour lines.
I want to eliminate border and internal contours from it

enter image description here

This is the code I’ve used to draw contours

import numpy as np
import cv2 as cv

im = cv.imread('0.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
#ret, thresh = cv.threshold(imgray, 127, 255, 0)
blur = cv.GaussianBlur(imgray,(5,5),0)
ret3,thresh = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, 
cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(im, contours, -1 ,(0,0,255), 1)
cv.imwrite('image.jpg',im)
Asked By: Giri Kishore

||

Answers:

The function findContours detects contours around non-zero pixels. In your example the background is white, while the objects you are trying to detect are black, therefore the contours are detected around the background, not the objects as you expect. You can simply negate the image with cv2.bitwise_not function to make the background black, assuming that the background color is 255.

Now when you have correctly defined objects and background, you can use the flag CV_RETR_EXTERNAL for findContours function to detect only external contours. Note that this flag won’t work if the background is white, because all those letters are internal contours for this one big contour on the image border.

Here is the fixed code:

import numpy as np
import cv2 as cv

im = cv.imread('0.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
blur = cv.GaussianBlur(imgray, (5, 5), 0)
ret3, thresh = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
thresh_inverse = cv.bitwise_not(imgray)
contours, hierarchy = cv.findContours(thresh_inverse, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(im, contours, -1, (0, 0, 255), 1)
cv.imwrite('image.jpg', im)

Update

As an alternative for using the cv2.bitwise_not you can change the thresholding function that it assigns 255 value to dark letters instead of background:

ret3, thresh = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
Answered By: Zygfryd Wieszok