2

I have a NumPy array contours that I got from cv2.findContours and flattened using contours = np.concatenate(contours, axis = 0). It stores coordinates of contours of objects from an image. However, I want to delete coordinates whose either X or Y is lower than, let's say, 100, or bigger than 1000. I first tried using contours = np.delete(contours, 0) and contours = np.delete(contours[0], 0) to just delete any item, but I kept getting this error: IndexError: invalid index to scalar variable.

How to delete such pairs of values?

print(type(contours))
→ <class 'numpy.ndarray'>
print(contours[0])
→ [[2834 4562]]
print(type(contours[0]))
→ <class 'numpy.ndarray'>
print(contours[0][0])
→ [2834 4562]
print(type(contours[0][0]))
<class 'numpy.ndarray'>

Also, I don't want to concatenate/flatten the list any further, because it's exactly in the form I need it to send to cv2.convexHull(contours).

Here's a minimal working sample of my code:

import cv2          # library for processing images
import numpy as np  # numerical calculcations for Python

img = cv2.imread("img.png")
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_, img_thr = cv2.threshold(img_gray,0,255,cv2.THRESH_OTSU)
img_rev = cv2.bitwise_not(img_thr)
img_cnt, contours, hierarchy = cv2.findContours(img_rev, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = np.concatenate(contours, axis = 0)

hull = cv2.convexHull(contours)
rect = cv2.minAreaRect(np.int0(hull))
box = cv2.boxPoints(rect)
box = np.int0(box)

img_cnt = cv2.drawContours(img, contours, -1, (0,255,0), 3)
img_cnt = cv2.drawContours(img, [box], -1, (0,0,255), 5)

cv2.imwrite("img_out.png", img_cnt)

Here is a sample input image, here is my output image. I want to ignore outlying "noise" for text selection. Assume I cannot use further noise reduction.

1

1 Answer 1

2

It seems that contours.shape is (N,1,2) . in this case,

contours[((contours>100)&(contours<1000)).all(axis=(1,2))]

will work.

by example:

In [106]: contours=randint(0,1100,(10,1,2))
[[[ 803  772]]
 [[ 210  490]]
 [[ 433   76]]
 [[ 347   88]]
 [[ 763  747]]
 [[ 678  200]]
 [[ 816  444]]
 [[ 528  817]]
 [[ 140  440]]
 [[1019  654]]]

In [108]: valid = ((contours>100)&(contours<1000)).all(axis=(1,2))
Out[108]: array([ True,  True, False, False,  True,  True,  
                  True,  True,  True, False], dtype=bool)

In [111]: contours[valid]
Out[111]: 
array([[[803, 772]],
       [[210, 490]],
       [[763, 747]],
       [[678, 200]],
       [[816, 444]],
       [[528, 817]],
       [[140, 440]]])

If you want different clips on X and Y, then you can use

(contours>[xmin,ymin])&(contours<[xmax,ymax])

instead.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the solution and the explanation. Works like charm now :)
I'll allow myself a short follow-up question: what if I wanted to set different thresholds for each axis? As I understand, all() requires an iterable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.