2

I have a camera that is sending the image data to my computer. From there my python script puts the 8bit color info (black and white; ranging from 0 - black - to 255 - white) into a numpy array. The array is 2D, first dimension up to 384 and second dimensions up to 288 Displaying this with a openCV window works great and the live video is more than 24fps.

My aim now is to manipulate the image, so that the live video displays any color value below 200 as 0 (completely black) and any color value above 200 as 255 (completely white). However, my code right now only gives me about 3fps.

My code is doing the following:

  • save the image that comes from the camera in a numpy array
  • open the first for loop to iterate through the x values
  • in that first for loop iterate through the second for loop with the y values
  • check each color value for every pixel and check if it is above 200 or not
  • depending on the if clause make the color value 0 or 255
  • display the new image

This is the decisive part in the code:

processedImage = frame
i = 0
for i in range(0, displayedWidth):
    ii = 0
    for ii in range(0, displayedHeight):
        if frame[ii, i] > 200:
            processedImage[ii, i] = 255
        else: 
            processedImage[ii, i] = 0
cv2.imshow("LiveVideo", processedImage)

I read here that for loops are faster than while loops, but it didn't improve the code's speed significantly, which is why I assume the rewriting of the processedImage takes too long.

Is there a way to make the whole process faster?

Thanks for any answers!

Divakar
  • 218,885
  • 19
  • 262
  • 358
X_841
  • 191
  • 1
  • 14

4 Answers4

4

Try this:

frame[frame > 200] = 255
frame[frame <= 200] = 0
cv2.imshow("LiveVideo", frame)
Seb
  • 4,422
  • 14
  • 23
  • 1
    Thank you very much! This made it go back to more than 24fps and the video is running smooth again. Will mark it as correct asap. – X_841 Nov 27 '19 at 14:32
1

np.where() can be used too:

processed_frame = np.where(frame >= 200, 255, 0)

e.g.

>>> im = (np.random.rand(5, 5) * 255).round()
>>> im
array([[ 45.,  92.,  74., 207., 211.],
       [206., 184.,  31., 117., 119.],
       [ 88.,  82., 203.,  34.,   5.],
       [157.,  99., 154., 251.,  54.],
       [160., 177.,  14., 206., 234.]])
>>> np.where(im >= 200, 255, 0)
array([[  0,   0,   0, 255, 255],
       [255,   0,   0,   0,   0],
       [  0,   0, 255,   0,   0],
       [  0,   0,   0, 255,   0],
       [  0,   0,   0, 255, 255]])
>>>
AKX
  • 152,115
  • 15
  • 115
  • 172
1

Staying with NumPy, it seems fastest would be -

(frame>200)*np.uint8(255)

Since, you are already using OpenCV, a faster way would be with cv2.threshold -

cv2.threshold(frame,200,255,cv2.THRESH_BINARY)[1]

Sample run to verify results and get timings on 1024X1024 image -

In [2]: np.random.seed(0)
   ...: frame = np.random.randint(0,256,(1024,1024)).astype(np.uint8)

In [3]: %timeit (frame>200)*np.uint8(255)
253 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [4]: %timeit cv2.threshold(frame,200,255,cv2.THRESH_BINARY)[1]
58.2 µs ± 437 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)    

In [7]: out1 = (frame>200)*np.uint8(255)

In [8]: out2 = cv2.threshold(frame,200,255,cv2.THRESH_BINARY)[1]

In [9]: np.allclose(out1, out2)
Out[9]: True

Timings with other solutions on same data -

# @AKX's soln
In [10]: %timeit np.where(frame >= 200, 255, 0)
3.73 ms ± 22.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# @Seb's soln
In [11]: %%timeit
    ...: frame[frame > 200] = 255
    ...: frame[frame <= 200] = 0
10.2 ms ± 15.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

OpenCV's version with cv2.threshold seems the fastest by a big margin among others.

AKX
  • 152,115
  • 15
  • 115
  • 172
Divakar
  • 218,885
  • 19
  • 262
  • 358
0

Using Seb's solution is a big improvement but it seems that using numpy.where is even faster:

%%timeit
frame1 = frame.copy()
frame1[frame1 > 200] = 255
frame1[frame1 <= 200] = 0

974 µs ± 19.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

vs.

%%timeit
frame2 = frame.copy()
frame2 = np.where(frame2 > 200, 255, 0)

385 µs ± 7.16 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> np.alltrue(frame1 == frame2)
True
foglerit
  • 7,792
  • 8
  • 44
  • 64
  • When I try your or @AKX solution I get the error `(-215: Assertion failed)` when trying to display the image with `cv2.imshow(...)`. Would I be able to save the points where the color value is above 200 with this as well, or would I need a second `np.where()` statement? – X_841 Nov 27 '19 at 14:55
  • Yes, the result should be the same as Seb's approach. For reference, I tested it with this array: `frame = np.floor(256 * np.random.rand(384, 288)).astype(int)` – foglerit Nov 27 '19 at 18:46