利用SSE优化图像转置

平时我们做图像处理或者视频处理, 很多地方会用到矩阵转置:

比如: DCT变换, 图像旋转, 图像滤波, 以及一些数据的内存行和列的交换等, 会大量使用转置这个动作.

然而由于数据量很大,处理速度很慢!如何来提高处理速度呢?

下面看看分析: 

HEVC中有个地方是如下这样实现(直接行和列对应的位置交换):

  1. Pel  tmp;  
  2. for (k=0;k<blkSize-1;k++)  
  3. {  
  4.   for (l=k+1;l<blkSize;l++)  
  5.   {  
  6.     tmp                 = pDst[k*dstStride+l];  
  7.     pDst[k*dstStride+l] = pDst[l*dstStride+k];  
  8.     pDst[l*dstStride+k] = tmp;  
  9.   }  
  10. }  


 

如何用汇编来实现呢?

我们先用SSE汇编来实现一个8X8的矩阵转置吧: 这里输入地址pSrc_128[i] 和输出地址pDst_128[i]可以相同也可以不同:

相同的话就是原地转置, 不同的话就是非原地转置.

  1.   __m128i* m_pSrc_tmp = pSrc_128[i];  
  2.   __m128i* m_pDst_tmp = pDst_128[i];  
  3.   __m128i Org_8_0,Org_8_1, Org_8_2, Org_8_3;  
  4.   __m128i tttt1,tttt2,tttt3,tttt4,tttt33,tttt44;  
  5.   __m128i tttt5,tttt6, tttt7, tttt8;  
  6.   int stride_ii = dstStride>>3;  
  7. //one  
  8.   Org_8_0 = _mm_load_si128(m_pSrc_tmp);  
  9.   m_pSrc_tmp+=8;  
  10.   Org_8_1 = _mm_load_si128(m_pSrc_tmp);  
  11.   m_pSrc_tmp+=8;  
  12.   Org_8_2 = _mm_load_si128(m_pSrc_tmp);  
  13.   m_pSrc_tmp+=8;  
  14.   Org_8_3 = _mm_load_si128(m_pSrc_tmp);  
  15.   m_pSrc_tmp+=8;  
  16.   
  17.   tttt1 = _mm_unpacklo_epi16(Org_8_0, Org_8_1);  
  18.   tttt2 = _mm_unpacklo_epi16(Org_8_2, Org_8_3);  
  19.   tttt3 = _mm_unpackhi_epi16(Org_8_0, Org_8_1);  
  20.   tttt4 = _mm_unpackhi_epi16(Org_8_2, Org_8_3);  
  21.   
  22.   tttt5 = _mm_unpacklo_epi32(tttt1, tttt2);  
  23.   tttt6 = _mm_unpackhi_epi32(tttt1, tttt2);  
  24.   
  25.   Org_8_0 = _mm_load_si128(m_pSrc_tmp);  
  26.   m_pSrc_tmp+=8;;   
  27.   Org_8_1 = _mm_load_si128(m_pSrc_tmp);  
  28.   m_pSrc_tmp+=8;  
  29.   Org_8_2 = _mm_load_si128(m_pSrc_tmp);  
  30.   m_pSrc_tmp+=8;   
  31.   Org_8_3 = _mm_load_si128(m_pSrc_tmp);  
  32.   //m_pSrc_tmp+=8;  
  33.   
  34.   tttt1 = _mm_unpacklo_epi16(Org_8_0, Org_8_1);  
  35.   tttt2 = _mm_unpacklo_epi16(Org_8_2, Org_8_3);  
  36.   tttt33 = _mm_unpackhi_epi16(Org_8_0, Org_8_1);  
  37.   tttt44 = _mm_unpackhi_epi16(Org_8_2, Org_8_3);  
  38.   
  39.   tttt7 = _mm_unpacklo_epi32(tttt1, tttt2);  
  40.   tttt8 = _mm_unpackhi_epi32(tttt1, tttt2);  
  41.   
  42.   tttt1 = _mm_unpacklo_epi64(tttt5, tttt7);  
  43.   tttt2 = _mm_unpackhi_epi64(tttt5, tttt7);  
  44.   _mm_storeu_si128(m_pDst_tmp, tttt1);  
  45.   m_pDst_tmp+=stride_ii;  
  46.   _mm_storeu_si128(m_pDst_tmp, tttt2);  
  47.   m_pDst_tmp+=stride_ii;  
  48.   tttt5 = _mm_unpacklo_epi64(tttt6, tttt8);  
  49.   tttt7 = _mm_unpackhi_epi64(tttt6, tttt8);  
  50.   _mm_storeu_si128(m_pDst_tmp, tttt5);  
  51.   m_pDst_tmp+=stride_ii;  
  52.   _mm_storeu_si128(m_pDst_tmp, tttt7);  
  53.   m_pDst_tmp+=stride_ii;  
  54.   
  55. //tow  
  56.   tttt5 = _mm_unpacklo_epi32(tttt3, tttt4);  
  57.   tttt6 = _mm_unpackhi_epi32(tttt3, tttt4);  
  58.   
  59.   tttt7 = _mm_unpacklo_epi32(tttt33, tttt44);  
  60.   tttt8 = _mm_unpackhi_epi32(tttt33, tttt44);  
  61.   
  62.   tttt1 = _mm_unpacklo_epi64(tttt5, tttt7);  
  63.   tttt2 = _mm_unpackhi_epi64(tttt5, tttt7);  
  64.   _mm_storeu_si128(m_pDst_tmp, tttt1);  
  65.   m_pDst_tmp+=stride_ii;  
  66.   _mm_storeu_si128(m_pDst_tmp, tttt2);  
  67.   m_pDst_tmp+=stride_ii;  
  68.   tttt5 = _mm_unpacklo_epi64(tttt6, tttt8);  
  69.   tttt7 = _mm_unpackhi_epi64(tttt6, tttt8);  
  70.   _mm_storeu_si128(m_pDst_tmp, tttt5);  
  71.   m_pDst_tmp+=stride_ii;  
  72.   _mm_storeu_si128(m_pDst_tmp, tttt7);  


要实现的是NXN的转置,如何实现呢:

基于8X8来实现NXN的块或者图像的转置:

这里先把NXN划分为size_case 个8X8, 然后循环调用8X8的转置!

 

  1.  __m128i* pDst_128[64];  
  2.  __m128i* pSrc_128[64];  
  3.  int size_case = (blkSize>>3);  
  4.  dstStride = dstStride_tmp;  
  5.  for(int y = 0; y<size_case; y++)//对所有8x8的块进行地址映射  
  6.   for(int x = 0; x<size_case; x++)  
  7.   {  
  8.    pSrc_128[y*size_case + x] = (__m128i*)(pDst + 8*x + y*8*64);  
  9.    pDst_128[y*size_case + x] = (__m128i*)(rpDst + 8*y + x*8*dstStride);  
  10.   }  
  11.   
  12.  size_case = size_case*size_case;  
  13.  for(int  i = 0;i <size_case; i++)//开始转置  
  14.  {  
  15.   
  16.     8x8转置的代码:  
  17.   
  18. }  

通过比较, 用SSE汇编优化实现转置比用纯 C代码实现的转置速度快5倍左右! 

 

同样在ARM cortext上的汇编优化也是基于这个原理:

主要循环体代码如下:

  1. VTRN.16 q8, q9  
  2. VTRN.16 q10, q11  
  3. VTRN.16 q4, q5  
  4. VTRN.16 q6, q7  
  5. VTRN.32 q8, q10  


感兴趣的可以自己调试下!

 

当然DSP上也是同样的方法, 只是涉及到的指令不同而已!


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值