mxdbld
mxdbld

Reputation: 17725

Contouring a binary mask with OpenCV / Python

With Python and OpenCV I am detecting contours of a binary mask:

import numpy as np
import cv2
import matplotlib.pyplot as plt

mask = np.zeros(20000, dtype=np.uint8).reshape(100, 200)
mask[5:-5,5:-5] = 255
mask[10:70,40:80] = 0
plt.subplot(121)
plt.imshow(mask, cmap='Greys_r', interpolation='none')

_, contours, hierarchy = cv2.findContours(mask.copy(), 
                                          cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,
                                          offset=(0, 0))

Resulting in an expected behaviour :

plt.subplot(122)
cv2.drawContours(mask, contours, -1, (127, 127, 127), 2)
plt.imshow(mask, cmap='Greys_r',  interpolation='none')
plt.show()

Simple opencv contouring

However, I cannot seem to understand the result of a full activated mask :

mask = np.ones(20000, dtype=np.uint8).reshape(100, 200)
mask *=255
_, contours, hierarchy = cv2.findContours(mask.copy(),
                                            cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,
                                            offset=(0, 0))

print contours[0]

Which produces:

(1   1), (1  98), (198 98), (198 1)

instead of (0 0), (0 99), (199, 99), (199, 0)

Why is opencv findcontours behaving like that, with an offset of 1?

Upvotes: 3

Views: 8975

Answers (1)

Miki
Miki

Reputation: 41765

Until OpenCV 3.1 findContours has this wierd behaviour on borders, also stated in the documentation:

Source image is modified by this function. Also, the function does not take into account 1-pixel border of the image (it's filled with 0's and used for neighbor analysis in the algorithm), therefore the contours touching the image border will be clipped.

This has been corrected in OpenCV 3.2, which also doesn't modify the source image:

Since opencv 3.2 source image is not modified by this function.


As a workaround for previous releases, you can use copyMakeBorder to create a black (0) border of 1 pixel, and use findContours with an offset of (-1,-1):

border = cv2.copyMakeBorder(mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0 )
_, contours, hierarchy = cv2.findContours(border, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE, offset=(-1, -1))

Upvotes: 5

Related Questions