Efficient contours of a non-binary image

Viewed 331

I have this image: enter image description here

I want to get the contours of every unique (none-zero) value, so basically this output:

[array([[1, 1],
        [3, 2],
        [3, 1]], dtype=int32),
 array([[4, 2],
        [4, 3],
        [5, 3],
        [5, 2]], dtype=int32),
 array([[2, 4],
        [3, 4]], dtype=int32)]

I can achieve this by converting the image into binary mask and then using cv2's findContours function, which get the output which I want.

import numpy as np
import cv2

img = np.array([
    [0,0,0,0,0,0,0],
    [0,1,1,1,0,0,0],
    [0,0,1,1,2,2,0],
    [0,0,0,0,2,2,0],
    [0,0,3,3,0,0,0]
])

contour_list = []

for level in [l for l in np.unique(img) if l != 0]:
    mask = (img == level).astype(np.uint8)
    contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
    contours[0] = cv2.approxPolyDP(contours[0], 0.5, True)
    contours_xy = contours[0][:, 0]
    contour_list.append(contours_xy)


contour_list

However, this approach is slow for large images with many unique levels. Is there a way to improve the speed of this function (without multiprocessing)? I feel like I'm overlooking some functionality.

1 Answers

Resize your original image , do the process, than scale the resulted contours to fit your original image.

import cv2
import numpy as np

img = cv2.imread('images/multi-level-color.png',0)
img_r = cv2.resize(img,(int(img.shape[1]*0.1),int(img.shape[0]*0.1)))

cv2.imshow('img_r',img_r)
cv2.waitKey(0)

contour_list = []
contour_list_r = []

for level in [l for l in np.unique(img) if l != 0]:
    #mask = (img == level).astype(np.uint8)

    contours = cv2.findContours(img_r, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
    contours[0] = cv2.approxPolyDP(contours[0], 0.5, True)
    contours_xy = contours[0][:, 0]
    contours_xy_r = contours[0][:, 0]*10

    contour_list.append(contours_xy)
    contour_list_r.append(contours_xy_r)


print("resized 0.1",contour_list[0])

print("resized 10",contour_list_r[0])

original image: enter image description here

resized image to process: enter image description here

Output:

resized 0.1

[[11 10]
 [11 20]
 [20 20]
 [21 21]
 [21 31]
 [42 31]
 [43 32]
 [43 41]
 [42 42]
 [21 42]
 [21 52]
 [42 52]
 [42 42]
 [43 41]
 [63 41]
 [63 21]
 [43 21]
 [42 20]
 [42 10]]

resized 10

[[110 100]
 [110 200]
 [200 200]
 [210 210]
 [210 310]
 [420 310]
 [430 320]
 [430 410]
 [420 420]
 [210 420]
 [210 520]
 [420 520]
 [420 420]
 [430 410]
 [630 410]
 [630 210]
 [430 210]
 [420 200]
 [420 100]]
Related