i tried to make a webcam video recording to a file using openCV python, i could not open the file with any of my video players. here is the code,
it works fine but I stop the recording and looking the file and it doesn't open. I guess there are some codec issues. I tried also (*'XVID') .avi format. but changed nothing.
here is the code
please help
from tkinter import *
from PIL import ImageTk, Image
import cv2
import threading
root = Tk()
root.geometry("750x500")
root.configure(bg="#0059aa")
#camera
camera_frame = LabelFrame(root, text=u"KAMERA STREAMING",
border=2,
width=398,
height=265)
camera_frame.place(x=183,y=33)
camera_label = Label(camera_frame,width=55,height=14)
camera_label.grid(row=0,column=0)
global capture
capture = cv2.VideoCapture(0)
# edit: close following two lines
# capture.set(3,250)
# capture.set(4,225)
global out
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('blabla.mp4', fourcc, 20.0, (640, 480))
global stopCam
stopCam = False
def show_frames():
global capture
# read the capture
ret, frame = capture.read()
# turned into image and display
cv2image = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
height, width, channels = cv2image.shape
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image = img)
camera_label.imgtk = imgtk
camera_label.configure(image=imgtk,width=width,height=height)
# record
global out
out.write(frame)
# quit
if (stopCam):
out.release()
capture.release()
cv2.destroyAllWindows()
return
camera_label.after(20,show_frames)
p1 = threading.Thread(target=show_frames)
buttonLabel = Label(camera_frame)
buttonLabel.grid(row=1,column=0)
connectButton = Button (buttonLabel, text=u"connect", command=p1.start, width=14)
connectButton.grid(row=0,column=0)
stopButton = Button(buttonLabel, text=u"stop", command= lambda: globals().update(stopCam=True) , width=14)
stopButton.grid(row=0,column=1)
root.mainloop()
edit (also solved way):
I looked at some code that worked properly. and I saw capture.set() as the difference. When I close the capture.set() lines, I had no problems with either streaming or recording. Now the main problem is that I have to show the video in a label with a certain size. Without set() the video size gets too big. how can i solve it now?
I am building an App which got several video filters. But the App is not working on Android cause => capture = cv.VideoCapture(0) doesnt get access to the android camera.
The Code below shows how i edit the Video footage of the App at the moment. On PC it works perfect. But after building it, it shows no
video footage on android
Buidlozer Spec got permission btw..
capture = cv.VideoCapture(0)
class BinaryCam(Image):
def on_kv_post(self, base_widget):
#self.capture = cv.VideoCapture(0)
# cv.namedWindow("CV2 Image")
Clock.schedule_interval(self.update, 1.0 / 33.0)
def update(self, dt):
# display image from cam in opencv window
ret, frame = capture.read()
if ret==True:
# cv.imshow("CV2 Image", frame)
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# convert it to texture
adaptive_thresh = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 3)
buf1 = cv.flip(adaptive_thresh, 0)
buf = buf1.tobytes()
texture1 = Texture.create(size=(adaptive_thresh.shape[1], adaptive_thresh.shape[0]),
colorfmt='luminance') # in grayscale gibts kein bgr
# if working on RASPBERRY PI, use colorfmt='rgba' here instead, but stick with "bgr" in blit_buffer.
texture1.blit_buffer(buf, colorfmt='luminance', bufferfmt='ubyte') # replacing texture
# display image from the texture
self.texture = texture1
Or maybe is there a way to get the Camera Frames from the Kivy Camera and edit them with OpenCV.
But i really would prefer this method i showed here with OpenCV.
Thank you
If I use OpenCV to play video into its own window using this sort of logic:
cap = cv2.VideoCapture('video.mp4',cv2.CAP_FFMPEG)
while True:
ret, frame = cap.read()
if(ret):
cv2.imshow('', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
then it works well and smoothly. But if I use what appears to be the recommended way of playing into my own tkinter window, using the window.after() technique like this snippet:
def update(self):
# Get a frame from the video source
ret, frame = cap.read()
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)
self.update_id = self.window.after(self.VIDEO_READ_DELAY, self.update)
it is slow and stutters badly. I've played with the update delay without any real success, so I'm guessing that the processing overhead of the image conversion is what's causing the problem.
Can imshow() be made to play into my tkinter canvas directly?
Need help finding why the error with cv2.imshow('frame',frame)
(Working in Jupyter Notebook)
This program is to collect many images so that I can train a model that has 10 gestures.
import cv2 #opencv
import os
import time
import uuid
# Directory to collect images for training
IMAGES_PATH='Tensorflow/workspace/images/collectedimages'
# Setting an array with all the labels
labels = ['gest-1', 'gest-2', 'gest-3','gest-4','gest-5','gest-6','gest-7','gest-8', 'gest-
9', 'gest-10']
number_imgs = 20
# loop throught each of the labels in the array
for label in labels:
#create a directory for each of the labels
os.mkdir ('Tensorflow/workspace/images/collectedimages/'+label)
# Initialize the webcam (openCV)
cap = cv2.VideoCapture(0)
# time.sleep(2)
print('Collecting images for {}'.format(label))
time.sleep(5)
#Loop through the number of images we want to collect
for imgnum in range(number_imgs):
ret,frame = cap.read()
# Naming of the pictures
# .format(str(uuid.uuid1())) makes sure that we dont duplicate names
imgname = os.path.join(IMAGES_PATH, label, label+'.'+'{}.jpg'.format(str(uuid.uuid1())))
cv2.imwrite(imgname, frame)
cv2.imshow('frame',frame)
time.sleep(2)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
1- Your path image is false : Image == None --> generate error
2- for show image in jupyter you need do this:
image = cv2.imread('example/image.png')
cv2.imshow("test", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Is there a way with Python (maybe with OpenCV or PIL) to continuously grab frames of all or a portion of the screen, at least at 15 fps or more? I've seen it done in other languages, so in theory it should be possible.
I do not need to save the image data to a file. I actually just want it to output an array containing the raw RGB data (like in a numpy array or something) since I'm going to just take it and send it to a large LED display (probably after re-sizing it).
With all of the above solutions, I was unable to get a usable frame rate until I modified my code in the following way:
import numpy as np
import cv2
from mss import mss
from PIL import Image
bounding_box = {'top': 100, 'left': 0, 'width': 400, 'height': 300}
sct = mss()
while True:
sct_img = sct.grab(bounding_box)
cv2.imshow('screen', np.array(sct_img))
if (cv2.waitKey(1) & 0xFF) == ord('q'):
cv2.destroyAllWindows()
break
With this solution, I easily get 20+ frames/second.
For reference, check this link: OpenCV/Numpy example with mss
There is an other solution with mss which provide much better frame rate. (Tested on a Macbook Pro with MacOS Sierra)
import numpy as np
import cv2
from mss import mss
from PIL import Image
mon = {'left': 160, 'top': 160, 'width': 200, 'height': 200}
with mss() as sct:
while True:
screenShot = sct.grab(mon)
img = Image.frombytes(
'RGB',
(screenShot.width, screenShot.height),
screenShot.rgb,
)
cv2.imshow('test', np.array(img))
if cv2.waitKey(33) & 0xFF in (
ord('q'),
27,
):
break
You will need to use ImageGrab from Pillow (PIL) Library and convert the capture to numpy array. When you have the array you can do what you please with it using opencv. I converted capture to gray color and used imshow() as a demonstration.
Here is a quick code to get you started:
from PIL import ImageGrab
import numpy as np
import cv2
img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height *starts top-left)
img_np = np.array(img) #this is the array obtained from conversion
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
cv2.imshow("test", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
you can plug an array there with the frequency you please to keep capturing frames. After that you just decode the frames. don't forget to add before the loop:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
vid = cv2.VideoWriter('output.avi', fourcc, 6, (640,480))
and inside the loop you can add:
vid.write(frame) #the edited frame or the original img_np as you please
UPDATE
the end result look something like this (If you want to achieve a stream of frames that is. Storing as video just a demonstration of using opencv on the screen captured):
from PIL import ImageGrab
import numpy as np
import cv2
while(True):
img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height)
img_np = np.array(img)
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
cv2.imshow("test", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
Hope that helps
based on this post and others posts, i made something like this .
Its taking a screenshot and writing into a video file without saving the img.
import cv2
import numpy as np
import os
import pyautogui
output = "video.avi"
img = pyautogui.screenshot()
img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
#get info from img
height, width, channels = img.shape
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output, fourcc, 20.0, (width, height))
while(True):
try:
img = pyautogui.screenshot()
image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
out.write(image)
StopIteration(0.5)
except KeyboardInterrupt:
break
out.release()
cv2.destroyAllWindows()
You can try this code as it is working for me. I've tested it on Linux
import numpy as np
import cv2
from mss import mss
from PIL import Image
sct = mss()
while 1:
w, h = 800, 640
monitor = {'top': 0, 'left': 0, 'width': w, 'height': h}
img = Image.frombytes('RGB', (w,h), sct.grab(monitor).rgb)
cv2.imshow('test', cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
Make sure that the following packages are installed:
Pillow, opencv-python, numpy, mss
You can try this=>
import mss
import numpy
with mss.mss() as sct:
monitor = {'top': 40, 'left': 0, 'width': 800, 'height': 640}
img = numpy.array(sct.grab(monitor))
print(img)
I tried all of the above but it did not give me the real-time screen update.
You can try this. This code is tested and worked successfully and also give you a good fps output. You can also judge this by each loop time it's needed.
import numpy as np
import cv2
from PIL import ImageGrab as ig
import time
last_time = time.time()
while(True):
screen = ig.grab(bbox=(50,50,800,640))
print('Loop took {} seconds',format(time.time()-last_time))
cv2.imshow("test", np.array(screen))
last_time = time.time()
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
If anyone looking for a much easier and fastest way to grab screen as frame in python, then try ScreenGear API from my high-performance video-processing vidgear library in just few lines of python code on any machine (Tested on all platforms, including Windows 10, MacOS Serra, Linux Mint) and enjoy threaded screen-casting:
Note: It also supports multiple backends and screens out-of-the box.
# import required libraries
from vidgear.gears import ScreenGear
import cv2
# define dimensions of screen w.r.t to given monitor to be captured
options = {'top': 40, 'left': 0, 'width': 100, 'height': 100}
# open video stream with defined parameters
stream = ScreenGear(logging=True, **options).start()
# loop over
while True:
# read frames from stream
frame = stream.read()
# check for frame if Nonetype
if frame is None:
break
# {do something with the frame here}
# Show output window
cv2.imshow("Output Frame", frame)
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# close output window
cv2.destroyAllWindows()
# safely close video stream
stream.stop()
VidGear library Docs: https://abhitronix.github.io/vidgear
ScreenGear API: https://abhitronix.github.io/vidgear/latest/gears/screengear/overview/
More examples: https://abhitronix.github.io/vidgear/latest/gears/screengear/usage/
I've tried ImageGrab from PIL and it gave me 20fps which is ok but using win32 libraries gave me +40fps which is amazing!
I used this code by Frannecklp but it didn't work just fine so I needed to modify it:
-Firstly pip install pywin32 in case using the libraries
-import the libraries like this instead:
import cv2
import numpy as np
from win32 import win32gui
from pythonwin import win32ui
from win32.lib import win32con
from win32 import win32api
for geting a simple image screen do:
from grab_screen import grab_screen
import cv2
img = grab_screen()
cv2.imshow('frame',img)
and for getting frames:
while(True):
#frame = grab_screen((0,0,100,100))
frame = grab_screen()
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q') or x>150:
break
This task is very simple with opencv, we are just capturing screenshots in loop, and converting it into frames. I created timer for screenrecording, in start you have to enter how many seconds you want to record:) Here is the code.
import cv2
import numpy as np
import pyautogui
from win32api import GetSystemMetrics
import time
#Take resolution from system automatically
w = GetSystemMetrics(0)
h = GetSystemMetrics(1)
SCREEN_SIZE = (w,h)
fourcc = cv2.VideoWriter_fourcc(*"XVID")
out = cv2.VideoWriter("recording.mp4", fourcc, 20.0, (SCREEN_SIZE))
tim = time.time()
tp = int(input('How many times you want to record screen?->(Define value in Seconds): '))
tp = tp+tp
f = tim+tp
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
tu = time.time()
if tu>f:
break
cv2.destroyAllWindows()
out.release()
So that's how you can use time in screen recording, you don't need to use imshow() because it shows infinitely our screen recording on-screen so output video looks weird.