Python mss多进程截屏仅30FPS 如何优化至稳定60FPS
结论
你的硬件配置(AMD Ryzen 5 4500U、8GB内存)完全可以支撑1366768分辨率下60FPS的屏幕捕获与实时展示,帧率稳定在30FPS不是硬件限制,是代码逻辑和依赖库默认配置导致的。
你统计的单帧0.033s耗时,是屏幕采集+跨进程全尺寸帧传输的总耗时,不是纯采集的耗时——纯mss采集1366768分辨率画面在你的CPU上单帧耗时通常在10ms以内,剩下的23ms基本都是跨进程传输大体积原始帧的开销。
具体优化点
- 把缩放、格式转换逻辑移回采集进程
你现在把未做任何处理的1366768 BGRA原始帧直接放入跨进程队列,单帧体积约4MB,跨进程的序列化、内存拷贝开销极高。你之前注释掉的「采集后直接resize到目标分辨率、转格式再入队」的逻辑是正确方向,处理完的640480帧单帧体积仅900KB左右,IPC开销能降低75%以上。 - 砍掉冗余的颜色空间转换
你现在用cv2把BGRA格式转成BGR再给pygame渲染完全是多余操作,pygame原生支持BGRA/BGRX格式的buffer输入,直接把pygame.image.frombuffer的格式参数从'BGR'改成'BGRA'即可,每帧能省掉几毫秒的转码耗时。 - 给队列加限长和丢帧逻辑
你现在用的是无界队列,一旦展示端消费速度暂时跟不上采集端,队列里会堆积大量过时帧,展示端会一直处理旧帧,进一步拉低实际显示帧率。初始化队列时设置maxsize=2,采集端入队前如果队列已满就先丢弃最旧的帧,保证队列里永远存的是最新的画面,避免堆积。 - 调整mss捕获参数
初始化mss时关闭鼠标捕获(不需要显示鼠标的场景下):with mss(with_cursor=False) as sct,能省掉每帧绘制鼠标指针的开销,Windows平台下这个调整通常能让采集耗时直接降低一半。 - 优化pygame渲染逻辑
开启双缓冲的情况下,用pygame.display.flip()替代pygame.display.update()渲染效率更高;非调试场景下去掉每帧调用queue.qsize()的逻辑,这个方法在多进程队列下会加锁,有额外开销。
优化后的核心代码参考
SCR_SIZE = (640, 480) def grabber(queue, isRunning): from mss import mss from time import time from numpy import asarray from cv2 import resize global SCR_SIZE # 关闭鼠标捕获降低开销 with mss(with_cursor=False) as sct: monitor = sct.monitors[1] while isRunning.value: t0 = time() # 采集后直接缩放到目标分辨率,不做多余颜色转换,直接传BGRA格式 raw_frame = asarray(sct.grab(monitor)) resized_frame = resize(raw_frame, SCR_SIZE) # 队列满就丢旧帧,避免堆积 if queue.full(): try: queue.get_nowait() except: pass queue.put(resized_frame) print('P1 Finished!') def displayer(queue, isRunning): import pygame import pygame.display import pygame.image import pygame.time import pygame.font import pygame.event from pygame.locals import DOUBLEBUF global SCR_SIZE pygame.init() SCR = pygame.display.set_mode(SCR_SIZE, DOUBLEBUF) SCR.set_alpha(None) clock = pygame.time.Clock() FONT_COMIC = pygame.font.SysFont('Cambria Math', 20) while isRunning.value: for EVENT in pygame.event.get(): if EVENT.type == pygame.QUIT: print('Quit') isRunning.value = 0 currentFrame = queue.get() if currentFrame is not None: # 直接读BGRA格式,去掉cv2转色步骤 SCR.blit(pygame.image.frombuffer(currentFrame, SCR_SIZE, 'BGRA'), (0,0)) else: break clock.tick(60) SCR.blit(FONT_COMIC.render('FPS:'+str(clock.get_fps())[:5], False, (0,255,0)),(10,10)) # 双缓冲下用flip替代update pygame.display.flip() print('P2 Finished!') if __name__ == "__main__": from multiprocessing import Process, Queue, Value from time import sleep # 队列限长2,避免帧堆积 queue = Queue(maxsize=2) isGameRunning = Value('i', 1) p1 = Process(target=grabber, args=(queue,isGameRunning, )) p2 = Process(target=displayer, args=(queue,isGameRunning, )) p1.start() p2.start() while isGameRunning.value: sleep(5) p1.terminate() p2.terminate()
调整完之后,正常情况下稳定帧率可以跑到60FPS。
内容的提问来源于stack exchange,提问作者Muhammad Ikhwan Perwira
相关产品推荐
相关产品推荐

