Search code examples
pythonperformanceopencvvideo-capture

How to increase speed of video playback within Python using openCV


I am writing a program that draws a line on a video where the first pixels of the railing are encountered, my problem is that the video playback is sluggish.

enter image description here Screenshot for reference of what the video looks like. During the video the camera is moved closer but because of the slow speed, I have to wait a few minutes to see the change, but when filming took place, it was moved every few seconds.

I assume the issue is the fact that the for loops are operating on every single frame of the video but I am not sure.

What solution could I implement to speed up my program?

import cv2

cap = cv2.VideoCapture('video.mp4')

while(cap.isOpened()):

    ret, frame = cap.read()
    canny = cv2.Canny(frame, 85, 255)
    height, width = canny.shape

    first_black_array = []

    for x in range(width):
        first_black_pixel_found = 0
        for y in range(height):
            if first_black_pixel_found == 0:
                if canny[y,x] == 255:
                    first_black_array.append(height - y)
                    first_black_pixel_found = 1
                    cv2.line(frame,(x,y),(x,y),(0,255,0),1)

    cv2.imshow('frame',frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Thanks!


Solution

  • This is the problem...

    for x in range(width):
        for y in range(height):
             if canny[y,x] == 255:
    

    Numpy.argmax is the solution...

    for x in range(width-1):
        # Slice the relevant column from the image
        # The image 'column' is a tall skinny image, only 1px thick
        column = np.array(canny[:,x:x+1])
        # Use numpy to find the first non-zero value
        railPoint = np.argmax(column)
    

    Full Code:

    import cv2, numpy as np, time
    # Get start time
    start = time.time()
    # Read in the image
    img = cv2.imread('/home/stephen/Desktop/rail.jpg')[40:,10:-10]
    # Canny filter
    canny = cv2.Canny(img, 85, 255)
    # Get height and width
    height, width = canny.shape
    # Create list to store rail points
    railPoints = []
    # Iterate though each column in the image
    for position in range(width-1):
        # Slice the relevant column from the image
        # The image 'column' is a tall skinny image, only 1px thick
        column = np.array(canny[:,position:position+1])
        # Use numpy to find the first non-zero value
        railPoint = np.argmax(column)
        # Add the railPoint to the list of rail points
        railPoints.append(railPoint)
        # Draw a circle on the image
        cv2.circle(img, (position, railPoint), 1, (123,234,123), 2)
    cv2.imshow('img', img)                      
    k = cv2.waitKey(1)
    cv2.destroyAllWindows()
    print(time.time() - start)
    

    My solution using Numpy took 6ms and your solution took 266ms. rail output