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()
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?
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))
来源:https://stackoverflow.com/questions/41592039/contouring-a-binary-mask-with-opencv-python