采用ARM NEON内在cvtColor的SIMD优化 [英] SIMD optimization of cvtColor using ARM NEON intrinsics

查看:3831
本文介绍了采用ARM NEON内在cvtColor的SIMD优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的工作BGR的SIMD优化,灰度转换相当于的 OpenCV中的 cvtColor()功能。有此功能的英特尔SSE版本,我指的是它。 (我在做什么,基本上转换SSE code到NEON code)

我已经差不多写完code,并能与G ++编译它,但我不能得到正确的输出。没有人有任何想法的错误可能是什么?

什么我收到(不正确):

我应该得到什么:

下面是我的code:

 的#include< OpenCV的/ cv.hpp>
#包括LT&; OpenCV的/ highgui.h>
#包括LT&;&arm_neon.h GT;
//#包括LT&;&iostream的GT;使用命名空间std;
使用命名空间CV //;的#define int8x16_to_8x8x2(ⅴ)((int8x8x2_t){vget_low_s8(v)中,vget_high_s8(ⅴ)})无效cvtBGR2GrayNEON(CV ::垫&放大器; SRC,CV ::垫&安培; DEST)
{
  。const int的大小= src.size()区域()* src.channels();
  UCHAR * S = src.ptr&所述; UCHAR>(0);
  UCHAR * D = dest.ptr&所述; UCHAR>(0);  常量int8x16_t掩码1 = {} 0,3,6,9,12,15,1,4,7,10,13,2,5,8,11,14;
  常量int8x16_t smask1 = {} 6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15;
  常量int8x16_t ssmask1 = {} 11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10;  常量int8x16_t MASK2 = {0,3,6,9,12,15,2,5,8,11,14,1,4,7,10,13};
  常量int8x16_t ssmask2 = {} 0,1,2,3,4,11,12,13,14,15,5,6,7,8,9,10;  常量int8x16_t bmask1 = {} 255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0;
  常量int8x16_t bmask2 = {} 255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0;
  常量int8x16_t bmask3 = {} 255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0;
  常量int8x16_t bmask4 = {} 255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0;  const int的转变= 8;
  const int的放大器= 1<<移位;  常量int16_t _R_ =(int16_t)(AMP * 0.299);
  常量int16_t _G_ =(int16_t)(AMP * 0.587);
  常量int16_t _B_ =(int16_t)(AMP * 0.114);
  常量int16x8_t R = vdupq_n_s16(_R_);
  常量int16x8_t G = vdupq_n_s16(_G_);
  常量int16x8_t B = vdupq_n_s16(_B_);
  常量int8x16_t零= vdupq_n_s8(0);  的for(int i = 0; I<大小; I + = 48)
    {
      int8x16_t A = vld1q_s8((*中int8_t)S + I);
      int8x16_t B = vld1q_s8((*中int8_t)S + 1 + 16);
      int8x16_t C = vld1q_s8((*中int8_t)S + 1 + 32);      A = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a),vget_low_s8(mask1)),vtbl2_s8(int8x16_to_8x8x2(a),vget_high_s8(mask1)));
      B = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(B),vget_low_s8(MASK2)),vtbl2_s8(int8x16_to_8x8x2(B),vget_high_s8(MASK2)));
      C = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(C),vget_low_s8(MASK2)),vtbl2_s8(int8x16_to_8x8x2(C),vget_high_s8(MASK2)));      // BBBBBB
      常量int8x16_t AAAA = vbslq_s8(C,vbslq_s8(B,A,bmask1),bmask2);      一个= vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a)中,vget_low_s8(smask1)),vtbl2_s8(int8x16_to_8x8x2(a)中,vget_high_s8(smask1)));
      B = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(B),vget_low_s8(smask1)),vtbl2_s8(int8x16_to_8x8x2(B),vget_high_s8(smask1)));
      C = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(C),vget_low_s8(smask1)),vtbl2_s8(int8x16_to_8x8x2(C),vget_high_s8(smask1)));      // GGGGGG
      常量int8x16_t BBBB = vbslq_s8(C,vbslq_s8(B,A,bmask3),bmask2);      一个= vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a)中,vget_low_s8(ssmask1)),vtbl2_s8(int8x16_to_8x8x2(a)中,vget_high_s8(ssmask1)));
      C = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(C),vget_low_s8(ssmask1)),vtbl2_s8(int8x16_to_8x8x2(C),vget_high_s8(ssmask1)));
      B = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(B),vget_low_s8(ssmask2)),vtbl2_s8(int8x16_to_8x8x2(B),vget_high_s8(ssmask2)));      // RRRRRR
      常量int8x16_t CCCC = vbslq_s8(C,vbslq_s8(B,A,bmask3),bmask4);      / *
      int8x8x2_t A1 = vzip_s8(vget_high_s8(AAAA),vget_high_s8(零));
      int8x8x2_t A2 = vzip_s8(vget_low_s8(AAAA),vget_low_s8(零));
      * /      int8x16_t A1 = AAAA;
      int8x16_t A2 =零;
      int8x16x2_t的temp1 = vzipq_s8(A1,A2);
      A1 = temp1.val [0];
      A2 = temp1.val [1];
      int16x8_t AA1 = vmulq_s16((int16x8_t)A,B);
      int16x8_t AA2 = vmulq_s16((int16x8_t)A1,B);      int8x16_t B1 = BBBB;
      int8x16_t B2 =零;
      int8x16x2_t TEMP2 = vzipq_s8(B1,B2);
      B1 = temp2.​​val [0];
      B2 = temp2.​​val [1];
      int16x8_t BB1 = vmulq_s16((int16x8_t)B2,G);
      int16x8_t BB2 = vmulq_s16((int16x8_t)B1,G);      int8x16_t C1 = CCCC;
      int8x16_t C2 =零;
      int8x16x2_t TEMP3 = vzipq_s8(C1,C2);
      C1 = temp3.val [0];
      C2 = temp3.val [1];
      int16x8_t CC1 = vmulq_s16((int16x8_t)C2,R);
      int16x8_t CC2 = vmulq_s16((int16x8_t)C1,R);      AA1 = vaddq_s16(AA1,BB1);
      AA1 = vaddq_s16(AA1,CC1);
      AA2 = vaddq_s16(AA2,BB2);
      AA2 = vaddq_s16(AA2,CC2);      const int的SHIFT1 = 8;
      AA1 = vshrq_n_s16(AA1,SHIFT1);
      AA2 = vshrq_n_s16(AA2,SHIFT1);      uint8x8_t AAA1 = vqmovun_s16(AA1);
      uint8x8_t aaa2 = vqmovun_s16(AA2);      uint8x16_t结果= vcombine_u8(AAA1,aaa2);      vst1q_u8((uint8_t有*)(D),结果);      D + = 16;
    }
}诠释的main()
{
  CV ::垫SRC = CV :: imread(Lenna.bmp);
  CV ::垫DEST(src.rows,src.cols,CV_8UC1);  cvtBGR2GrayNEON(SRC,DEST);  CV :: imwrite(grey.jpg,DEST);  返回0;
}

下面是等价SSE code(从这里

 无效cvtBGR2GraySSEShort(垫&放大器; SRC,垫&安培; DEST)
{
    。const int的大小= src.size()区域()* src.channels();
    UCHAR * S = src.ptr&所述; UCHAR>(0);
    UCHAR * D = dest.ptr&所述; UCHAR>(0);    //数据结构
    // BGR BGR BGR BGR BGR乙
    // GR BGR BGR BGR BGR BG
    // - [R BGR BGR BGR BGR BGR
    //洗牌BBBBBBGGGGGRRRRR
    常量__m128i掩码1 = _mm_setr_epi8(0,3,6,9,12,15,1,4,7,10,13,2,5,8,11,14);
    常量__m128i smask1 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15);
    常量__m128i ssmask1 = _mm_setr_epi8(11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10);    //洗牌GGGGGGBBBBBRRRRR
    常量__m128i MASK2 = _mm_setr_epi8(0,3,6,9,12,15,2,5,8,11,14,1,4,7,10,13);
    //常量__m128i smask2 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15);同smask1
    常量__m128i ssmask2 = _mm_setr_epi8(0,1,2,3,4,11,12,13,14,15,5,6,7,8,9,10);    //洗牌RRRRRRGGGGGBBBBB
    // __ m128i MASK3 = _mm_setr_epi8(0,3,6,9,12,15,2,5,8,11,14,1,4,7,10,13); //一样MASK2
    //常量__m128i smask3 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,6,7,8,9,10); //相同smask1
    //常量__m128i ssmask3 = _mm_setr_epi8(11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10); //相同ssmask1    //混合面膜
    常量__m128i bmask1 = _mm_setr_epi8
        (255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0);    常量__m128i bmask2 = _mm_setr_epi8
        (255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0);    常量__m128i bmask3 = _mm_setr_epi8
        (255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0);    常量__m128i bmask4 = _mm_setr_epi8
        (255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0);    const int的转变= 8;
    const int的放大器= 1<<移位;
    const int的_R _ =(INT)(AMP * 0.299);
    const int的_G _ =(INT)(AMP * 0.587);
    const int的_B _ =(INT)(AMP * 0.114);
    常量__m128i R = _mm_set1_epi16(_R_);
    常量__m128i G = _mm_set1_epi16(_G_);
    常量__m128i B = _mm_set1_epi16(_B_);
    常量__m128i零= _mm_setzero_si128();    的for(int i = 0; I<大小; I + = 48)
    {
        __m128i A = _mm_shuffle_epi8(_mm_load_si128((__ m128i *)(S + I)),MASK1);
        __m128i B = _mm_shuffle_epi8(_mm_load_si128((__ m128i *)(S + I + 16)),MASK2);
        __m128i C = _mm_shuffle_epi8(_mm_load_si128((__ m128i *)(S + I + 32)),MASK2);
        常量__m128i AAAA = _mm_blendv_epi8(C,_mm_blendv_epi8(B,A,bmask1),bmask2);        一个= _mm_shuffle_epi8(一,smask1);
        B = _mm_shuffle_epi8(B,smask1);
        C = _mm_shuffle_epi8(C,smask1);
        常量__m128i BBBB = _mm_blendv_epi8(C,_mm_blendv_epi8(B,A,bmask3),bmask2);        一个= _mm_shuffle_epi8(一,ssmask1);
        C = _mm_shuffle_epi8(C,ssmask1);
        B = _mm_shuffle_epi8(B,ssmask2);
        常量__m128i CCCC = _mm_blendv_epi8(C,_mm_blendv_epi8(B,A,bmask3),bmask4);        __m128i A1 = _mm_unpackhi_epi8(AAAA,零);
        __m128i A2 = _mm_unpacklo_epi8(AAAA,零);
        A1 = _mm_mullo_epi16(A1,B);
        A2 = _mm_mullo_epi16(A2,B);
        __m128i B1 = _mm_unpackhi_epi8(BBBB,零);
        __m128i B2 = _mm_unpacklo_epi8(BBBB,零);
        B1 = _mm_mullo_epi16(B1,G);
        B2 = _mm_mullo_epi16(B2,G);        __m128i C1 = _mm_unpackhi_epi8(CCCC,零);
        __m128i C2 = _mm_unpacklo_epi8(CCCC,零);
        C1 = _mm_mullo_epi16(C1,R);
        C2 = _mm_mullo_epi16(C2,R);        A1 = _mm_add_epi16(A1,B1);
        A1 = _mm_add_epi16(A1,C1);
        A2 = _mm_add_epi16(A2,B2);
        A2 = _mm_add_epi16(A2,C2);        A1 = _mm_srli_epi16(a1,8);
        A2 = _mm_srli_epi16(a2,8);        一个= _mm_packus_epi16(A1,A2);        _mm_stream_si128((__ m128i *)(D),A);
        D + = 16;
    }
}


解决方案

好吧,下面是这个函数我刚写的一个完全优化的版本(请注意,这个函数只是返回如果大小比32小)。

  / *
 *创建于:2014年7 27。
 *作者:李杰克
 *项目FANIC - 最快的ARM NEON Implementaion挑战
 * ///无效fanicCvtBGR2GrayNEON(无效* pDst,无效* PSRC,无符号整型的大小);
// Y = 0.114 * B + 0.587 * G + 0.299 * R
    。文本
    。臂
    。全球fanicCvtBGR2GrayNEON    pDst名.req R0
    PSRC名.req R1
    尺寸名.req R2    .align伪5
    .FUNC
fanicCvtBGR2GrayNEON:
    PLD [PSRC]
    潜艇的尺寸,大小,#32
    PLD [PSRC,#64]
    bxmi LR
    PLD [PSRC,#64 * 2]
    vmov.i8 D0,#29
    vmov.i8 D1,#150
    vmov.i8 D2,#77    .align伪5
1:
    vld3.8 {D20,D21,D22},[PSRC]!
    vld3.8 {D23,D24,D25},[PSRC]!
    vld3.8 {D26,D27,D28},[PSRC]!
    vld3.8 {D29,D30,D31},[PSRC]!    vmull.u8 Q8,D20,D0
    vmlal.u8 Q8,D21,D1
    vmlal.u8 Q8,D22,D2
    vmull.u8 Q9,D23,D0
    vmlal.u8 Q9,D24,D1
    vmlal.u8 Q9,D25,D2
    vmull.u8 Q10,D26,D0
    vmlal.u8 Q10,D27,D1
    vmlal.u8 Q10,D28,D2
    vmull.u8 Q11,D29,D0
    vmlal.u8 Q11,D30,D1
    vmlal.u8 Q11,D31,D2    vrshrn.u16 D24,Q8,#8
    vrshrn.u16 D25,Q9,#8
    vrshrn.u16 D26,Q10,#8
    vrshrn.u16 D27,Q11,#8    潜艇的尺寸,大小,#32
    PLD [PSRC,#64 * 3]
    PLD [PSRC,#64 * 4]    vst1.8 {Q12,Q13},[pDst]!
    BPL 1B    CMP大小,-32#
    添加PSRC,PSRC,大小
    bxle LR
    添加PSRC,PSRC,大小,LSL#1
    添加pDst,pDst,大小
    b 1B    .endfunc
    。结束

正如你所看到的,它是如此的装配比内在更容易和更短的写作NEON codeS,尽管沉重的展开。

玩得开心。

I'm working on a SIMD optimization of BGR to grayscale conversion which is equivalent to OpenCV's cvtColor() function. There is an Intel SSE version of this function and I'm referring to it. (What I'm doing is basically converting SSE code to NEON code.)

I've almost finished writing the code, and can compile it with g++, but I can't get the proper output. Does anyone have any ideas what the error could be?

What I'm getting (incorrect):

What I should be getting:

Here's my code:

#include <opencv/cv.hpp>
#include <opencv/highgui.h>
#include <arm_neon.h>
//#include <iostream>

using namespace std;
//using namespace cv;

#define int8x16_to_8x8x2(v) ((int8x8x2_t) { vget_low_s8(v), vget_high_s8(v) })

void cvtBGR2GrayNEON(cv::Mat& src, cv::Mat& dest)
{
  const int size = src.size().area()*src.channels();
  uchar* s = src.ptr<uchar>(0);
  uchar* d = dest.ptr<uchar>(0);

  const int8x16_t mask1 = {0,3,6,9,12,15,1,4,7,10,13,2,5,8,11,14};
  const int8x16_t smask1 = {6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15};
  const int8x16_t ssmask1 = {11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10};

  const int8x16_t mask2 = {0,3,6,9,12,15, 2,5,8,11,14,1,4,7,10,13};
  const int8x16_t ssmask2 = {0,1,2,3,4,11,12,13,14,15,5,6,7,8,9,10};

  const int8x16_t bmask1 = {255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0};
  const int8x16_t bmask2 = {255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0};
  const int8x16_t bmask3 = {255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0};
  const int8x16_t bmask4 = {255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0};

  const int shift = 8;
  const int amp = 1<<shift;

  const int16_t _R_ = (int16_t)(amp*0.299);
  const int16_t _G_ = (int16_t)(amp*0.587);
  const int16_t _B_ = (int16_t)(amp*0.114);
  const int16x8_t R = vdupq_n_s16(_R_);
  const int16x8_t G = vdupq_n_s16(_G_);
  const int16x8_t B = vdupq_n_s16(_B_);
  const int8x16_t zero = vdupq_n_s8(0);

  for(int i = 0; i < size; i += 48)
    {
      int8x16_t a = vld1q_s8((int8_t *) s + i);
      int8x16_t b = vld1q_s8((int8_t *) s + i + 16);
      int8x16_t c = vld1q_s8((int8_t *) s + i + 32);

      a = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a),vget_low_s8(mask1)),vtbl2_s8(int8x16_to_8x8x2(a),vget_high_s8(mask1)));
      b = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(b), vget_low_s8(mask2)), vtbl2_s8(int8x16_to_8x8x2(b), vget_high_s8(mask2)));
      c = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(c), vget_low_s8(mask2)), vtbl2_s8(int8x16_to_8x8x2(c), vget_high_s8(mask2)));

      //BBBBBB
      const int8x16_t aaaa = vbslq_s8(c, vbslq_s8(b, a, bmask1), bmask2);

      a = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a), vget_low_s8(smask1)), vtbl2_s8(int8x16_to_8x8x2(a), vget_high_s8(smask1)));
      b = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(b), vget_low_s8(smask1)), vtbl2_s8(int8x16_to_8x8x2(b), vget_high_s8(smask1)));
      c = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(c), vget_low_s8(smask1)), vtbl2_s8(int8x16_to_8x8x2(c), vget_high_s8(smask1)));

      //GGGGGG
      const int8x16_t bbbb = vbslq_s8(c, vbslq_s8(b, a, bmask3), bmask2);

      a = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(a), vget_low_s8(ssmask1)), vtbl2_s8(int8x16_to_8x8x2(a), vget_high_s8(ssmask1)));
      c = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(c), vget_low_s8(ssmask1)), vtbl2_s8(int8x16_to_8x8x2(c), vget_high_s8(ssmask1)));
      b = vcombine_s8(vtbl2_s8(int8x16_to_8x8x2(b), vget_low_s8(ssmask2)), vtbl2_s8(int8x16_to_8x8x2(b), vget_high_s8(ssmask2)));

      //RRRRRR
      const int8x16_t cccc = vbslq_s8(c, vbslq_s8(b, a, bmask3), bmask4);

      /*
      int8x8x2_t a1 = vzip_s8(vget_high_s8(aaaa), vget_high_s8(zero));
      int8x8x2_t a2 = vzip_s8(vget_low_s8(aaaa), vget_low_s8(zero));
      */

      int8x16_t a1 = aaaa;
      int8x16_t a2 = zero;
      int8x16x2_t temp1 =  vzipq_s8(a1, a2);
      a1 = temp1.val[0];
      a2 = temp1.val[1];
      int16x8_t aa1 = vmulq_s16((int16x8_t)a2, B);
      int16x8_t aa2 = vmulq_s16((int16x8_t)a1, B);

      int8x16_t b1 = bbbb;
      int8x16_t b2 = zero;
      int8x16x2_t temp2 =  vzipq_s8(b1, b2);
      b1 = temp2.val[0];
      b2 = temp2.val[1];
      int16x8_t bb1 = vmulq_s16((int16x8_t)b2, G);
      int16x8_t bb2 = vmulq_s16((int16x8_t)b1, G);

      int8x16_t c1 = cccc;
      int8x16_t c2 = zero;
      int8x16x2_t temp3 =  vzipq_s8(c1, c2);
      c1 = temp3.val[0];
      c2 = temp3.val[1];
      int16x8_t cc1 = vmulq_s16((int16x8_t)c2, R);
      int16x8_t cc2 = vmulq_s16((int16x8_t)c1, R);

      aa1 = vaddq_s16(aa1, bb1);
      aa1 = vaddq_s16(aa1, cc1);
      aa2 = vaddq_s16(aa2, bb2);
      aa2 = vaddq_s16(aa2, cc2);

      const int shift1 = 8;
      aa1 = vshrq_n_s16(aa1, shift1);
      aa2 = vshrq_n_s16(aa2, shift1);

      uint8x8_t aaa1 = vqmovun_s16(aa1);
      uint8x8_t aaa2 = vqmovun_s16(aa2);

      uint8x16_t result = vcombine_u8(aaa1, aaa2);

      vst1q_u8((uint8_t *)(d), result);

      d+=16;
    }    
}

int main() 
{
  cv::Mat src = cv::imread("Lenna.bmp");
  cv::Mat dest(src.rows, src.cols, CV_8UC1);

  cvtBGR2GrayNEON(src, dest);

  cv::imwrite("grey.jpg", dest);

  return 0;
}

Here is equivalent SSE code (from here):

void cvtBGR2GraySSEShort(Mat& src, Mat& dest)
{
    const int size = src.size().area()*src.channels();
    uchar* s = src.ptr<uchar>(0);
    uchar* d = dest.ptr<uchar>(0);

    //data structure
    //BGR BGR BGR BGR BGR B
    //GR BGR BGR BGR BGR BG
    //R BGR BGR BGR BGR BGR
    //shuffle to BBBBBBGGGGGRRRRR
    const __m128i mask1 = _mm_setr_epi8(0,3,6,9,12,15,1,4,7,10,13,2,5,8,11,14);
    const __m128i smask1 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15);
    const __m128i ssmask1 = _mm_setr_epi8(11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10);

    //shuffle to GGGGGGBBBBBRRRRR
    const __m128i mask2 = _mm_setr_epi8(0,3,6,9,12,15, 2,5,8,11,14,1,4,7,10,13);
    //const __m128i smask2 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,11,12,13,14,15);same as smask1
    const __m128i ssmask2 = _mm_setr_epi8(0,1,2,3,4,11,12,13,14,15,5,6,7,8,9,10);

    //shuffle to RRRRRRGGGGGBBBBB
    //__m128i mask3 = _mm_setr_epi8(0,3,6,9,12,15, 2,5,8,11,14,1,4,7,10,13);//same as mask2
    //const __m128i smask3 = _mm_setr_epi8(6,7,8,9,10,0,1,2,3,4,5,6,7,8,9,10);//same as smask1
    //const __m128i ssmask3 = _mm_setr_epi8(11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10);//same as ssmask1

    //blend mask
    const __m128i bmask1 = _mm_setr_epi8
        (255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0);

    const __m128i bmask2 = _mm_setr_epi8
        (255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0);

    const __m128i bmask3 = _mm_setr_epi8
        (255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0);

    const __m128i bmask4 = _mm_setr_epi8
        (255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0);  

    const int shift = 8;
    const int amp = 1<<shift;
    const int _R_=(int)(amp*0.299);
    const int _G_=(int)(amp*0.587);
    const int _B_=(int)(amp*0.114);
    const __m128i R = _mm_set1_epi16(_R_);
    const __m128i G = _mm_set1_epi16(_G_);
    const __m128i B = _mm_set1_epi16(_B_);
    const __m128i zero = _mm_setzero_si128();   

    for(int i=0;i<size;i+=48)
    {
        __m128i a = _mm_shuffle_epi8(_mm_load_si128((__m128i*)(s+i)),mask1);
        __m128i b = _mm_shuffle_epi8(_mm_load_si128((__m128i*)(s+i+16)),mask2);
        __m128i c = _mm_shuffle_epi8(_mm_load_si128((__m128i*)(s+i+32)),mask2);
        const __m128i aaaa = _mm_blendv_epi8(c,_mm_blendv_epi8(b,a,bmask1),bmask2);

        a = _mm_shuffle_epi8(a,smask1);
        b = _mm_shuffle_epi8(b,smask1);
        c = _mm_shuffle_epi8(c,smask1);
        const __m128i bbbb =_mm_blendv_epi8(c,_mm_blendv_epi8(b,a,bmask3),bmask2);

        a = _mm_shuffle_epi8(a,ssmask1);
        c = _mm_shuffle_epi8(c,ssmask1);
        b = _mm_shuffle_epi8(b,ssmask2);
        const __m128i cccc =_mm_blendv_epi8(c,_mm_blendv_epi8(b,a,bmask3),bmask4);

        __m128i a1 = _mm_unpackhi_epi8(aaaa,zero);
        __m128i a2 = _mm_unpacklo_epi8(aaaa,zero);
        a1 = _mm_mullo_epi16(a1,B);
        a2 = _mm_mullo_epi16(a2,B);
        __m128i b1 = _mm_unpackhi_epi8(bbbb,zero);
        __m128i b2 = _mm_unpacklo_epi8(bbbb,zero);
        b1 = _mm_mullo_epi16(b1,G);
        b2 = _mm_mullo_epi16(b2,G);

        __m128i c1 = _mm_unpackhi_epi8(cccc,zero);
        __m128i c2 = _mm_unpacklo_epi8(cccc,zero);
        c1 = _mm_mullo_epi16(c1,R);
        c2 = _mm_mullo_epi16(c2,R);

        a1 = _mm_add_epi16(a1,b1);
        a1 = _mm_add_epi16(a1,c1);
        a2 = _mm_add_epi16(a2,b2);
        a2 = _mm_add_epi16(a2,c2);

        a1 = _mm_srli_epi16(a1,8);
        a2 = _mm_srli_epi16(a2,8);

        a = _mm_packus_epi16(a1,a2);

        _mm_stream_si128((__m128i*)(d),a);
        d+=16;
    } 
}

解决方案

Ok, below is a FULLY OPTIMIZED version of that function I just wrote (Beware that this function simply returns if size is smaller than 32.)

/*
 *  Created on: 2014. 7. 27.
 *      Author: Jake Lee
 *      Project FANIC - Fastest ARM NEON Implementaion Challenge
 */

// void fanicCvtBGR2GrayNEON(void *pDst, void *pSrc, unsigned int size);
// Y = 0.114*B + 0.587*G + 0.299*R
    .text
    .arm
    .global fanicCvtBGR2GrayNEON

    pDst    .req    r0
    pSrc    .req    r1
    size    .req    r2

    .align 5
    .func
fanicCvtBGR2GrayNEON:
    pld     [pSrc]
    subs    size, size, #32
    pld     [pSrc, #64]
    bxmi    lr
    pld     [pSrc, #64*2]
    vmov.i8     d0, #29
    vmov.i8     d1, #150
    vmov.i8     d2, #77

    .align 5
1:
    vld3.8      {d20, d21, d22}, [pSrc]!
    vld3.8      {d23, d24, d25}, [pSrc]!
    vld3.8      {d26, d27, d28}, [pSrc]!
    vld3.8      {d29, d30, d31}, [pSrc]!

    vmull.u8    q8, d20, d0
    vmlal.u8    q8, d21, d1
    vmlal.u8    q8, d22, d2
    vmull.u8    q9, d23, d0
    vmlal.u8    q9, d24, d1
    vmlal.u8    q9, d25, d2
    vmull.u8    q10, d26, d0
    vmlal.u8    q10, d27, d1
    vmlal.u8    q10, d28, d2
    vmull.u8    q11, d29, d0
    vmlal.u8    q11, d30, d1
    vmlal.u8    q11, d31, d2

    vrshrn.u16  d24, q8, #8
    vrshrn.u16  d25, q9, #8
    vrshrn.u16  d26, q10, #8
    vrshrn.u16  d27, q11, #8

    subs    size, size, #32
    pld     [pSrc, #64*3]
    pld     [pSrc, #64*4]

    vst1.8      {q12, q13}, [pDst]!
    bpl     1b

    cmp     size, #-32
    add     pSrc, pSrc, size
    bxle    lr
    add     pSrc, pSrc, size, lsl #1
    add     pDst, pDst, size
    b       1b

    .endfunc
    .end

As you can see, it's so much easier and shorter writing NEON codes in assembly than in intrinsics despite the heavy unrolling.

Have fun.

这篇关于采用ARM NEON内在cvtColor的SIMD优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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