Pygame幻灯片延迟异常长
Posted
技术标签:
【中文标题】Pygame幻灯片延迟异常长【英文标题】:Pygame slideshow delay anormally long 【发布时间】:2019-07-31 08:45:09 【问题描述】:我正在从一个目录设置一个混合图像和视频的幻灯片系统。 我正在使用 Raspberry Pi B、pygame 和 vlc。 我没有安装 X,所以一切都发生在帧缓冲区中。
我的实际代码正在运行,但是:
不遵守 4 秒延迟。图像显示 +- 11 秒。 其中一张图像没有什么特别之处,显示时间更长,+- 1m30。 (我真正的问题)我尝试了一个带有 fbi、fim、vlc 的 bash 脚本,但没有合适的结果。最接近的是 vlc,但在帧缓冲区中渲染图像需要很长时间。
我对 pygame 很陌生。代码如下:
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()
我愿意接受任何能够处理图片和视频的替代方案。
编辑:终于到了 pygame 用pygame.transform.scale()
调整(下一个)图像大小的时候了。
有什么办法可以优化吗?例如,在不调整大图像大小的情况下全屏打印?
【问题讨论】:
关于这个One of the images witch has nothing particular, is displayed much longer
:肯定有什么特别的,不然很长的时间就没法解释了。总是相同的图像停留时间更长吗?您的幻灯片中有很大的图片吗?
这是一个PNG,和其他几个一样。它实际上是最小的图像:35k。尺寸为 1451x816。你认为这可能是下一张图像(3,3M,3000x1687)的缩放,pygame.transform.scale()
需要很长时间吗?
确实是这个原因。
是的。缩放大图片需要时间。
【参考方案1】:
如果没有图像和视频,我无法重现该行为,但这里有一些建议可以帮助在显示图像时加快代码速度。
不要使用time.sleep()
。它将在给定时间内冻结游戏,因此所有计算都在此时间窗口之外完成,从而消耗更多时间。最好使用pygame time Clock。来自其tick()
方法的文档:
如果您传递可选的帧速率参数,该函数将延迟以使游戏运行速度低于给定的每秒滴答数。这可以用来帮助限制游戏的运行速度。通过每帧调用一次 Clock.tick(40),程序将永远不会以每秒超过 40 帧的速度运行。
tick()
方法应该在主循环的每次迭代中调用一次,所以最好不要将它放在 if
语句中。
这里:
screen.fill(black)
screen.blit(img, imgrect)
第一行screen.fill(black)
完全没用:你在第二行重新绘制整个表面,覆盖了所有的黑色背景,因为图像被重新缩放到屏幕大小。您可以安全地blit
图像而不用黑色填充背景。
这样会节省时间,因为每次使用blit
或fill
,后台pygame
都会在Surface上做很多操作来改变像素的颜色(改变的像素越多,需要的时间越长)。
当然,如果您加载的任何图像都有 Alpha 通道。如果您有带 Alpha 通道的图片,则需要先将背景涂成黑色。为了节省时间,我建议使用其他程序从图像中删除 alpha 通道。
pygame.transform.scale()
需要时间,特别是如果您的图片非常大。尝试使用另一个程序重新缩放您的图像,并加载尺寸尽可能靠近您的屏幕的 pygame 图像。
加载图片时,添加.convert()
。这将使 blitting 更快。应该是:img = pygame.image.load(fullname).convert()
。
最后,您的代码应如下所示:
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
【讨论】:
感谢这些很棒的提示,我完全可以分辨出其中的区别。我最终使用 Pillow 来调整、压缩和保存图像。但是,加载我调整大小的 PNG (1.4Mo)(它是 Pi B..)仍然可能很长,因此,之前的图像显示为 10 到 35 秒而不是 5 秒。这是我的实际代码:framagit.org/SecT0uch/slideshow/blob/master/Slideshow.py。你认为我应该降低 img.save(fullname, optimize=True, quality=95) 的质量吗?我在想 80 或 85 降低质量可能有助于节省时间。你可以试试看。如果它们的屏幕大小不同,您当前的代码是否会覆盖磁盘上的图像?这可能有点危险,您会丢失原始图像。 是的,它会替换文件,而不是完全在 SD 上,而是在挂载的 SMB 共享上。这不是问题,因为目录中的所有文件都是副本,原始文件安全地保存在其他地方。明天我会尝试降低和测试 ipage 质量。【参考方案2】:在Valentino 的帮助下,我找出了问题所在。 他帮助我优化了代码以提高每张图片的加载时间,从而解决了第一个问题。
See his answer.
另外,我添加了一段代码:
# 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()
如果图片不是屏幕分辨率,我使用 Pillow (PIL) 来调整调色板的大小并将其缩小为 8 位(256 色)。 它显着减小了文件大小(尤其是对于大文件)并允许 pygame 更快地加载图像。 它解决了第二个问题。
对于那些感兴趣的人,完整的代码是:
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()
【讨论】:
以上是关于Pygame幻灯片延迟异常长的主要内容,如果未能解决你的问题,请参考以下文章