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 图像而不用黑色填充背景。 这样会节省时间,因为每次使用blitfill,后台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幻灯片延迟异常长的主要内容,如果未能解决你的问题,请参考以下文章

php WooSlider黑名单Jetpack延迟加载幻灯片图片

带有 knitr 幻灯片的长功能垂直滚动条

jQuery幻灯片插件OWL Carousel

React 延迟加载/无限滚动解决方案

使用 jQuery Mobile 幻灯片转换保持滚动位置

自动为li添加动画延迟