我正在使用v4l2 api从Microsoft Lifecam获取图像,然后通过TCP将这些图像传输到远程计算机。我也使用ffmpeg API将视频帧编码为MPEG2VIDEO。这些录制的视频播放速度太快,这可能是因为没有捕获足够的帧并且由于FPS设置不正确。
以下是将YUV422源转换为RGB888图像的代码。这段代码片段是我代码中的瓶颈,因为它需要将近100到150毫秒才能执行,这意味着我无法以1280 x 720分辨率记录超过6 - 10 FPS。 CPU使用率也是100%。
for (int line = 0; line < image_height; line++) {
for (int column = 0; column < image_width; column++) {
*dst++ = CLAMP((double)*py + 1.402*((double)*pv - 128.0)); // R - first byte
*dst++ = CLAMP((double)*py - 0.344*((double)*pu - 128.0) - 0.714*((double)*pv - 128.0)); // G - next byte
*dst++ = CLAMP((double)*py + 1.772*((double)*pu - 128.0)); // B - next byte
vid_frame->data[0][line * frame->linesize[0] + column] = *py;
// increment py, pu, pv here
}
&#39; DST&#39;然后压缩为jpeg并通过TCP和&#39; vid_frame&#39;保存到磁盘。
如何使这段代码片段更快,以便在目前的5-6 FPS下,以1280x720分辨率获得至少30 FPS?
我尝试使用p_thread在三个线程之间并行化for循环,处理每个线程中三分之一的行。
for (int line = 0; line < image_height/3; line++) // thread 1
for (int line = image_height/3; line < 2*image_height/3; line++) // thread 2
for (int line = 2*image_height/3; line < image_height; line++) // thread 3
这让我每帧只有20-30毫秒的微小改进。 并行化这种循环的最佳方法是什么?我可以使用GPU计算或类似OpenMP吗?说吐出大约100个线程进行计算?
与Microsoft USB Lifecam相比,笔记本电脑摄像头的帧速率也更高。
以下是其他细节:
答案 0 :(得分:1)
如果您关心的只是fps而不是每帧ms(延迟),则另一个选项是每帧一个单独的线程。
线程不是提高速度的唯一选择。您也可以执行整数运算而不是浮点运算。 SIMD是一种选择。使用像sws_scale这样的现有库可能会为您提供最佳性能。
请确保您正在编译-O3(或-Os)。
确保禁用调试符号。
在循环外移动重复操作,例如
// compiler cant optimize this because another thread could change frame->linesize[0]
int row = line * frame->linesize[0];
for (int column = 0; column < image_width; column++) {
...
vid_frame->data[0][row + column] = *py;
您可以预先计算表,因此循环中没有数学运算:
init() {
for(int py = 0; py <= 255 ; ++py)
for(int pv = 0; pv <= 255 ; ++pv)
ytable[pv][py] = CLAMP(pv + 1.402*(py - 128.0));
}
for (int column = 0; column < image_width; column++) {
*dst++ = ytable[*pv][*py];
仅举几个选项。
答案 1 :(得分:1)
我认为除非你想重新发明痛苦的轮子,否则使用预先存在的选项(ffmpeg&#39; libswscale或ffmpeg&#fscmpeg&#39的缩放过滤器,gstreamer&#39;缩放插件等)是一个更好的选择
但是如果你想因任何原因重新发明轮子,请显示你使用的代码。例如,线程启动很昂贵,因此您需要在测量循环时间之前创建线程,并在帧到帧之间重用线程。更好的是帧线程,但这增加了延迟。这通常没问题,但取决于您的使用案例。更重要的是,不要编写C代码,学习编写x86程序集(simd),所有前面提到的库都使用simd进行此类转换,并且它会给你3-4倍的加速(因为它允许你做4-8个像素而不是每次迭代1次。
答案 2 :(得分:0)
您可以构建x行块并在单独的线程中转换每个块
答案 3 :(得分:0)
不要混用整数和浮点运算!
char x;
char y=((double)x*1.5); /* ouch casting double<->int is slow! */
char z=(x*3)>>1; /* fixed point arithmetic rulez */
使用SIMD
(尽管如果输入和输出数据都正确对齐,这会更容易......例如,使用RGB8888
作为输出)
使用openMP
一种不需要任何处理编码的替代方案,只需使用一个框架来完成整个处理,该框架在整个流水线中进行适当的时间戳(从图像采集时开始),并且希望能够优化以处理大数据。例如gstreamer
答案 4 :(得分:0)
这样的事情不起作用吗?
#pragma omp parallel for
for (int line = 0; line < image_height; line++) {
for (int column = 0; column < image_width; column++) {
dst[ ( image_width*line + column )*3 ] = CLAMP((double)*py + 1.402*((double)*pv - 128.0)); // R - first byte
dst[ ( image_width*line + column )*3 + 1] = CLAMP((double)*py - 0.344*((double)*pu - 128.0) - 0.714*((double)*pv - 128.0)); // G - next byte
dst[ ( image_width*line + column )*3 + 2] = CLAMP((double)*py + 1.772*((double)*pu - 128.0)); // B - next byte
vid_frame->data[0][line * frame->linesize[0] + column] = *py;
// increment py, pu, pv here
}
当然,您还必须相应地处理增量py,py,pv部分。
答案 5 :(得分:0)
通常仅使用整数变量来执行像素格式的变换。 它允许防止浮点和整数变量之间的转换。 此外,它允许更有效地使用现代CPU的SIMD扩展。 例如,这是转换YUV到BGR的代码:
const int Y_ADJUST = 16;
const int UV_ADJUST = 128;
const int YUV_TO_BGR_AVERAGING_SHIFT = 13;
const int YUV_TO_BGR_ROUND_TERM = 1 << (YUV_TO_BGR_AVERAGING_SHIFT - 1);
const int Y_TO_RGB_WEIGHT = int(1.164*(1 << YUV_TO_BGR_AVERAGING_SHIFT) + 0.5);
const int U_TO_BLUE_WEIGHT = int(2.018*(1 << YUV_TO_BGR_AVERAGING_SHIFT) + 0.5);
const int U_TO_GREEN_WEIGHT = -int(0.391*(1 << YUV_TO_BGR_AVERAGING_SHIFT) + 0.5);
const int V_TO_GREEN_WEIGHT = -int(0.813*(1 << YUV_TO_BGR_AVERAGING_SHIFT) + 0.5);
const int V_TO_RED_WEIGHT = int(1.596*(1 << YUV_TO_BGR_AVERAGING_SHIFT) + 0.5);
inline int RestrictRange(int value, int min = 0, int max = 255)
{
return value < min ? min : (value > max ? max : value);
}
inline int YuvToBlue(int y, int u)
{
return RestrictRange((Y_TO_RGB_WEIGHT*(y - Y_ADJUST) +
U_TO_BLUE_WEIGHT*(u - UV_ADJUST) +
YUV_TO_BGR_ROUND_TERM) >> YUV_TO_BGR_AVERAGING_SHIFT);
}
inline int YuvToGreen(int y, int u, int v)
{
return RestrictRange((Y_TO_RGB_WEIGHT*(y - Y_ADJUST) +
U_TO_GREEN_WEIGHT*(u - UV_ADJUST) +
V_TO_GREEN_WEIGHT*(v - UV_ADJUST) +
YUV_TO_BGR_ROUND_TERM) >> YUV_TO_BGR_AVERAGING_SHIFT);
}
inline int YuvToRed(int y, int v)
{
return RestrictRange((Y_TO_RGB_WEIGHT*(y - Y_ADJUST) +
V_TO_RED_WEIGHT*(v - UV_ADJUST) +
YUV_TO_BGR_ROUND_TERM) >> YUV_TO_BGR_AVERAGING_SHIFT);
}
此处的代码(http://simd.sourceforge.net/)。此外,还有针对不同SIMD优化的代码。