Adithya Raj
Adithya Raj

Reputation: 95

It is taking too much time to process frames while doing pixel by pixel operation of video frames

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

def threshold_slow(image):
    h = image.shape[0]
    w = image.shape[1]
    for x in range(0, w):
        for y in range(0, h):
            k = np.array(image[x, y])
            print(k)

def video():
    while True:
        ret,frame = cap.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        threshold_slow(frame)
        cv2.imshow('frame',frame)
        key = cv2.waitKey(25)
        if key == ord('q'):
            break
if __name__ == '__main__':
    video()
cap.release()
cv2.destroyAllWindows()

I have done almost everything I can and still I am not able to reslove this. Anybody who has some idea please help with some piece of code. When I put print outside the for loop then it was working fine. But then I was not getting the value of each pixel in the image.

Upvotes: 1

Views: 477

Answers (2)

Mark Setchell
Mark Setchell

Reputation: 207465

You really, really should avoid for loops and "pixel-by-pixel" operations on images in Python. Try to use OpenCV vectorised routines, like cv2.threshold(), or failing that, vectorised Numpy routines.

You mention in the comments that you want to do this:

h = im.shape[0] 
w = im.shape[1] 
for x in range(0, w): 
    for y in range(0, h): 
        if im[y,x]>140 and im[y,x]<160: 
            im[y,x]=255 

That takes 487 milliseconds on my machine. If you use Numpy, like this, it takes 56 microseconds. i.e. 9,000x faster.

im[ np.logical_and(im>140, im<160) ] = 255

That will make your code look more like this - untested:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

def video():
    while True:
        ret,frame = cap.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        frame[ np.logical_and(frame>140, frame<160) ] = 255
        cv2.imshow('frame',frame)
        key = cv2.waitKey(25)
        if key == ord('q'):
            break
if __name__ == '__main__':
    video()
cap.release()
cv2.destroyAllWindows()

You can also use OpenCV inRange() to select a range of shades of grey in a greyscale image, like this:

mask = cv2.inRange(im, 140, 160)

Then you could apply that to your image with:

frame[~mask] = 255

But I think that is slower.

Upvotes: 3

taniwha
taniwha

Reputation: 391

print is unavoidably slow (in comparison to the rest of the code). Assuming a 256x256 image, your code will print 65536 values. Depending on the format (I'm not familar with OpenCV, but lets assume 1 byte per pixel), that ranges from 2 to 4 bytes of output for every pixel (convert 8-bit unsigned byte to text + end of line), so 128kB-320kB, that your terminal then needs to scroll.

Your best bet is to limit the region of pixels you try to print: ie, specify a suitably small region using appropriate parameters to your range invocations.

Upvotes: 1

Related Questions