我必须将从一个专有设备SDK检索到的医学图像数据传递给第二个供应商在另一个(也是专有设备)SDK中的图像处理功能。
第一个功能为我提供了平面rgb格式的图像:
int mrcpgk_retrieve_frame(uint16_t *r, uint16_t *g, uint16_t *b, int w, int h);
uint16_t的原因是可以切换设备以输出编码为16位浮点值的每个颜色值。但是,我在“字节模式”下运行,因此每个颜色值的高8位始终为零。
另一个设备SDK的第二个功能定义如下:
BOOL process_cpgk_image(const PBYTE rgba, DWORD width, DWORD height);
因此,我们用以下位填充了三个缓冲区:(16位平面rgb)
R: 0000000 rrrrrrrr 00000000 rrrrrrrr ...
G: 0000000 gggggggg 00000000 gggggggg ...
B: 0000000 bbbbbbbb 00000000 bbbbbbbb ...
位所示的期望输出是:
RGBA: rrrrrrrrggggggggbbbbbbbb00000000 rrrrrrrrggggggggbbbbbbbb00000000 ....
我们无权访问这些功能的源代码,也无法更改环境。当前,我们已经实现了以下基本的“桥”来连接两个设备:
void process_frames(int width, int height)
{
uint16_t *r = (uint16_t*)malloc(width*height*sizeof(uint16_t));
uint16_t *g = (uint16_t*)malloc(width*height*sizeof(uint16_t));
uint16_t *b = (uint16_t*)malloc(width*height*sizeof(uint16_t));
uint8_t *rgba = (uint8_t*)malloc(width*height*4);
int i;
memset(rgba, 0, width*height*4);
while ( mrcpgk_retrieve_frame(r, g, b, width, height) != 0 )
{
for (i=0; i<width*height; i++)
{
rgba[4*i+0] = (uint8_t)r[i];
rgba[4*i+1] = (uint8_t)g[i];
rgba[4*i+2] = (uint8_t)b[i];
}
process_cpgk_image(rgba, width, height);
}
free(r);
free(g);
free(b);
free(rgba);
}
该代码工作得很好,但是处理成千上万的高分辨率图像需要花费很长时间。处理和检索这两个功能非常快,我们的桥梁目前是瓶颈。
我知道如何使用SSE2内部函数执行基本的算术,逻辑和移位运算,但是我想知道是否可以以及如何通过MMX,SSE2或[S] SSE3来加速从16位平面RGB到打包RGBBA的转换?
(最好使用SSE2,因为仍然有一些2005年前的设备在使用中)。
最佳答案
这是一个简单的SSE2实现:
#include <emmintrin.h> // SSE2 intrinsics
assert((width*height)%8 == 0); // NB: total pixels must be multiple of 8
for (i=0; i<width*height; i+=8)
{
__m128i vr = _mm_load_si128((__m128i *)&r[i]); // load 8 pixels from r[i]
__m128i vg = _mm_load_si128((__m128i *)&g[i]); // load 8 pixels from g[i]
__m128i vb = _mm_load_si128((__m128i *)&b[i]); // load 8 pixels from b[i]
__m128i vrg = _mm_or_si128(vr, _mm_slli_epi16(vg, 8));
// merge r/g
__m128i vrgba = _mm_unpacklo_epi16(vrg, vb); // permute first 4 pixels
_mm_store_si128((__m128i *)&rgba[4*i], vrgba); // store first 4 pixels to rgba[4*i]
vrgba = _mm_unpackhi_epi16(vrg, vb); // permute second 4 pixels
_mm_store_si128((__m128i *)&rgba[4*i+16], vrgba); // store second 4 pixels to rgba[4*i+16]
}