【发布时间】:2020-03-25 04:35:34
【问题描述】:
我遇到了将两个寄存器相乘的问题(或者只是通过float 常量进行寄存器)。一个寄存器是__m128i 类型,包含一个来自 16 个像素的 RGBA 像素颜色通道(16 个像素的数组作为参数发送到 CPP dll)。我想将此寄存器乘以常数以获得此通道的灰度值,并对存储在__m128i 寄存器中的其他通道执行此操作。
我认为使用 SIMD 将图像转换为灰度的一个好主意是使用此算法。
fY(R, G, B) = R x 0.29891 + G x 0.58661 + B x 0.11448
我有以下代码,现在它只是将图像分解为通道并将其打包在一起以作为src 向量返回。现在我需要将其设为灰度 :)src 变量是指向 unsigned char 数组的指针。
__m128i vecSrc = _mm_loadu_si128((__m128i*) &src[srcIndex]);
__m128i maskR = _mm_setr_epi16(1, 0, 0, 0, 1, 0, 0, 0);
__m128i maskG = _mm_setr_epi16(0, 1, 0, 0, 0, 1, 0, 0);
__m128i maskB = _mm_setr_epi16(0, 0, 1, 0, 0, 0, 1, 0);
__m128i maskA = _mm_setr_epi16(0, 0, 0, 1, 0, 0, 0, 1);
// Creating factors.
const __m128i factorR = _mm_set1_epi16((short)(0.29891 * 0x10000)); //8 coefficients - R scale factor.
const __m128i factorG = _mm_set1_epi16((short)(0.58661 * 0x10000)); //8 coefficients - G scale factor.
const __m128i factorB = _mm_set1_epi16((short)(0.11448 * 0x10000)); //8 coefficients - B scale factor.
__m128i zero = _mm_setzero_si128();
// Shifting higher part of src register to lower.
__m128i vectSrcLowInHighPart = _mm_cvtepu8_epi16(vecSrc);
__m128i vectSrcHighInHighPart = _mm_unpackhi_epi8(vecSrc, zero);
// Multiply high parts of 16 x uint8 vectors by channels masks and save lower half. Getting each channels separatly (in two parts H and L)
__m128i vecR_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskR);
__m128i vecG_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskG);
__m128i vecB_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskB);
__m128i vecA_L = _mm_mullo_epi16(vectSrcLowInHighPart, maskA);
// Multiply lower parts of 16 x uint8 vectors by channels masks and save lower half.
__m128i vecR_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskR);
__m128i vecG_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskG);
__m128i vecB_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskB);
__m128i vecA_H = _mm_mullo_epi16(vectSrcHighInHighPart, maskA);
// Lower and high masks using to packing.
__m128i maskLo = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14, 12, 10, 8, 6, 4, 2, 0);
__m128i maskHi = _mm_set_epi8(14, 12, 10, 8, 6, 4, 2, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
// Packed the High and Lowe part of register into one 16 x 8bit registers of each channels.
__m128i R = _mm_or_si128(_mm_shuffle_epi8(vecR_L, maskLo), _mm_shuffle_epi8(vecR_H, maskHi));
__m128i G = _mm_or_si128(_mm_shuffle_epi8(vecG_L, maskLo), _mm_shuffle_epi8(vecG_H, maskHi));
__m128i B = _mm_or_si128(_mm_shuffle_epi8(vecB_L, maskLo), _mm_shuffle_epi8(vecB_H, maskHi));
__m128i A = _mm_or_si128(_mm_shuffle_epi8(vecA_L, maskLo), _mm_shuffle_epi8(vecA_H, maskHi));
// Added all sub vectors to get in result one 128-bit vector with all edited channels.
__m128i resultVect = _mm_add_epi8(_mm_add_epi8(R, G), _mm_add_epi8(B, A));
// Put result vector into array to return as src pointer.
_mm_storel_epi64((__m128i*)&src[srcIndex], resultVect);
感谢您的帮助!这是我的第一个带有 SIMD (SSE) 指令的程序。
【问题讨论】:
-
所以基本上你想从存储在 simd 寄存器中的 RGBA 中获取灰度?
-
是的,然后用参数中的 src 数组将其拆分。
-
您不需要将整数向量转换为浮点向量、相乘并转换回来吗?浏览一下内在函数指南,
__mm_cvtepi32_ps()可能很有用。 -
坦克@Shawn 支持!你能写一些如何使用
__mm_cvtepi32_ps()来实现你的想法的例子吗? -
为了澄清:1)您的实际输入是具有 8 位无符号值的交错 RGBA 像素? (您可能不需要完全去交错它们)。 2)你想要什么输出格式? (我猜又是
uint8的流?或者你真的想要float?) 3)从你写的内容来看,你似乎对16位定点乘法没问题(这真的应该足够了)。您也可以使用 8 位定点乘法吗? 4)你可以使用什么SSE版本(SSSE3会添加pmaddubsw,这会很有帮助)