簡體   English   中英

如何使用 x86 內在函數進行灰度 -> RGB 像素復制

[英]How to do grayscale -> RGB pixel replication with x86 intrinsics

所以假設我有以下代碼示例,如下:

   /* replicate the single-channel gray value into 3 RGB pixel values (R=G=B) */
    auto outPtr = convertedImageBuffer->data();
    auto inPtr = *m_img;

    for (auto p : inPtr)
    {
        outPtr[0] = p;
        outPtr[1] = p;
        outPtr[2] = p;

        outPtr += 3;
    }

m_img 是一個包含std::vector<uint8_t>shared_ptr “outPtr”是一個原始字節指針,它也源自另一個托管字節緩沖區。 “inPtr”指向一個線性緩沖區,它是一個 8 位灰度圖像。

for 循環確保 outPtr 中的線性字節緩沖區具有來自 inPtr[] 的輸入像素重復 3 次。 當然,這種方式對我來說似乎效率很低。

我將如何使用 x86 內在函數來做到這一點?

pshufb指令非常適合這項任務。 一次讀取 16 字節並使用 48 字節表中的三個查找表,然后可以在一次迭代中 output 48 字節。 為什么是 48 字節? 加載和存儲可能是對齊的,不會浪費 SIMD 寄存器中的單個通道/計算資源。

 // initialise once outside a loop
 alignas(16) const uint8_t lut[48]= { 0,0,0, 1,1,1, 2,2,2, ..., 15,15,15 };
 auto const lut0 = _mm_load_si128(lut);
 auto const lut1 = _mm_load_si128(lut + 16);
 auto const lut2 = _mm_load_si128(lut + 32);
     
 // then loop over the data
 auto data = _mm_load_si128(input_ptr);
 auto out0 = _mm_shuffle_epi8(data, lut0);
 auto out1 = _mm_shuffle_epi8(data, lut1);
 auto out2 = _mm_shuffle_epi8(data, lut2);
 _mm_store_si128(output_ptr, out0);
 _mm_store_si128(output_ptr + 16, out1);
 _mm_store_si128(output_ptr + 32, out2);

如果您的輸入 / output 數據可能不是 16 字節對齊的,請使用_mm_loadu_si128 / _mm_storeu_si128

SSSE3 pshufb ( _mm_shuffle_epi8 ) 似乎是通往 go 的方式。 使用其中的低 5 個字節加載 8 個字節。 執行 16 字節存儲,將 output 指針增加 15(因此重疊 1)。

盡早停止以避免讀取和/或寫入超出 arrays 的末端,執行最后一個元素標量。 (假設您的問題大小總是足以容納超過 1 個完整向量,您可以做一個最終向量,直到最后一個元素。)

或者使用 3 個不同的 shuffle-control 向量加載 16 字節會更好; 正如 Aki 的回答所示,允許充分利用每次加載和存儲中的所有數據,沒有重疊。 (48 字節 = LCM(3, 16))。


如果您不能假設 SSSE3,那么使用 SIMD 會更不方便,並且可能不值得這樣做。 標量x * 0x01010101將一個字節廣播到 4 個字節,重疊的 4 字節存儲將比 3 個單獨的字節存儲更有效。 (除非您想使用 GNU C typedef uint32_t aliasing_u32 __attribute__((aligned(1),may_alias)) ,否則將memset(dst, tmp, 4)或其他東西用於未對齊的可能別名存儲。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM