Contornear una máscara binaria con OpenCV / Python

Con Python y OpenCV estoy detectando los contornos de una máscara binaria:

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)) 

Resultando en un comportamiento esperado:

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

Contorno de apertura simple.

Sin embargo, parece que no puedo entender el resultado de una máscara activada completa:

 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] 

Lo que produce:

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

en lugar de (0 0), (0 99), (199, 99), (199, 0)

¿Por qué opencv findcontours se comporta de esa manera, con un desplazamiento de 1?

Hasta que findContours 3.1 findContours tiene este extraño comportamiento en las fronteras, también se indica en la documentación :

La imagen de origen es modificada por esta función. Además, la función no tiene en cuenta el borde de 1 píxel de la imagen (se rellena con 0 y se utiliza para el análisis de vecinos en el algoritmo), por lo tanto, los contornos que tocan el borde de la imagen se recortarán.

Esto se ha corregido en OpenCV 3.2, que tampoco modifica la imagen de origen :

Dado que la imagen fuente opencv 3.2 no es modificada por esta función.


Como solución para versiones anteriores, puede usar copyMakeBorder para crear un borde negro (0) de 1 píxel, y usar findContours con un desplazamiento de (-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))