python-3.xfor-loopnested-loopsvideo-capture

an efficient for loop for this program


this is one frame of several thousandsI'm writing a program that will analyze a video frame-by-frame. The program is supposed to calculate the height of an object that is varying through time. I used the fact that the top of the object is a white color, so I would print out the position of a white pixel to find the position of the top and, from there, measure the height of the object. I was able to write such a program, but the for loop that iterates every frame (about 10,000 frames per video) is lagging behind the whole program. It would take almost a day to come up with the height of the object in each frame. My question is, is there a better and more efficient way to approach this problem?

capture = cv2.VideoCapture("/Users/beadpile/Desktop/aman/bpile.mp4")   
_, frame = capture.read()
MovieBase = MovieName
angles=[]
y_base = 247
x_center = 569
x_edgeL = 51
x_edgeR = 1087
for i in range(0,1000):
    capture.set(cv2.CAP_PROP_POS_FRAMES, i)
    _, frame = capture.read()
    white = [255,255,255]
    # Get X and Y coordinates of all white color pixels
    X,Y = np.where(np.all(frame==white,axis=2))
    XX=list(X)
    YY=list(Y)
    xpoints = np.array(X)
    ypoints = np.array(Y)
    test_list=list(zip(xpoints,ypoints))
    #get x,y coordinate of the white pixels found at the top     of the object
    subcoords = [(x, y) for x, y in test_list if y==min(YY)]
    s=[]
    for j in subcoords:
    s.append(j[0])
    #find average value of the x coordinate of the values     coordinates from subcoordds
    xax=sum(s)/len(subcoords)
    slope=(y_base-min(YY))/(xax-x_edgeL)
#gets angle that extends from one point to the top of the object
    aangle=math.degrees(math.atan(slope))
    angles.append(aangle)
print(angles)

Solution

  • You assume that the image only has white pixels in the pile, but you have other pixels that are white meanwhile outside of the pile. You have to clean those pixels first.

    bad pixels

    After you clean those pixels

    bad pixels cleaned

    Your code is slow, because you abandon numpy, by using lists.
    This code shows the height and width of the pile, from the cleaned image using only numpy, which is fast, because takes advantage of SIMD in the CPU.

    import numpy as np
    import requests
    import cv2
    
    # Download the image from stackoverflow
    #url = "https://i.sstatic.net/y6mbR.jpg" # Original image
    url = "https://i.sstatic.net/xLG0M.jpg" # Cleaned image
    response = requests.get(url, stream=True)
    img_array = np.array(bytearray(response.raw.read()), dtype=np.uint8)
    img = cv2.imdecode(img_array, -1)
    
    # Find white pixels
    white = np.array([255, 255, 255], dtype=np.uint8)
    white_pixels = np.all(img == white, axis=-1)
    white_columns = white_pixels.max(axis=0)
    white_rows = white_pixels.max(axis=1)
    
    white_rows_indexes = np.where(white_rows == True)[0]
    print(f"height of pile = {white_rows_indexes[-1]-white_rows_indexes[0]+1}")
    
    white_column_indexes = np.where(white_columns == True)[0]
    print(f"Width of pile = {white_column_indexes[-1]-white_column_indexes[0]+1}")
    
    cv2.imshow('Image', white_pixels.astype(np.uint8)*255)
    cv2.waitKey(0)
    cv2.destroyAllWindows()