是否有一个很好的库或算法可以帮助快速将平面图像转换为交错图像(C ++)?

时间:2011-09-01 14:01:10

标签: c++ algorithm image-processing

我正在开发一个视频播放应用程序,它将逐帧显示包含原始平面图像数据的视频文件。它包含的数据是8位rgb(此时没有alpha)。这里的硬件仅接受交错图像数据。结果,我需要将平面图像数据转换为交错图像数据。我所做的是记忆或记忆平面数据。但是,在处理高清内容时,由于数据是原始数据,这将占用大量时间。我尝试用两个线程实现,一个用于处理另一个用于显示。事实证明,显示已处理的交错数据非常快,但处理线程无法跟上它。因此,由于处理速度,帧速率受到严重影响。

我确实知道要预先处理所有内容并将其保存在内存中(这些视频剪辑的帧数相对较小)。需要时,我只会在内存中显示已处理的数据。实际上我测试了这种方法,它相当快(60fps)。但是,这似乎不是最理想的,因为我要么首先运行速度很慢,要么在播放开始前需要等待一段时间。而且,当文件大小变大时,由于内存限制,不可能这样做。

所以我正在寻找任何能够快速进行平面>交错的图像处理库或算法。我确实尝试了来自增强的吉尔,但表现并不是很好。

5 个答案:

答案 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)

我不得不解决相同的问题,但是我有一个额外的约束,即我需要“就地”执行转换(即,我必须将图像数据留在同一缓冲区中)。在下图中,我演示了如何将像素从平面表示移动到交错表示:

enter image description here

因此,我们看到可以通过一系列交换来“原地”更改图像。 这是我的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);

无论如何,这很有趣。