我正在开发一个视频播放应用程序,它将逐帧显示包含原始平面图像数据的视频文件。它包含的数据是8位rgb(此时没有alpha)。这里的硬件仅接受交错图像数据。结果,我需要将平面图像数据转换为交错图像数据。我所做的是记忆或记忆平面数据。但是,在处理高清内容时,由于数据是原始数据,这将占用大量时间。我尝试用两个线程实现,一个用于处理另一个用于显示。事实证明,显示已处理的交错数据非常快,但处理线程无法跟上它。因此,由于处理速度,帧速率受到严重影响。
我确实知道要预先处理所有内容并将其保存在内存中(这些视频剪辑的帧数相对较小)。需要时,我只会在内存中显示已处理的数据。实际上我测试了这种方法,它相当快(60fps)。但是,这似乎不是最理想的,因为我要么首先运行速度很慢,要么在播放开始前需要等待一段时间。而且,当文件大小变大时,由于内存限制,不可能这样做。
所以我正在寻找任何能够快速进行平面>交错的图像处理库或算法。我确实尝试了来自增强的吉尔,但表现并不是很好。
答案 0 :(得分:2)
(在我上面的评论旁边添加代码)
在2.4GHz Intel Core 2 Duo上使用g ++ 4.2.1和-O2编译,在10秒内运行2000帧。
int const kWidth = 1920;
int const kHeight = 1080;
for (std::size_t i = 0; i != kWidth*kHeight; ++i) {
interleavedp[i*3+0] = planarp[i+0*kWidth*kHeight];
interleavedp[i*3+1] = planarp[i+1*kWidth*kHeight];
interleavedp[i*3+2] = planarp[i+2*kWidth*kHeight];
}
请注意,以这种方式编写它可以让编译器更好地进行优化。将其分解为行(或12字节块)只会使事情变得更慢。
答案 1 :(得分:1)
libswscale(ffmpeg的一部分)可以做到这一点据我所知,可以找到一个好的教程here
答案 2 :(得分:1)
使用矢量内在函数编写此函数应该相当简单。我不知道你正在使用什么处理器,编译器或打包像素格式,所以我将为x86使用GCC和MMX内在函数给出一个示例实现。将此代码转换为ARM NEON,PowerPC Altivec或x86 / x64 SSE代码也应该很容易。
这应该将RGB平面转换为32位RGBA打包,尽管ARGB实际上更常见。如果你需要24位RGB,你将需要有点创意。在编写这一小段代码时,处理器的“软件开发人员手册”将是您最好的朋友,您还需要阅读编译器的文档。
SIMD可以很好地处理这个问题,您可以通过下面的代码来判断它有多短。请注意,下面的代码实际上是C99,而不是C ++,因为C99允许访问restrict
关键字,这可以减少生成的加载和存储的数量。
另请注意,此代码具有严格的对齐要求。
#include <stddef.h>
#if defined(USE_MMX)
typedef char v8qi __attribute__ ((vector_size(8)));
void pack_planes3(void *dest, const void *src[3], size_t n)
{
v8qi *restrict dp = dest, x, y, zero = { 0, 0, 0, 0, 0, 0, 0, 0 };
const v8qi *restrict sp1 = src[0];
const v8qi *restrict sp2 = src[1];
const v8qi *restrict sp3 = src[2];
size_t i;
for (i = 0; i < n; i += 8) {
x = __builtin_ia32_punpckhbw(*sp1, *sp3);
y = __builtin_ia32_punpckhbw(*sp2, zero);
dp[0] = __builtin_ia32_punpckhbw(x, y);
dp[1] = __builtin_ia32_punpcklbw(x, y);
x = __builtin_ia32_punpcklbw(*sp1, *sp3);
y = __builtin_ia32_punpcklbw(*sp2, zero);
dp[2] = __builtin_ia32_punpckhbw(x, y);
dp[3] = __builtin_ia32_punpcklbw(x, y);
sp1++;
sp2++;
sp3++;
dp += 4;
}
}
#else
/* Scalar implementation goes here */
#endif
答案 3 :(得分:1)
有一个Simd Library。它有许多图像转换算法。它支持以下图像格式之间的转换:NV12,YUV420P,YUV422P,YUV444P,BGR-24,BGRA-32,HSL-24,HSV-24,Gray-8,Bayer等。 使用不同的SIMD CPU扩展优化算法。特别是该库支持以下CPU扩展:用于x86 / x64的SSE,SSE2,SSSE3,SSE4.1,SSE4.2,AVX和AVX2,用于PowerPC的VMX(Altivec)和VSX(Power7)。
答案 4 :(得分:1)
我不得不解决相同的问题,但是我有一个额外的约束,即我需要“就地”执行转换(即,我必须将图像数据留在同一缓冲区中)。在下图中,我演示了如何将像素从平面表示移动到交错表示:
因此,我们看到可以通过一系列交换来“原地”更改图像。
这是我的C ++实现,它以线性时间运行。模板
参数T
是图像通道类型(例如,uint8_t
用于字节大小
频道)。
#include <vector>
#include <cstdint>
#include <algorithm>
template <typename T>
void planarToInterleaved(int numPixels, int numChannels, T pixels[]) {
const int size = numPixels * numChannels;
std::vector<bool> visited(size);
std::fill(visited.begin(), visited.end(), false);
auto nextUnvisited = [&](int index) -> int {
int i;
for (i = index; i < size && visited[i]; i++)
;
return i;
};
auto interleavedIndex = [=](int planarIndex) -> int {
const int i = planarIndex % numPixels;
const int k = planarIndex / numPixels;
return numChannels*i + k;
};
int J = 0;
int Jnext = 0;
while ( (J = nextUnvisited(Jnext++)) < size ) {
visited[J] = true;
const int Jstart = J;
T tmp = pixels[J];
while ( true ) {
const int I = interleavedIndex(J);
if ( I == J ) break; // 1-node cycle
std::swap(pixels[I],tmp);
if ( I == Jstart ) break;
J = I;
visited[J] = true;
}
}
}
在这里,我转换存储在缓冲区image
中的WxH RGB图像(
保持W * H * 3值)从平面到交错:
planarToInterleaved(W*H, 3, image);
无论如何,这很有趣。