【发布时间】:2015-09-14 04:48:42
【问题描述】:
我正在尝试使用 python 编写一个简单的 YUV 视频播放器。经过一些初步研究,我认为我可以使用 PySide 并开始使用它。作为第一步,我在不考虑实时性能的情况下采用了以下方法。 读取 YUV 缓冲区(420 平面)-> 将 YUV 图像转换为 RGB(32 位格式)-> 调用 PySide 实用程序进行显示。我的简单程序的基本问题是,我只能显示第一帧,其余的不显示,即使根据(下面)代码中的计数器似乎正在发生绘制事件。我将不胜感激任何 cmets 理解 (i) 我对在 QLabel/QWidget 上定期绘制/重新绘制的任何错误和缺乏理解。 (ii) 任何指向来自 YUV 或 RGB 源的基于 Python 的视频播放器/显示器的指针。
#!/usr/bin/python
import sys
from PySide.QtCore import *
from PySide.QtGui import *
import array
import numpy as np
class VideoWin(QWidget):
def __init__(self, width, height, f_yuv):
QWidget.__init__(self)
self.width = width
self.height = height
self.f_yuv = f_yuv
self.setWindowTitle('Video Window')
self.setGeometry(10, 10, width, height)
self.display_counter = 0
self.img = QImage(width, height, QImage.Format_ARGB32)
#qApp.processEvents()
def getImageBuf(self):
return self.img.bits()
def paintEvent(self, e):
painter = QPainter(self)
self.display_counter += 1
painter.drawImage(QPoint(0, 0), self.img)
def timerSlot(self):
print "In timer"
yuv = array.array('B')
pix = np.ndarray(shape=(height, width), dtype=np.uint32, buffer=self.getImageBuf())
for i in range(0,self.height):
for j in range(0, self.width):
pix[i, j] = 0
for k in range (0, 10):
#qApp.processEvents()
yuv.fromfile(self.f_yuv, 3*self.width*self.height/2)
for i in range(0, self.height):
for j in range(0, self.width):
Y_val = yuv[(i*self.width)+j]
U_val = yuv[self.width*self.height + ((i/2)*(self.width/2))+(j/2)]
V_val = yuv[self.width*self.height + self.width*self.height/4 + ((i/2)*(self.width/2))+(j/2)]
C = Y_val - 16
D = U_val - 128
E = V_val - 128
R = (( 298 * C + 409 * E + 128) >> 8)
G = (( 298 * C - 100 * D - 208 * E + 128) >> 8)
B = (( 298 * C + 516 * D + 128) >> 8)
if R > 255:
R = 255
if G > 255:
G = 255
if B > 255:
B = 255
assert(int(R) < 256)
pix[i, j] = (255 << 24 | ((int(R) % 256 )<< 16) | ((int(G) % 256 ) << 8) | (int(B) % 256))
self.repaint()
print "videowin.display_counter = %d" % videowin.display_counter
if __name__ == "__main__":
try:
yuv_file_name = sys.argv[1]
width = int(sys.argv[2])
height = int(sys.argv[3])
f_yuv = open(yuv_file_name, "rb")
videoApp = QApplication(sys.argv)
videowin = VideoWin(width, height, f_yuv)
timer = QTimer()
timer.singleShot(100, videowin.timerSlot)
videowin.show()
videoApp.exec_()
sys.exit(0)
except NameError:
print("Name Error : ", sys.exc_info()[1])
except SystemExit:
print("Closing Window...")
except Exception:
print(sys.exc_info()[1])
我尝试了第二种方法,我尝试了创建一个 Signal 对象的组合,该对象“发射”每个解码的 RGB 图像(从 YUV 转换)作为信号,该信号被显示类中的“updateFrame”方法捕获使用 QPainter.drawImage(...) 方法显示接收到的 RGB 缓冲区/帧。 YUV-to-RGB decode--->Signal(Image buffer) --->updateFrame ---> QPainter.drawImage(...) 这也仅显示第一张图像,尽管捕获信号(获取图像)的插槽显示它被调用的次数与 YUV->RGB 转换器/解码器发送的信号一样多。我也试过在单独的线程中运行 YUV->RGB 转换器和视频显示(调用 drawImage),但结果是一样的。
请注意,在这两种情况下,我都将 RGB 像素值直接写入 QImage 对象的位缓冲区,该对象是所示代码中 VideoWin 类的一部分(注意:代码行 pix = np.ndarray( shape=(height, width), dtype=np.uint32, buffer=videowin.getImageBuf()) 获取 QImage 类的 img.bits() 缓冲区) 此外,对于这个测试,我只解码和显示视频文件的前 10 帧。 版本:Python - 2.7,Qt - 4.8.5 使用 Pyside
【问题讨论】:
-
您的示例代码的根本问题在于,它在事件循环开始之前完成了所有处理。您需要先启动事件循环,然后调用一个进行处理的槽(您可以为此使用QTimer.singleShot,或者只是向用户界面添加一个按钮)。您可能还想考虑使用QApplication.processEvents。
-
嗨,ekhumoro,感谢您的意见。我不确定我是否明白你的意思。我已经按照上面编辑的代码尝试了更改,但这没有帮助。仅绘制第一帧,即使调用了 10 次paintEvent SLOT(对于 10 帧视频输入)。
-
也许你应该提供你正在使用的 yuv 文件的链接,以便其他人可以测试你的代码。
-
我在dropbox.com/s/e42iyvv40q2zw2m/test.yuv?dl=0保存了一个10帧流test.yuv
-
test.yuv的分辨率为1920x1080
标签: python qt numpy video pyside