NEON 指令集並行技術優化彩色影象轉灰度圖【Android】
阿新 • • 發佈:2018-12-22
參考原文:
目前市面上主流的旗艦android手機搭載的Soc都是64位的CPU,常見的armv7指令集的公版架構如Cortex-A8,Cortex-A9,Cortex-A15,常見的armv8指令集的公版架構如Cortex-A53,Cortex-A57,Cortex-A72,Cortex-A73。arm架構的CPU從armv7a開始已經支援neon(可選項),從而實現平行計算功能。armv8a還具備32個128neon暫存器,並且支援雙精度浮點數。
下面為我新增的程式碼註釋 :
void method_argb2gray_neon(AndroidBitmapInfo info, void *pixels) { // Gray = (R*38 + G*75 + B*15) >> 7 TickMeter tm3; tm3.start(); unsigned short *dst = (unsigned short *) pixels; unsigned char *src = (unsigned char *) pixels; uint8x8_t r = vdup_n_u8(38); // 將一個標量擴充套件城向量 8 bit * 8 uint8x8_t g = vdup_n_u8(75); uint8x8_t b = vdup_n_u8(15); uint16x8_t alp = vdupq_n_u16(255 << 8); uint16x8_t temp; uint8x8_t gray; uint8x8x4_t argb; uint16x8_t high; uint16x8_t low; uint16x8x2_t res; int i, size = info.height * info.width / 8; // 暫不考慮不能被 8 整除的情況 for (i = 0; i < size; ++i) { // 獲取r、g、b值,計算灰度值 argb = vld4_u8(src); // 從記憶體中載入資料(以 AOS 儲存)到 4 個向量(8 bit * 8)中(SOA) temp = vmull_u8(argb.val[1], r); // vmul 指令 temp = vmlal_u8(temp, argb.val[2], g); // vmla 指令 temp = vmlal_u8(temp, argb.val[3], b); gray = vshrn_n_u16(temp, 7); // vshrn_n_u16 會在做右移 7 位的同時將2位元組無符號型轉成1位元組無符號型 src += 8 * 4; // src 移到下個位置 // 賦值 4 通道argb // 先用 vmovl_u8 (長指令)符號擴充套件或零擴充套件雙字向量中的每個元素到其原始長度的兩倍 // vqmovn_* 為窄指令 high = vorrq_u16(alp, vmovl_u8(gray)); // 再用 vorrq_u16 按位或運算 low = vorrq_u16(vshlq_n_u16(vmovl_u8(gray), 8), vmovl_u8(gray)); // 注意這裡是左移 res = vzipq_u16(low, high); // 兩個向量的元素交錯打包成一個新向量 vst1q_u16(dst, res.val[0]); // 儲存一個向量(res 的前半部分)到記憶體(16bit*8) dst += 8; vst1q_u16(dst, res.val[1]); dst += 8; } tm3.stop(); LOGI("method_argb2gray_neon time: %lf", tm3.getTimeMilli()); LOGI(" \n"); }
值得注意的是:Android 的 Bitmap 預設是按 ARGB 順序儲存的。 vzipq_u16 函式執行的結果如下:
可以看出 res[0] 和 res[1] 兩個向量分別由 low, hight 兩個向量一前一後交錯打包而成。
程式執行效果如下:
最後記得將 Build Variants 設定為 release(預設為 debug)