利用neon技術對矩陣旋轉進行加速


一般的矩陣旋轉操作都是對矩陣中的元素逐個操作,假設矩陣大小為m*n,那么時間復雜度就是o(mn)。如果使用了arm公司提供的neon加速技術,則可以並行的讀取多個元素,對多個元素進行操作,雖然時間復雜度還是o(mn),但是常數因子會變小,並且在寄存器里的操作比在普通內存中還要快一些,所以會帶來一定的性能提升。

 

在實際應用中,我需要對一個矩陣進行順時針旋轉90度,網上這方面的資料很少,於是自己研究了一下,利用neon給出的一些加速指令,設計了一個簡單的neon矩陣旋轉算法。

 

1.目標:將輸入矩陣順時針旋轉90度,如下圖所示:

   輸入矩陣                                      輸出矩陣

                                

 

2.一般做法:

一般的做法是,將輸入矩陣中的每個元素,根據旋轉的角度,計算出其在旋轉后矩陣中的位置,並填充該值。

 

3.利用NEON的做法:

考慮將一個矩陣划分成若干子矩陣,例如:一個128×256大小的矩陣可以划分為16×32個8×8大小的矩陣。分別對每個8x8的子矩陣進行旋轉,再將其復制到輸出矩陣中正確的坐標上即可。可以總結為2步:

循環執行以下步驟,直到所有子矩陣均被處理過

1.旋轉當前子矩陣

2.將旋轉后的子矩陣復制到輸出矩陣中

 

其中最關鍵的就是第一步,詳細講一下利用neon技術如何做到:

以byte數組為例(因為android中獲取的yuv數據就是byte型,將一個矩形按行連成了一個大一維數組),假設圖像的長寬都可以被8整除。首先利用2個uint8x8x4_t型數組,將8×8大小的子矩陣讀入

            mat1.val[0]=vld1_u8(srcImg+i*width+j);
            mat1.val[1]=vld1_u8(srcImg+(i+1)*width+j);
            mat1.val[2]=vld1_u8(srcImg+(i+2)*width+j);
            mat1.val[3]=vld1_u8(srcImg+(i+3)*width+j);
            mat2.val[0]=vld1_u8(srcImg+(i+4)*width+j);
            mat2.val[1]=vld1_u8(srcImg+(i+5)*width+j);
            mat2.val[2]=vld1_u8(srcImg+(i+6)*width+j);
            mat2.val[3]=vld1_u8(srcImg+(i+7)*width+j);

接着,對兩兩相鄰的寄存器做基於uint8_t類型的專置操作,即:mat1和mat2中的0和1,2和3寄存器分別做轉置,得到4個uint8x8x2_t類型數組

vtrn操作示意圖如下:

            temp1=vtrn_u8(mat1.val[1],mat1.val[0]);
            temp2=vtrn_u8(mat1.val[3],mat1.val[2]);
            temp3=vtrn_u8(mat2.val[1],mat2.val[0]);
            temp4=vtrn_u8(mat2.val[3],mat2.val[2]);

注意,vtrn_8里面兩個寄存器的順序不能顛倒

然后,對這四個數組的類型進行轉換,將uint8x8_t轉換成uint16x4_t

 

            temp5.val[0]= vreinterpret_u16_u8(temp1.val[0]);
            temp5.val[1]= vreinterpret_u16_u8(temp1.val[1]);
            temp6.val[0]= vreinterpret_u16_u8(temp2.val[0]);
            temp6.val[1]= vreinterpret_u16_u8(temp2.val[1]);
            temp7.val[0]= vreinterpret_u16_u8(temp3.val[0]);
            temp7.val[1]= vreinterpret_u16_u8(temp3.val[1]);
            temp8.val[0]= vreinterpret_u16_u8(temp4.val[0]);
            temp8.val[1]= vreinterpret_u16_u8(temp4.val[1]);

接下來的做法和上面的這一套很像,繼續對這些uint16x4_t數據進行轉置,這次的順序和上次有所不同,相鄰的奇偶序號寄存器之間進行專置即:0和2,1和3,4和6,5和7

            temp9=vtrn_u16(temp6.val[0],temp5.val[0]);
            temp10=vtrn_u16(temp6.val[1],temp5.val[1]);
            temp11=vtrn_u16(temp8.val[0],temp7.val[0]);
            temp12=vtrn_u16(temp8.val[1],temp7.val[1]);

然后,繼續對這四個數組的類型進行轉換,將uint16x4_t轉換成uint32x2_t

            temp13.val[0]= vreinterpret_u32_u16(temp9.val[0]);
            temp13.val[1]= vreinterpret_u32_u16(temp9.val[1]);
            temp14.val[0]= vreinterpret_u32_u16(temp10.val[0]);
            temp14.val[1]= vreinterpret_u32_u16(temp10.val[1]);
            temp15.val[0]= vreinterpret_u32_u16(temp11.val[0]);
            temp15.val[1]= vreinterpret_u32_u16(temp11.val[1]);
            temp16.val[0]= vreinterpret_u32_u16(temp12.val[0]);
            temp16.val[1]= vreinterpret_u32_u16(temp12.val[1]);

最后,再做一次基於uint32x2_t的轉置

            temp17=vtrn_u32(temp15.val[0],temp13.val[0]);
            temp18=vtrn_u32(temp15.val[1],temp13.val[1]);
            temp19=vtrn_u32(temp16.val[0],temp14.val[0]);
            temp20=vtrn_u32(temp16.val[1],temp14.val[1]);

最后的最后,還需要對各個寄存器中存儲的值重新排列一遍,並轉換回最初的uint8x8_t

            temp1.val[0]= vreinterpret_u8_u32(temp17.val[0]);
            temp1.val[1]= vreinterpret_u8_u32(temp19.val[0]);
            temp2.val[0]= vreinterpret_u8_u32(temp18.val[0]);
            temp2.val[1]= vreinterpret_u8_u32(temp20.val[0]);
            temp3.val[0]= vreinterpret_u8_u32(temp17.val[1]);
            temp3.val[1]= vreinterpret_u8_u32(temp19.val[1]);
            temp4.val[0]= vreinterpret_u8_u32(temp18.val[1]);
            temp4.val[1]= vreinterpret_u8_u32(temp20.val[1]);

大功告成!此時的子矩陣已經被順時針旋轉了90度,接下來,只要將其復制到輸出矩陣的正確位置即可。

 

幾點說明

1.為什么這么做可以旋轉矩陣:

NEON提供的專置函數相當於對2×2的小矩陣進行專置,因此利用這個特性,可以對更大的矩陣進行旋轉。其實自己按照我說的步驟,畫個矩陣,自己做一下,就明白了。也不一定用8×8的,4×4的就能說明問題,當然4×4比8×8的要簡單。

2.怎樣得到正確位置:

這個還是自己思考一下吧,不難,假設某元素在輸入矩陣的位置是(i,j),那么在輸出的旋轉90度的矩陣中的位置和i,j是相關的。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM