I would like to blit images from webcam to a screen of pygame.
I'm using OS X.
I know pygame currently supports only Linux and v4l2 cameras,but,then how can we use webcam with pygame?
import cv2
import pygame
import numpy as np
pygame.init()
pygame.display.set_caption("OpenCV camera stream on Pygame")
surface = pygame.display.set_mode([1280,720])
#0 Is the built in camera
cap = cv2.VideoCapture(0)
#Gets fps of your camera
fps = cap.get(cv2.CAP_PROP_FPS)
print("fps:", fps)
#If your camera can achieve 60 fps
#Else just have this be 1-30 fps
cap.set(cv2.CAP_PROP_FPS, 60)
while True:
surface.fill([0,0,0])
success, frame = cap.read()
if not success:
break
#for some reasons the frames appeared inverted
frame = np.fliplr(frame)
frame = np.rot90(frame)
# The video uses BGR colors and PyGame needs RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
surf = pygame.surfarray.make_surface(frame)
for event in pygame.event.get():
if event.type == pygame.KEYUP:
background_color = red
surface.fill(background_color)
pygame.display.update
end_time = self.time()
# Show the PyGame surface!
surface.blit(surf, (0,0))
pygame.display.flip()
Related
I want a screen recorder. I thought of making my own.
I checked the internet and found: https://www.thepythoncode.com/code/make-screen-recorder-python
The Code:
import cv2
import numpy as np
import pyautogui
# Display screen resolution, get it from your OS settings
SCREEN_SIZE = (1366, 768)
# Define the codec
fourcc = cv2.VideoWriter_fourcc(*"XVID")
# Create the video write object
out = cv2.VideoWriter("output.avi", fourcc, 30.0, (SCREEN_SIZE))
while True:
# make a screenshot
img = pyautogui.screenshot()
# img = pyautogui.screenshot(region=(0, 0, 300, 400))
# convert these pixels to a proper numpy array to work with OpenCV
frame = np.array(img)
# convert colors from BGR to RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# write the frame
out.write(frame)
# show the frame
cv2.imshow("screenshot", frame)
# if the user clicks q, it exits
if cv2.waitKey(1) == ord("q"):
break
# Make sure everything is closed when exited
cv2.destroyAllWindows()
out.release()
The Problem:
When I run this, this works good. But it has a random speed after output. The fps is 30 but when I record for 1 minute, the video is 5 seconds or 10 minutes (random).
How do I make this recorder give output in 30 fps with the correct speed?
basically if you want to continue with your same code, you will have to compromise on resolution or frame rate.
My suggestion is to try the cv2.VideoCapture() functionality.
I am attaching the link to the webpage where there is a detailed step-by-step process where the author has achieved an FPS rate of 30.75.
Here's the link:
https://www.pyimagesearch.com/2017/02/06/faster-video-file-fps-with-cv2-videocapture-and-opencv/
The second half of the content present in the link has The faster, threaded method to reading video frames with OpenCV.
# import the necessary packages
from imutils.video import FileVideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True,
help="path to input video file")
args = vars(ap.parse_args())
# start the file video stream thread and allow the buffer to
# start to fill
print("[INFO] starting video file thread...")
fvs = FileVideoStream(args["video"]).start()
time.sleep(1.0)
# start the FPS timer
fps = FPS().start()
# loop over frames from the video file stream
while fvs.more():
# grab the frame from the threaded video file stream, resize
# it, and convert it to grayscale (while still retaining 3
# channels)
frame = fvs.read()
frame = imutils.resize(frame, width=450)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame = np.dstack([frame, frame, frame])
# display the size of the queue on the frame
cv2.putText(frame, "Queue Size: {}".format(fvs.Q.qsize()),
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
# show the frame and update the FPS counter
cv2.imshow("Frame", frame)
cv2.waitKey(1)
fps.update()
# stop the timer and display FPS information
fps.stop()
print("[INFO] elasped time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# do a bit of cleanup
cv2.destroyAllWindows()
fvs.stop()
This question already has answers here:
How can I load an animated GIF and get all of the individual frames in Pygame?
(3 answers)
Closed 2 years ago.
So I have this idea for a game where you dodge projectiles while controlling a helicopter. I am wondering if you can make a sprite appear as a gif, or something along the lines of two images switching every fraction of a second. I know how to make a sprite appear as one image:
self.surf = pygame.image.load("example.png").convert()
But I was wondering if this had an effect:
self.surf = pygame.image.load("example.gif").convert()
Unfortunately, it only displayed the first image in the gif.
Here is the gif:
Edit: Ok so I looked at the answers and tried to implement them in my code, but then it was all too confusing and I had tried to do something a bit more simple. This is what I came up with:
if play == 1:
self.surf = pygame.image.load("Image2.png").convert()
pygame.display.update()
play = 2
time.sleep(.2)
if play == 2:
self.surf = pygame.image.load("Image1.png").convert()
pygame.display.update()
play = 1
time.sleep(.2)
But, all that did was display the player sprite as image 1. Is there anything I can add to make this work?
PyGame respectively the pygame.image module can only handle non-animated GIFs.
But on the PyGame homepage is introduced the GIFImage library:
This library adds GIF animation playback to pygame.
Another option is to use the Pillow library (pip install Pillow).
Write a function, that can convert a PIL image to a pygame.Surface:
(see also PIL and pygame.image)
def pilImageToSurface(pilImage):
mode, size, data = pilImage.mode, pilImage.size, pilImage.tobytes()
return pygame.image.fromstring(data, size, mode).convert_alpha()
Use the PIL library to load a GIF frame by frame:
(see also Extracting The Frames Of An Animated GIF Using Pillow
def loadGIF(filename):
pilImage = Image.open(filename)
frames = []
if pilImage.format == 'GIF' and pilImage.is_animated:
for frame in ImageSequence.Iterator(pilImage):
pygameImage = pilImageToSurface(frame.convert('RGBA'))
frames.append(pygameImage)
else:
frames.append(pilImageToSurface(pilImage))
return frames
See also Load animated GIF and a simple animated gif viewer example:
import pygame
from PIL import Image
def pilImageToSurface(pilImage):
mode, size, data = pilImage.mode, pilImage.size, pilImage.tobytes()
return pygame.image.fromstring(data, size, mode).convert_alpha()
def loadGIF(filename):
pilImage = Image.open(filename)
frames = []
if pilImage.format == 'GIF' and pilImage.is_animated:
for frame in ImageSequence.Iterator(pilImage):
pygameImage = pilImageToSurface(frame.convert('RGBA'))
frames.append(pygameImage)
else:
frames.append(pilImageToSurface(pilImage))
return frames
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
gifFrameList = loadGIF("my_gif.gif")
currentFrame = 0
run = True
while run:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
rect = gifFrameList[currentFrame].get_rect(center = (250, 250))
window.blit(gifFrameList[currentFrame], rect)
currentFrame = (currentFrame + 1) % len(gifFrameList)
pygame.display.flip()
You can use the list of pygame.Surface objects to generate a Spritesheet.
Pygame can't do gifs, but if you really want to, you could animate it frame by frame one image at a time.
I am trying to make a game with pygame and face recognition. In order to do that I need to windows open. The one with the face recognizing one and one with the game one. But when I imported the face_recog.py , the game window wouldn't show until the face_recog would be closed. What should I do??
I tried importing in side the while of the game.py file
...python game.py code
import pygame
import face_recog
from background import *
FRAME=0
class Game:
def __init__(self):
self.width=900
self.height=600
self.screen=pygame.display.set_mode((self.width,self.height))
self.clock=pygame.time.Clock()
self.fire_rect=[530,40]
def main(self):
global FRAME
#sprite 그룹 생성
self.all_sprites=pygame.sprite.Group()
self.platforms=pygame.sprite.Group()
self.player_group=pygame.sprite.Group()
pygame.init()
#sprite 그룹에 sprite 추가
self.player1=Player((self.width/2,self.height/2),self)
self.all_sprites.add(self.player1)
self.player_group.add(self.player1)
#배경 벽 불러옴
for plat in PlatformList:
p=Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
#초기화
trap1=trap(self)
background_=background(self.width,self.height)
item_=item(self)
self.shot_=shot(self.screen,self)
item_.item_display(self.screen) #아이템은 사라질 수 있으므로 while 밖
while True:
#settings
time=self.clock.tick(60)
FRAME+=1
self.screen.fill((255,193,158))
#배경 그림
background_.background(self.screen)
#item_.item_display(self.screen)
item_.item_eat(self.screen)
trap1.trap_draw(self.screen,self.fire_rect)
self.shot_.shooting()
self.event()
self.all_sprites.update()
self.all_sprites.draw(self.screen)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
'''face_recog.py code
import sys
import os
import dlib
import glob
from skimage import io
import numpy as np
import cv2
from scipy.spatial import distance as dist #입술 사이 거리 계산 위해
import math
import pygame
from game import *
cap = cv2.VideoCapture(0) #동영상 입력부분
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (1280, 720))
predictor_path = 'shape_predictor_81_face_landmarks.dat'
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_path) #LANDMARK PREDICTOR
(mStart,mEnd)=(48,54) #mouth의 시작점, 끝점 번호
MOUTH_AR_THRESH = 0.1
while(cap.isOpened()):
ret, frame = cap.read() #영상 읽어들임
frame = cv2.flip(frame, 1)
dets = detector(frame, 0) #rects
for k, d in enumerate(dets):
shape = predictor(frame, d)
landmarks = np.matrix([[p.x, p.y] for p in shape.parts()])
for num in range(shape.num_parts):
cv2.circle(frame, (shape.parts()[num].x, shape.parts()[num].y), 3, (0,255,0), -1)
A=dist.euclidean((shape.parts()[61].x,shape.parts()[61].y),(shape.parts()[67].x,shape.parts()[67].y))
B=dist.euclidean((shape.parts()[63].x,shape.parts()[63].y),(shape.parts()[65].x,shape.parts()[65].y))
C=dist.euclidean((shape.parts()[48].x,shape.parts()[48].y),(shape.parts()[54].x,shape.parts()[54].y))
mar=(A+B)/(2.0*C)
mar=round(mar,5)
if mar>MOUTH_AR_THRESH:
cv2.putText(frame,"MOUTH IS OPEN!",(30,60),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)
cv2.imshow('frame', frame) #윈도우 창의 제목
out.write(frame)
if cv2.waitKey(1) & 0xFF == ord('q'): #key입력을 기다림, q를 입력받으면 종료
print("q pressed")
break
cap.release()
out.release()
cv2.destroyAllWindows()
Your program is working inside a single thread. The first while loop keeps the current thread busy so that it can not handle the second loop (window) at the same time.
The solution is you must separate them into 2 different threads. You should create 2 classes, one class runs as main thread, the other runs in new thread.
I'm setting up a Slideshow system mixing images and videos, from a directory.
I'm using a Raspberry Pi B, pygame and vlc.
I didn't install X so everything happens in framebuffer.
My actual code is working but :
The 4 seconds delay is not respected. The image is displayed +- 11 seconds.
One of the images witch has nothing particular, is displayed much longer, +- 1m30. (my real problem)
I tried a bash script with fbi, fim, vlc without suitable result. The closest was with vlc but it takes too long to render an image in framebuffer.
I'm quite new to pygame. Here is the code:
import pygame
import sys
import time
import vlc
import os
filesdir = '/home/pi/SMBmount/'
pygame.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
black = 0, 0, 0
screen = pygame.display.set_mode(size)
while True:
# For every file in filesdir :
for filename in os.listdir(filesdir):
filenamelower = filename.lower()
# If image:
if filenamelower.endswith('.png') or filenamelower.endswith('.jpg') or filenamelower.endswith('.jpeg'):
fullname = filesdir + filename
img = pygame.image.load(fullname)
img = pygame.transform.scale(img, size)
imgrect = img.get_rect()
screen.fill(black)
screen.blit(img, imgrect)
pygame.mouse.set_visible(False)
pygame.display.flip()
time.sleep(4)
# Elif video:
elif filenamelower.endswith('.mp4') or filenamelower.endswith('.mkv') or filenamelower.endswith('.avi'):
fullname = filesdir + filename
# Create instane of VLC and create reference to movie.
vlcInstance = vlc.Instance("--aout=adummy")
media = vlcInstance.media_new(fullname)
# Create new instance of vlc player
player = vlcInstance.media_player_new()
# Load movie into vlc player instance
player.set_media(media)
# Start movie playback
player.play()
# Do not continue if video not finished
while player.get_state() != vlc.State.Ended:
# Quit if keyboard pressed during video
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.display.quit()
pygame.quit()
sys.exit()
player.stop()
# Quit if keyboard pressed during video
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.display.quit()
pygame.quit()
sys.exit()
I'm open to any alternative able to work with pictures AND videos.
EDIT: It was finally the time it takes to pygame to resize the (next) image with pygame.transform.scale().
Is there any way to optimise that ? Like for example, to print fullscreen without resizing the large images ?
I cannot reproduce the behaviour without the images and the videos, but here a couple of advices which should help in speed up the code when displaying images.
Do not use time.sleep(). It will freeze the game for the given time, so all calculations are done outside this time window, consuming more time. Better to use pygame time Clock. From the docs of its tick() method:
If you pass the optional framerate argument the function will delay to keep the game running slower than the given ticks per second. This can be used to help limit the runtime speed of a game. By calling Clock.tick(40) once per frame, the program will never run at more than 40 frames per second.
The tick() method should be called once per iteration in the main loop, so better to not put it inside an if statement.
Here:
screen.fill(black)
screen.blit(img, imgrect)
The first line screen.fill(black) is completely useless: you are redrawing the whole surface in the second line covering all the black background, since the image is rescaled to the screen size. You can safely blit the image without filling the background with black.
This will save time, because each time you use blit or fill, pygame in background does a lot of operation on the Surface to change the color of the pixels (the more the pixels changed, the longer the time needed).
This of course if any of the images you load has an alpha channel. If you have pictures with alpha channel, you need to paint black the background before. To save time, I suggest to remove the alpha channel from the images using another program.
pygame.transform.scale() requires time, especially if you have very large picture. Try to rescale your image with another program and load in pygame images of size the closer possible to your screen.
When loading the images, add .convert(). This will make blitting faster. Should be: img = pygame.image.load(fullname).convert().
In the end, your code should look like:
imgexts = ['png', 'jpg', 'jpeg']
videxts = ['mp4', 'mkv']
#filtering out non video and non image files in the directory using regex
#remember to import re module
showlist = [filename for filename in os.listdir(filesdir) if re.search('[' + '|'.join(imgexts + videxts) + ']$', filename.lower())]
clock = pygame.time.Clock()
while True:
# For every file in filesdir :
for filename in showlist:
filenamelower = filename.lower()
# If image:
if filenamelower.endswith('.png') or filenamelower.endswith('.jpg') or filenamelower.endswith('.jpeg'):
#all your stuff but NOT the time.sleep()
elif filenamelower.endswith('.mp4') or filenamelower.endswith('.mkv') or filenamelower.endswith('.avi'):
#unchanged here
clock.tick(0.25) #framerate = 0.25 means 1 frame each 4 seconds
for event in pygame.event.get():
#unchanged here
I figured out what were the issues, with the help of Valentino.
He helped me to optimize the code to improve the loading times of every image, that fixed the first issue.
See his answer.
Additionnally, I added a block of code :
# If image is not same dimensions
if imgrect.size != size:
img = Image.open(fullname)
img = img.resize(size, Image.ANTIALIAS)
img.save(fullname, optimize=True, quality=95)
img = pygame.image.load(fullname).convert()
imgrect = img.get_rect()
If the picture is not the screen resolution, I use Pillow (PIL) to resize and reduce the color palette to 8-bit (256 colors).
It reduces file sizes significantly (especially for big files) and allow pygame to load the image faster.
It fixed the second issue.
For those interested, the full code is :
import pygame
import sys
import vlc
import os
import re
from PIL import Image
filesdir = '/home/pi/SMBmount/'
imgexts = ['png', 'jpg', 'jpeg']
videxts = ['mp4', 'mkv', 'avi']
time = 5 # Time to display every img
#filtering out non video and non image files in the directory using regex
showlist = [filename for filename in os.listdir(filesdir) if re.search('[' + '|'.join(imgexts + videxts) + ']$', filename.lower())]
pygame.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
while True:
# For every file in filesdir :
for filename in showlist:
filenamelower = filename.lower()
# If image:
if filenamelower.endswith('.png') or filenamelower.endswith('.jpg') or filenamelower.endswith('.jpeg'):
fullname = filesdir + filename
img = pygame.image.load(fullname).convert()
imgrect = img.get_rect()
# If image is not same dimensions
if imgrect.size != size:
img = Image.open(fullname)
img = img.resize(size, Image.ANTIALIAS)
img.save(fullname, optimize=True, quality=95)
img = pygame.image.load(fullname).convert()
imgrect = img.get_rect()
screen.blit(img, imgrect)
pygame.mouse.set_visible(False)
pygame.display.flip()
# Elif video:
elif filenamelower.endswith('.mp4') or filenamelower.endswith('.mkv') or filenamelower.endswith('.avi'):
fullname = filesdir + filename
# Create instane of VLC and create reference to movie.
vlcInstance = vlc.Instance("--aout=adummy")
media = vlcInstance.media_new(fullname)
# Create new instance of vlc player
player = vlcInstance.media_player_new()
# Load movie into vlc player instance
player.set_media(media)
# Start movie playback
player.play()
# Do not continue if video not finished
while player.get_state() != vlc.State.Ended:
# Quit if keyboard pressed during video
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.display.quit()
pygame.quit()
sys.exit()
player.stop()
clock.tick(1 / time) # framerate = 0.25 means 1 frame each 4 seconds
# Quit if keyboard pressed during video
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.display.quit()
pygame.quit()
sys.exit()
i need to put a university uniform to a camera feed to place their faces on top of it and take a snapshot and upload it to our database. i need some help in my code written below.
1st problem: I cant call my webcam
2nd problem: my overlaying image isnt appearing right as i want it to be.(i need it to the bottom of the window...)
I am following this youtube tutorial.
import pygame, sys
import pygame.camera
from pygame.locals import *
pygame.init()
pygame.camera.init()
screen = pygame.display.set_mode((640, 480))
cam = pygame.camera.Camera("/dev/video0", (640,480))
cam.start()
background=pygame.Surface((window.get_rect().width,
window.get_rect().height))
background.fill((0, 0, 0))
image=pygame.image.load('College Boy Medium.png')
image=image.convert()
image = pygame.transform.scale(image, (720,640))
rect=image.get_rect()
while 1:
image = cam.get_image()
screen.blit(image,(0,0))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit
Screen.blit(image,(0,0)) the 0,0 refers to the upper left of the image box x y position.
So 0,20 would place the image at x position 0 and the y position down 20 pixels.