Why does the cropped video not get saved? - python

Here, I have a code that takes a video as input and let the user draw a ROI. Later the cropped video will be displayed. This code was originally taken from I would like to define a Region of Interest in a video and only process that area.
I have provided the code below.
Question: I would like to save the cropped video in .mp4 format. The video output shows only a 1kb file. Can you go through the code and suggest a solution?
NB: I went through answer provided at OpenCV video not getting saved. Still I have failed to figure out the error.
import numpy as np
import cv2
import matplotlib.pyplot as plt
ORIGINAL_WINDOW_TITLE = 'Original'
FIRST_FRAME_WINDOW_TITLE = 'First Frame'
DIFFERENCE_WINDOW_TITLE = 'Difference'
canvas = None
drawing = False # true if mouse is pressed
#Retrieve first frame
def initialize_camera(cap):
_, frame = cap.read()
return frame
# mouse callback function
def mouse_draw_rect(event,x,y,flags, params):
global drawing, canvas
if drawing:
canvas = params[0].copy()
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
params.append((x,y)) #Save first point
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
cv2.rectangle(canvas, params[1],(x,y),(0,255,0),2)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
params.append((x,y)) #Save second point
cv2.rectangle(canvas,params[1],params[2],(0,255,0),2)
def select_roi(frame):
global canvas
#here, it is the copy of the first frame.
canvas = frame.copy()
params = [frame]
ROI_SELECTION_WINDOW = 'Select ROI'
cv2.namedWindow(ROI_SELECTION_WINDOW)
cv2.setMouseCallback(ROI_SELECTION_WINDOW, mouse_draw_rect, params)
roi_selected = False
while True:
cv2.imshow(ROI_SELECTION_WINDOW, canvas)
key = cv2.waitKey(10)
#Press Enter to break the loop
if key == 13:
break;
cv2.destroyWindow(ROI_SELECTION_WINDOW)
roi_selected = (3 == len(params))
print(len(params))
if roi_selected:
p1 = params[1]
p2 = params[2]
if (p1[0] == p2[0]) and (p1[1] == p2[1]):
roi_selected = False
#Use whole frame if ROI has not been selected
if not roi_selected:
print('ROI Not Selected. Using Full Frame')
p1 = (0,0)
p2 = (frame.shape[1] - 1, frame.shape[0] -1)
return roi_selected, p1, p2
if __name__ == '__main__':
cap = cv2.VideoCapture(r'E:\cardiovascular\brad1low.mp4')
#Grab first frame
first_frame = initialize_camera(cap)
#Select ROI for processing. Hit Enter after drawing the rectangle to finalize selection
roi_selected, point1, point2 = select_roi(first_frame)
#Grab ROI of first frame
first_frame_roi = first_frame[point1[1]:point2[1], point1[0]:point2[0]]
print(f'first frame roi is {first_frame_roi}')
#An empty image of full size just for visualization of difference
difference_image_canvas = np.zeros_like(first_frame)
out = cv2.VideoWriter(r'E:\cardiovascular\filename2.mp4', cv2.VideoWriter_fourcc(*'MP4V'), int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), (int(first_frame_roi.shape[0]), (int(first_frame_roi.shape[1]))))
while cap.isOpened():
ret, frame = cap.read()
if ret:
#ROI of current frame
roi = frame[point1[1]:point2[1], point1[0]:point2[0]]
print(f'roi is {roi}')
cv2.imshow(DIFFERENCE_WINDOW_TITLE, roi)
out.write(roi)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()

Related

Opencv rotate camera mode

I want to rotate the camera view by 90 degree clockwise with an interrupt button. But the button's state goes back to it's default state when I click once and unpress the button. As a result, on clicking the button, the camera rotates for an instance and then goes back to the default mode in the while loop. How to solve this issue?
Here is my snippet:
import cv2
cap = cv2. VideoCapture(0)
while True:
ret, frame = cap.read()
cv2.imshow('null', frame)
if (button):
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
cv2.imshow('null', frame)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
Any help would be appreciated.
You should use extra variable - rotate = False - to keep this state
rotate = False
while True:
ret, frame = cap.read()
# without imshow()
if button_a.is_pressed():
rotate = True
if rotate:
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
# outside `if`
cv2.imshow('null', frame)
This way it changes rotate from False to True but never from True to False
EDIT:
If next press should rotate another 90 degrees (to 180) then it would need more complex code. It would check if is_pressed() in previous loop was False and in current loop is True - to run it only once when button is pressed long time.
Something like this.
I used normal space to test it.
import cv2
cap = cv2.VideoCapture(0)
rotate = 0
is_pressed = False
previous = False
current = False
while True:
ret, frame = cap.read()
# without imshow()
current = is_pressed
if (previous is False) and (current is True):
rotate = (rotate + 90) % 360
print(rotate)
previous = current
if rotate == 90:
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
elif rotate == 180:
frame = cv2.rotate(frame, cv2.ROTATE_180)
elif rotate == 270:
frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
# outside `if`
cv2.imshow('null', frame)
key = cv2.waitKey(40) & 0xff
if key == 27:
break
is_pressed = (key == 32)
cv2.destroyAllWindows()
cap.release()
import cv2
cap = cv2. VideoCapture(0)
button_is_pressed = false;
while True:
ret, frame = cap.read()
if (button):
button_is_pressed = not button_is_pressed
if(button_is_pressed)
frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
cv2.imshow('null', frame)
cap.release()
I would try something like this, when you press the button it changes the state of variable button_is_pressed, while still unchanged it keeps rotating image.

how can i control frames in opencv?

how can i control frames in opencv?
here you insert the url of a video in the internet/local and this streams it to you.but voice and video are not Coordinated.video is faster than voice :/
import cv2
import numpy as np
from ffpyplayer.player import MediaPlayer
def getVideoSource(source, width, height):
cap = cv2.VideoCapture(source)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
return cap
def main():
url=input('enter url: ')
sourcePath = url
camera = getVideoSource(sourcePath, 720, 480)
player = MediaPlayer(sourcePath)
while True:
ret, frame = camera.read()
audio_frame, val = player.get_frame()
if (ret == 0):
print("End of video")
break
frame = cv2.resize(frame, (720, 480))
cv2.imshow('Camera', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if val != 'eof' and audio_frame is not None:
frame, t = audio_frame
print("Frame:" + str(frame) + " T: " + str(t))
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
A slight modification of the answer by #furas:
player = MediaPlayer(video_path)
while True:
frame, val = player.get_frame()
if val == 'eof':
break # this is the difference
if frame is not None:
image, pts = frame
w, h = image.get_size()
# convert to array width, height
img = np.asarray(image.to_bytearray()[0]).reshape(h,w,3)
# convert RGB to BGR because `cv2` need it to display it
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
time.sleep(val)
cv2.imshow('video', img)
if cv2.waitKey(1) & 0xff == ord('q'):
break
cv2.destroyAllWindows()
player.close_player()
The difference is the explicit break once EOF is reached, which causes the program to terminate. To me, that's expected behavior, so I wanted to post this code in case someone wants that behavior as well.
It seems you can't control when to play audio because it uses SDL to play it in separated thread but get_frame() gives tuple (frame, val) and frame is (image, time_when_to_display_image) and you should use this time_when_to_display_image to control when to display image.
And all this code doesn't need cv2.VideoCapture() to get frame.
I use cv2 only to display it but you can use any GUI to create window to display it.
I use current_time to get next frame without using time.sleep() because video wasn't smooth.
BTW: You can use player.set_size(720, 480) to resize frame.
from ffpyplayer.player import MediaPlayer
import cv2
import numpy as np
import time
filename = 'video.mp4'
player = MediaPlayer(filename)
player.set_size(720, 480) # resize it
#player.set_size(400, 300)
start_time = time.time()
frame_time = start_time + 0
while True:
current_time = time.time()
# check if it is time to get next frame
if current_time >= frame_time:
# get next frame
frame, val = player.get_frame()
if val != 'eof' and frame is not None:
image, pts = frame
w, h = image.get_size()
# convert to array width, height
img = np.asarray(image.to_bytearray()[0]).reshape(h,w,3)
# convert RGB to BGR because `cv2` need it to display it
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
cv2.imshow('video', img)
frame_time = start_time + pts
if cv2.waitKey(1) & 0xff == ord('q'):
break
cv2.destroyAllWindows()
player.close_player()
EDIT: I found that get_frame() gives (frame, val) and this val can be used in time.sleep(val). And probably it should sleep before displaying frame, not after displaying it.
from ffpyplayer.player import MediaPlayer
import cv2
import numpy as np
import time
filename = 'video.mp4'
player = MediaPlayer(filename)
player.set_size(720, 480)
#player.set_size(400, 300)
while True:
frame, val = player.get_frame()
if val != 'eof' and frame is not None:
image, pts = frame
w, h = image.get_size()
# convert to array width, height
img = np.asarray(image.to_bytearray()[0]).reshape(h,w,3)
# convert RGB to BGR because `cv2` need it to display it
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
time.sleep(val)
cv2.imshow('video', img)
if cv2.waitKey(1) & 0xff == ord('q'):
break
cv2.destroyAllWindows()
player.close_player()
EDIT: Code using tkinter to display it.
from ffpyplayer.player import MediaPlayer
import tkinter as tk
from PIL import Image, ImageTk
import time
# -- functions ---
def update_frame():
global photo # solution for BUG in PhotoImage
frame, val = player.get_frame()
if val != 'eof' and frame is not None:
image, pts = frame
w, h = image.get_size()
data = image.to_bytearray()[0]
img = Image.frombytes("RGB", (w,h), bytes(data))
photo = ImageTk.PhotoImage(img)
time.sleep(val)
label['image'] = photo
root.after(1, update_frame) # again after `1ms` without blocking `mainloop()`
# --- main ---
filename = 'video.mp4'
player = MediaPlayer(filename)
player.set_size(720, 480)
#player.set_size(400, 300)
root = tk.Tk()
label = tk.Label(root)
label.pack()
root.bind('q', lambda event:root.destroy())
update_frame()
root.mainloop()
player.close_player()

Python, drawing a polygon over webcam video using mouse clicks to detect points

I'm using Python3 and OpenCV (4.1.0) to realize a script that:
displays the contents of the webcam;
records the coordinates of mouse clicks over video;
after pressing a certain button ('p' in my example), draws a polyline between the points identified by previous mouse clicks;
So far, I'm trying:
import numpy as np
import cv2
def main():
cap = cv2.VideoCapture("files/long_video.mp4") # Open video file
points = []
while (cap.isOpened()):
ret, frame = cap.read() # read a frame
try:
cv2.imshow('Frame', frame)
except:
print('EOF')
break
cv2.setMouseCallback('Frame', left_click_detect, points)
# Abort and exit with 'Q'
key = cv2.waitKey(25)
if (key == ord('q')):
break
elif (key== ord('p')): # HERE, IT SHOULD DRAW POLYLINE OVER VIDEO!!!
pts_array = np.array([[x, y] for (x, y) in points], np.int0)
frame = cv2.polylines(frame, np.int32(np.array(points)), False, (255, 0, 0), thickness=5)
points = []
cv2.imshow('Frame', frame)
cap.release() # release video file
cv2.destroyAllWindows() # close all openCV windows
def left_click(event, x, y, flags, points):
if (event == cv2.EVENT_LBUTTONDOWN):
print(f"\tClick on {x}, {y}")
points.append([x,y])
It kinda works, but after pressing 'p' it doesn't draw the polyline over the video.
Any suggestions?
There are 2 problems with your code:
cv2.polylines() accepts a list of arrays. so here:
frame = cv2.polylines(frame, np.int32(np.array(points)), False, (255, 0, 0), thickness=5)
Replace np.int32(np.array(points)) with [np.int32(points)] to fix the exception. (you also don't need to use np.array() here)
After you draw the polygon on the frame, you call cv2.show(), but almost immediately after, you show the next frame without the polygon on it, so you don't have time to see the polygon. to fix it you need to draw the polygon again for each frame. and to do that, you need to save it until you press p again (to show another polygon).
This will work:
import numpy as np
import cv2
def main():
cap = cv2.VideoCapture("files/long_video.mp4") # Open video file
polygon = []
points = []
while (cap.isOpened()):
ret, frame = cap.read() # read a frame
if not ret:
print('EOF')
break
frame = cv2.polylines(frame, polygon, False, (255, 0, 0), thickness=5)
cv2.imshow('Frame', frame)
# Abort and exit with 'Q'
key = cv2.waitKey(25)
if (key == ord('q')):
break
elif (key== ord('p')):
polygon = [np.int32(points)]
points = []
cv2.setMouseCallback('Frame', left_click_detect, points)
cap.release() # release video file
cv2.destroyAllWindows() # close all openCV windows
def left_click_detect(event, x, y, flags, points):
if (event == cv2.EVENT_LBUTTONDOWN):
print(f"\tClick on {x}, {y}")
points.append([x,y])
print(points)

Opencv python crashes after certain time

In my code after certain time (depend of the PC or OS) crashes. The error displayed is
libpng warning: Image width is zero in IHDR
libpng warning: Image height is zero in IHDR
libpng error: Invalid IHDR data
My code draw ROI on a video from webcam and take a snapshot. The ROI is deleted after 5-10 minutes (I'm not sure why, this is the error) and the picture saved not have dimensions then the error above is displayed but I'm not find the error. The instructions are, press "c" to stop the video and draw a ROI, press again "c" to take the picture, press "r" to resume the video, and repeat if you need another pictures.
I tested the code in Windows 10 and raspbian on a raspberry and the time is not the same but on both the program crashes. This is my code:
import cv2
# python PSI_Camera_test.py
class staticROI(object):
def __init__(self):
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
# Bounding box reference points and boolean if we are extracting coordinates
self.image_coordinates = []
self.extract = False
self.selected_ROI = False
self.img_counter = 0
self.update()
def update(self):
while True:
if self.capture.isOpened():
# Read frame
(self.status, self.frame) = self.capture.read()
cv2.imshow('image', self.frame)
key = cv2.waitKey(2)
# Crop image
if key == ord('c'):
self.clone = self.frame.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.extract_coordinates)
while True:
key = cv2.waitKey(2)
cv2.imshow('image', self.clone)
# Crop and display cropped image
if key == ord('c'):
self.crop_ROI()
img_name = "Images\opencv_frame_{}.png".format(
self.img_counter)
cv2.imwrite(img_name, self.cropped_image)
print("{} written!".format(img_name))
self.img_counter += 1
# Resume video
if key == ord('r'):
break
# Close program with keyboard 'esp'
if key % 256 == 27:
cv2.destroyAllWindows()
exit(1)
else:
pass
def extract_coordinates(self, event, x, y, flags, parameters):
# Record starting (x,y) coordinates on left mouse button click
if event == cv2.EVENT_LBUTTONDOWN:
self.image_coordinates = [(x, y)]
self.extract = True
# Record ending (x,y) coordintes on left mouse bottom release
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x, y))
self.extract = False
self.selected_ROI = True
# Draw rectangle around ROI
cv2.rectangle(
self.clone, self.image_coordinates[0], self.image_coordinates[1], (0, 255, 0), 2)
# Clear drawing boxes on right mouse button click
elif event == cv2.EVENT_RBUTTONDOWN:
self.clone = self.frame.copy()
self.selected_ROI = False
def crop_ROI(self):
if self.selected_ROI:
self.cropped_image = self.frame.copy()
x1 = self.image_coordinates[0][0]
y1 = self.image_coordinates[0][1]
x2 = self.image_coordinates[1][0]
y2 = self.image_coordinates[1][1]
self.cropped_image = self.cropped_image[y1:y2, x1:x2]
print('Cropped image: {} {}'.format(
self.image_coordinates[0], self.image_coordinates[1]))
else:
print('Select ROI to crop before cropping')
# python PSI_Camera_test.py
if __name__ == '__main__':
static_ROI = staticROI()
Thanks in advance!

How can I zoom my webcam in Open CV Python?

I want my webcam to be zoomed in open cv python and I don't know how. Can anyone help me with my problem?
import cv2
video = cv2.VideoCapture(0)
while True:
check, frame = video.read()
cv2.imshow('Video', frame)
key = cv2.waitKey(1)
if key == 27:
break
video.release()
cv2.destroyAllWindows
You can use this solution. It makes the job -> croping + zoom + array up and array down.
import cv2
def show_webcam(mirror=False):
scale=10
cam = cv2.VideoCapture(0)
while True:
ret_val, image = cam.read()
if mirror:
image = cv2.flip(image, 1)
#get the webcam size
height, width, channels = image.shape
#prepare the crop
centerX,centerY=int(height/2),int(width/2)
radiusX,radiusY= int(scale*height/100),int(scale*width/100)
minX,maxX=centerX-radiusX,centerX+radiusX
minY,maxY=centerY-radiusY,centerY+radiusY
cropped = image[minX:maxX, minY:maxY]
resized_cropped = cv2.resize(cropped, (width, height))
cv2.imshow('my webcam', resized_cropped)
if cv2.waitKey(1) == 27:
break # esc to quit
#add + or - 5 % to zoom
if cv2.waitKey(1) == 0:
scale += 5 # +5
if cv2.waitKey(1) == 1:
scale = 5 # +5
cv2.destroyAllWindows()
def main():
show_webcam(mirror=True)
if __name__ == '__main__':
main()
Zooming is simply increasing image size . Just increase image size by converting frame to image using PIL and then resize function.
Sample code
import tkinter
import cv2
from PIL import Image,ImageTk
root = tkinter.Tk()
root.geometry("1000x500+200+0")
w = 1000
h = 630
capture = tkinter.Canvas(root, bd=2, bg="blue", height=h, width=w)
capture.grid(column = 0, row = 0)
video = None
frame = None
img=None
show=None
begin = False
def start_capture(event):
global begin,frame,img,root,show,capture,video
video = cv2.VideoCapture(0)
begin = True
while begin:
check, frame = video.read()
img = Image.fromarray(frame)
w,h = img.size
img = img.resize((w*2,h*2))
show = ImageTk.PhotoImage(img)
capture.create_image(0,0,anchor=tkinter.NW,image=show)
root.update()
def stop_capture(event):
global video,begin
begin = False
video.release()
start = tkinter.Button(root, text='Start')
start.grid(column = 0, row = 2)
start.bind('<Button-1>', start_capture)
stop = tkinter.Button(root, text='Stop')
stop.grid(column = 1, row = 2)
stop.bind('<Button-1>', stop_capture)
root.mainloop()
I am not sure if this is useful now to add this point. (Hope this helps for all the noobs in opencv at least)
I suffered a lot at this: cropped = image[minX:maxX, minY:maxY] Crop was somehow out of the interested area.
After researching a lot, I found out that the problem was with the syntax.
Actually it should have been : cropped = image[minY:maxY , minX:maxX]
because openCV crop syntax should be like this?..
Anyways, thanks to Amara BOUDIB & JDS for the sample codes!

Categories