Closed
Description
UP主你好,我是在从事 CV 和生物交叉的研究生,做性能优化时关注到你的B站教程,非常受用。(还有就是莫顿序列,最近也准备搞起)。视频我都看完了,这次想请教SIMD。SIMD 是针对浮点运算,但是对图像处理不太友好。图像是(R、G、B) 排布的,例如Opencv定义的 为HxWx3 的numpy 矩阵或C++数组。别扭的点在于:
-
uint8 不是SIMD 标准的数据单元。
-
3个uint8, 位宽为24 (3x8),或更宽 96 (3x32)。不是64, 128, 256 寄存器能处理的。
问题1: 请问对于这种图像 RGB24 数据,行内是怎样做加速?
问题2:代码请指导
我有个操作需要在原始图片上进行mask抠图。背景的区域替换为0。如下面 python 代码,但是性能特别差。所以用 SIMD 优化了一下,这个代码还很粗糙,只适配了图像的一个通道。还没与numpy对比过性能差异。
img[mask==0] = 0 #python numpy,方法1, 性能特别差
img = img * mask[..., None] #python numpy,方法2, 性能也差
用 c++ 的for循环做这件事情,收益也很小。所以在尝试 SIMD 。
//g++ -mavx2 fast_simd.cpp
#include <iostream>
#include <immintrin.h>
void vectorAnd(uint8_t* a, uint8_t* b, uint8_t* result, int size) {
int width = 256/8; // =32
uint8_t (* vectorA_)[width] = reinterpret_cast<uint8_t(*)[width]>(a);
uint8_t (* vectorB_)[width] = reinterpret_cast<uint8_t(*)[width]>(b);
uint8_t (* vectorResult_)[width] = reinterpret_cast<uint8_t(*)[width]>(result);
// 每次处理 8 个元素,
int batch = size * 8 / 256;
__m256i one = _mm256_set1_epi8(1);
__m256i xor_mask = _mm256_set1_epi8(0xff);
for (int i = 0; i < batch; i++) {
// 使用 AVX2 指令进行矢量操作。因为没有 int8 乘法,只能用多步骤的位操作符代替
__m256i vec_a = _mm256_loadu_si256((__m256i_u*) vectorA_[i]);
__m256i vec_b = _mm256_loadu_si256((__m256i_u*) vectorB_[i]);
__m256i b1 = _mm256_sub_epi8(vec_b, one);
__m256i b2 = _mm256_xor_si256(b1, xor_mask); //vec_b '0'-> b2 0; vec_b '1' -> b2 0xFF
__m256i resultVector = _mm256_and_si256(vec_a, b2);
// 将结果存储到结果数组中
_mm256_storeu_si256((__m256i_u*) vectorResult_[i], resultVector);
}
// 处理剩余的不足 ? 个元素的情况
int remainingElements = size * 8 % 256;
for (int i = 0; i < remainingElements; i++) {
// result[numVectors * 8 + i] = a[numVectors * 8 + i] + b[numVectors * 8 + i];
}
}
int main(){
const int size = 64;
uint8_t img[size] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64};
uint8_t mask[size] = {1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0,
1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0,
1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0};
uint8_t result[size] = {0};
std::cout << "pre" << std::endl;
for (int i = 0; i < size; i++) {
std::cout << (int)result[i] << " ";
}
std::cout << std::endl;
std::cout << "\npost" << std::endl;
vectorAnd(img, mask, result, size);
for (int i = 0; i < size; i++) {
std::cout << (int)result[i] << " ";
}
}
Metadata
Metadata
Assignees
Labels
No labels