Search code examples
pythonimage-processingpixelopencv3.0

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.


Solution

  • 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.