Search code examples
pythonopencvvideo-captureroivideo-editing

Cropping a video to a specific range of frames and region of interest in Python


I have a video and I am only interested in a small section of the video (both in terms of a certain amount of frames and a small window of the video). I have been slowing chewing my way through problems, with the help of people in this thread and elsewhere. I have been pulling my hair and everyone else's hair out trying to get this to work for a few days, and I think I figured out some parts of what was wrong. If you will, please offer help if you have any.

I'm unsure also if I need to be using waitKey() or not.

I am currently trying to figure out how to make sure that the new frames are the right size, which I assume would be the size of the cropped portion of the original video. This is how I have thought of doing this so far. Do you have any thoughts or potential solutions?


# dimension for cropping out each fish
box_dim = 220 

# set parameters for locating each ZeChat unit as ROIs for cropping
y_orig = 25
x_orig = 20
wall = 20
window = 15
ro = 1
col = 1

# locate the roi so that each cell can be selected by just changing the row and column number:
y_roi = y_orig+(box_dim*(ro))+(window*int(math.ceil(ro/2)))+(wall*(int(math.ceil((ro+1)/2))-1))
x_roi = x_orig+(box_dim*(col))+(wall*col)

roi_size = (y_roi+box_dim, x_roi+box_dim)

# Initialize video writer object
out = cv2.VideoWriter("BN4 A4-F4, H4 videos/BN4 A4-F4, H4 test.avi", cv2.VideoWriter_fourcc(*'XVID'), 50, roi_size) 

Anyways, this is the whole code:

# dimension for cropping out each fish
box_dim = 220 
        
# set parameters for locating each ZeChat unit as ROIs for cropping
y_orig = 25
x_orig = 20
wall = 20
window = 15
ro = 1
col = 1

# locate the roi so that each cell can be selected by just changing the row and column number:
y_roi = y_orig+(box_dim*(ro))+(window*int(math.ceil(ro/2)))+(wall*(int(math.ceil((ro+1)/2))-1))
x_roi = x_orig+(box_dim*(col))+(wall*col)

#Starting and ending frames, length of video in frames
starting_frame = 32
ending_frame = []
length = 56

def crops_frames_and_borders():
    cap = cv2.VideoCapture("BN4 A4-F4, H4.avi") 
    cap.set(cv2.CAP_PROP_POS_FRAMES, starting_frame)
    
    if (cap.isOpened() == False):
        print("Error opening the video file")
    else:
        print("Opening video BN4 A4-F4, H4.avi")
        # Get frame rate information
        fps = int(cap.get(5))
        print("Frame Rate : ",fps,"frames per second") 
        # Get frame count
        frame_count = cap.get(7)
        print("Frame count : ", frame_count)
            
    # Obtain frame size information using get() method
    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    frame_size = (frame_width,frame_height)
    
    # Initialize video writer object
    out = cv2.VideoWriter("BN4 A4-F4, H4 videos/BN4 A4-F4, H4 test.avi", cv2.VideoWriter_fourcc(*'XVID'), 50, frame_size)         
        
#while loop that continues until the frames counted equal the desired length of the clip
    frames_length = 0
    frames_progression = 0
    
    while (frames_progression <= length):
        frames_length = frames_length + frames_progression
        frames_progression = frames_progression + 1
        while(True):
            #reads through each frome within the loop, and then writes that frame into the new video isolate the roi:
            ret, frame = cap.read()
            if ret == True:
                #determine where the frames that will be cropped for the ROI
                roi = frame[y_roi:y_roi+box_dim, x_roi:x_roi+box_dim]
                # Write the frame into the file output .avi that was read from the original video in cap.read()
                out.write(roi)    
            else:
                print("Cannot retrieve frames. Breaking.")
                break
            if (out.isOpened() == False):
                print("Error opening the video file")
        else:
            break
        print(frames_length)
    
    # With everything done, release the capture
    cap.release()
    out.release()
    cv2.destroyAllWindows()
    print("Finished writing new video")

    
crops_frames_and_borders()

As of right now, it is returning this (rate of change of the counter increases until it hits 56, which is the number of frames that I wanted the actual length to be. So there's a problem with the while loop:

Opening video BN4 A4-F4, H4.avi
Frame Rate :  50 frames per second
Frame count :  46728.0
Cannot retrieve frames. Breaking.
0
Cannot retrieve frames. Breaking.
1
Cannot retrieve frames. Breaking.
3
Cannot retrieve frames. Breaking.
6
Cannot retrieve frames. Breaking.
10
Cannot retrieve frames. Breaking.
15
Cannot retrieve frames. Breaking.
21
Cannot retrieve frames. Breaking.
28
Cannot retrieve frames. Breaking.
36
Cannot retrieve frames. Breaking.
45
Cannot retrieve frames. Breaking.
55
Cannot retrieve frames. Breaking.
66
Cannot retrieve frames. Breaking.
78
Cannot retrieve frames. Breaking.
91
Cannot retrieve frames. Breaking.
105
Cannot retrieve frames. Breaking.
120
Cannot retrieve frames. Breaking.
136
Cannot retrieve frames. Breaking.
153
Cannot retrieve frames. Breaking.
171
Cannot retrieve frames. Breaking.
190
Cannot retrieve frames. Breaking.
210
Cannot retrieve frames. Breaking.
231
Cannot retrieve frames. Breaking.
253
Cannot retrieve frames. Breaking.
276
Cannot retrieve frames. Breaking.
300
Cannot retrieve frames. Breaking.
325
Cannot retrieve frames. Breaking.
351
Cannot retrieve frames. Breaking.
378
Cannot retrieve frames. Breaking.
406
Cannot retrieve frames. Breaking.
435
Cannot retrieve frames. Breaking.
465
Cannot retrieve frames. Breaking.
496
Cannot retrieve frames. Breaking.
528
Cannot retrieve frames. Breaking.
561
Cannot retrieve frames. Breaking.
595
Cannot retrieve frames. Breaking.
630
Cannot retrieve frames. Breaking.
666
Cannot retrieve frames. Breaking.
703
Cannot retrieve frames. Breaking.
741
Cannot retrieve frames. Breaking.
780
Cannot retrieve frames. Breaking.
820
Cannot retrieve frames. Breaking.
861
Cannot retrieve frames. Breaking.
903
Cannot retrieve frames. Breaking.
946
Cannot retrieve frames. Breaking.
990
Cannot retrieve frames. Breaking.
1035
Cannot retrieve frames. Breaking.
1081
Cannot retrieve frames. Breaking.
1128
Cannot retrieve frames. Breaking.
1176
Cannot retrieve frames. Breaking.
1225
Cannot retrieve frames. Breaking.
1275
Cannot retrieve frames. Breaking.
1326
Cannot retrieve frames. Breaking.
1378
Cannot retrieve frames. Breaking.
1431
Cannot retrieve frames. Breaking.
1485
Cannot retrieve frames. Breaking.
1540
Cannot retrieve frames. Breaking.
1596
Finished writing new video

Solution

  • I got it to work! Next, I will be making it so my program will do the same thing to make about 750 more videos. Here was my solution:

    # Dimension for cropping out each fish
    box_dim = 220 
            
    # Set parameters for locating each ZeChat unit as ROIs for cropping
    y_orig = 25
    x_orig = 20
    wall = 20
    window = 15
    ro = 1
    col = 1
    
    # locate the roi so that each cell can be selected by just changing the row and column number:
    y_roi = y_orig+(box_dim*(ro))+(window*int(math.ceil(ro/2)))+(wall*(int(math.ceil((ro+1)/2))-1))
    x_roi = x_orig+(box_dim*(col))+(wall*col)
    
    # Starting and ending frames, length of video in frames
    starting_frame = 32
    ending_frame = []
    length = 56
    
    def crops_frames_and_borders():
        cap = cv2.VideoCapture("BN4 A4-F4, H4.avi") 
        cap.set(cv2.CAP_PROP_POS_FRAMES, starting_frame)
        
        if (cap.isOpened() == False):
            print("Error opening the video file")
        else:
            print("Opening video BN4 A4-F4, H4.avi")
            # Get frame rate information
            fps = int(cap.get(5))
            print("Frame Rate : ",fps,"frames per second") 
            # Get frame count
            frame_count = cap.get(7)
            print("Frame count : ", frame_count)
                
        # Obtain frame size information using get() method
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        frame_size = (frame_width,frame_height)
        
        roi_size = (220, 220)
        
        # Initialize video writer object
        out = cv2.VideoWriter("BN4 A4-F4, H4 videos/BN4 A4-F4, H4 test.avi", cv2.VideoWriter_fourcc(*'XVID'), 50, roi_size) 
        
        frames_progression = 0
        # For loop that iterates until the index matches the desired length of the clip
        for i in range(length): # reads through each frome within the loop, and then writes that frame into the new video isolate the roi
            ret, frame = cap.read() 
            if ret == True:
                # Locating the ROI within the frames that will be cropped
                roi = frame[y_roi:y_roi+box_dim, x_roi:x_roi+box_dim]
                # Write the frame into the file output .avi that was read from the original video in cap.read()
                out.write(roi)    
            else:
                print("Cannot retrieve frames. Breaking.") #If a frame cannot be retrieved, this error is thrown
            if (out.isOpened() == False):
                print("Error opening the video file") # If the out video cannot be opened, this error is thrown
            else:
                frames_progression = frames_progression + 1 # Shows how far the frame writting process got. Compare this to the desired frame length
        print(frames_progression)
        
        # Release the capture
        cap.release()
        out.release()
        cv2.destroyAllWindows()
        print("Finished writing new video")
    
        # These were just for me to verify that the right frames were written
        cap = cv2.VideoCapture("BN4 A4-F4, H4 videos/BN4 A4-F4, H4 test.avi")
        fps = cap.get(cv2.CAP_PROP_FPS)
        frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        duration = frame_count/fps
        print('fps = ' + str(fps))
        print('number of frames = ' + str(frame_count))
        print('duration (S) = ' + str(duration))
        cap.release()
        cv2.destroyAllWindows()
        
    crops_frames_and_borders()
    

    The problems were with the two while loops, which I ditched for a single for loop, and the size of the new video like Christoph suggested. I found that simplifying the size to something more simple was enough to let the video work. Thanks to those who were helpful while responding to me.