MrVocabulary
MrVocabulary

Reputation: 655

Remove pairs of values from NumPy array

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.

Upvotes: 2

Views: 1209

Answers (1)

B. M.
B. M.

Reputation: 18638

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.

Upvotes: 2

Related Questions