使用SSE2(作为浮点)缩放字节像素值(y = ax + b)? [英] Scaling byte pixel values (y=ax+b) with SSE2 (as floats)?

查看:1030
本文介绍了使用SSE2(作为浮点)缩放字节像素值(y = ax + b)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想计算 y = ax + b ,其中x和y是像素值[即,值范围的字节为0〜255] c $ c> a 和 b 是一个浮动



这个公式对于图像中的每个像素,此外,a和b对于不同的像素是不同的。在C ++中的直接计算很慢,所以我很有兴趣知道c ++中的sse2指令。



搜索后,我发现float中的乘法和加法sse2就像 _mm_mul_ps _mm_add_ps 。但是首先我需要将字节中的x转换为float(4字节)。



问题是,在从字节数据源 _mm_load_si128 ),如何将数据从字节转换为浮点型?

解决方案

<对于每个像素,p> a b 是不同的?



有没有办法可以有效地生成 a

code>和 b 在向量中,作为定点或浮点?如果不是,插入4个FP值或8个16位整数可能比标量操作更糟。






/ h2>

如果 a b 或者首先使用定点生成,这可能是定点数学的一个很好的用例。 (即表示值* 2 ^ scale的整数)。 SSE / AVX没有8b * 8b-> 16b乘;最小的元素是字,所以你必须解压缩字节到字,但不是一直到32位。



有一个 _mm_maddubs_epi16 指令,如果 b a 更改不够频繁,或者您可以轻松生成交替a * 2 ^ 4和b * 2的向量^ 1个字节。显然,它是真正的方便的双线性插值法,但它仍然得到

  float a,b;如果我们可以准备一个a和b向量, 
const int logascalale = 4,logbscale = 1;
const int ascale = 1<< logascale; // fixed point scale for a:2 ^ 4
const int bscale = 1<< logbscale; // b的定点标度:2 ^ 1

const __m128i brescale = _mm_set1_epi8(1 <(logascale-logbscale)); //重新缩放b以匹配16位临时结果中的a

for(i = 0; i // __ m128i avec = get_scaled_a ;
// __ m128i bvec = get_scaled_b(i);
// __ m128i ab_lo = _mm_unpacklo_epi8(avec,bvec);
// __ m128i ab_hi = _mm_unpackhi_epi8(avec,bvec);

__m128i abvec = _mm_set1_epi16(((int8_t)(bscale * b)<< 8)|(int8_t)(ascale * a) //整数提升规则可能在这里的错误的地方做符号扩展,所以如果你实际写这样的方式检查这个。

__m128i block = _mm_load_si128(& buf [i]); // call this {v [0] .. v [15]}

__m128i lo = _mm_unpacklo_epi8(block,brescale); // {v [0],8,v [1],8,...}
__m128i hi = _mm_unpackhi_epi8(block,brescale); // {v [8],8,v [9],8,...
lo = _mm_maddubs_epi16(lo,abvec); // first arg is unsigned bytes,2nd arg is signed bytes
hi = _mm_maddubs_epi16(hi,abvec);
// lo = {v [0] *(2 ^ 4 * a)+ 8 *(2 ^ 1 * b),...}

lo = _mm_srli_epi16 logascale); // truncated from scaled fixed-point to integer
hi = _mm_srli_epi16(hi,logascale);

//并重新打包。逻辑,不是算术右移位,不能设置符号位
block = _mm_packuswb(lo,hi);
_mm_store_si128(& buf [i],block);
}
//然后标量清理循环

2 ^ 4是任意选择。它为 a 的整数部分和4个小数位留下3个非符号位。因此,它有效地将 a 舍入到最接近的16,如果它的幅度大于8和15 / 16th,则溢出。 2 ^ 6将给出更多的小数位,并允许从<-2到+1和63/64>的 a



因为 b 正被添加,而不是相乘,所以它的有效范围要大得多,而小数部分的有用性要小得多。为了以8位来表示它,将其四舍五入到最接近的一半仍然保留一些小数信息,但允许它[-64:63.5]没有溢出。



为了更精确,16b定点是一个不错的选择。你可以缩放 a b 向上乘以2 ^ 7或某事,以具有分数精度的7b,整数部分为[-256..255]。这种情况下没有乘法和加法指令,所以你必须单独做。用于执行乘法的好选项包括:




  • _mm_mulhi_epu16 :unsigned 16b * 16b- > high16(bits [31:16])。有用的 a 不能为负

  • _mm_mulhi_epi16 :signed 16b * 16b-> high16(bits [31:16])。

  • _mm_mulhrs_epi16 :signed 16b * 16b-> bits [30:15 ]的32b临时,有舍入。有一个很好的缩放因子为 a ,这应该更好。根据我的理解,SSSE3为这种使用引入了这个指令。

  • _mm_mullo_epi16 :signed 16b * 16b-> low16位[15:0])。这只允许在$ 16结果溢出之前 a 的8个有效位,所以我想你在 _mm_maddubs_epi16 8bit解决方案对于 b 更精确。



获得 a b 值的缩放16b向量,然后:



查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆