利用 AVX 向量化技术优化的中值滤波
代码和注释如下:
void medianFilterAVX(int height, int width, unsigned char *__restrict src, unsigned char *__restrict dst) { // 注意是从 1 开始 for(int i = 1; i < height - 1; i++) { int j; // 4字节*8 = 32 for(j = 1; j < width - 1 - 32; j += 32) { __m256i a[9]; // 3*3 的领域 a[0] = _mm256_loadu_si256((__m256i *)(src + i * width + j)); // 非对齐模式加载8 个 int 型 a[1] = _mm256_loadu_si256((__m256i *)(src + i * width + j + 1)); a[2] = _mm256_loadu_si256((__m256i *)(src + i * width + j - 1)); a[3] = _mm256_loadu_si256((__m256i *)(src + (i + 1) * width + j)); a[4] = _mm256_loadu_si256((__m256i *)(src + (i + 1) * width + j + 1)); a[5] = _mm256_loadu_si256((__m256i *)(src + (i + 1) * width + j - 1)); a[6] = _mm256_loadu_si256((__m256i *)(src + (i - 1) * width + j)); a[7] = _mm256_loadu_si256((__m256i *)(src + (i - 1) * width + j + 1)); a[8] = _mm256_loadu_si256((__m256i *)(src + (i - 1) * width + j - 1)); // 因为只要知道中值就好(a[4]) for( int ji = 0; ji < 5; ji++) { for( int k = ji + 1; k < 9; k++) { // 逐字节的比较(32个字节) __m256i large = _mm256_max_epu8(a[ji], a[k]); __m256i small = _mm256_min_epu8(a[ji], a[k]); a[ji] = small; a[k] = large; } } _mm256_storeu_si256((__m256i *)(dst + i * width + j), a[4]);// 非对齐模式存储8 个 int 型 } // 不能被 32 整除的部分 for(int je = j; je < width - 1; je++) { unsigned char a[9]; a[0] = src[i * width + je]; a[1] = src[i * width + je + 1]; a[2] = src[i * width + je - 1]; a[3] = src[(i + 1) * width + je]; a[4] = src[(i + 1) * width + je + 1]; a[5] = src[(i + 1) * width + je - 1]; a[6] = src[(i - 1) * width + je]; a[7] = src[(i - 1) * width + je + 1]; a[8] = src[(i - 1) * width + je - 1]; for( int ji = 0; ji < 5; ji++) { for( int jj = ji + 1; jj < 9; jj++) { unsigned char large = std::max<unsigned char>(a[ji], a[jj]); unsigned char small = std::min<unsigned char>(a[ji], a[jj]); a[ji] = small; a[jj] = large; } } dst[i * width + je] = a[4]; } } // 边界处理:直接拷贝 for( int i = 0; i < width; i++) { dst[i] = src[i]; dst[(height - 1)*width + i] = src[(height - 1) * width + i]; } for(int i = 0; i < height; i++) { dst[i * width] = src[i * width]; dst[i * width + width - 1] = src[i * width + width - 1]; } }
入口函数:
cv::Mat input = imread("micky.png", cv::IMREAD_GRAYSCALE); cv::Mat scale; cv::resize(input, scale, cv::Size(3200, 3200));// 为了使得优化效果更明显 int width = scale.cols; int height = scale.rows; cv::Mat output = cv::Mat(cv::Size(width, height), CV_8UC1, cv::Scalar(0)); Timer timer; //meanFilter(scale.data, output.data, width, height); medianFilter(height, width, scale.data, output.data); timer.rlog("时间"); timer.restart(); medianFilterAVX(height, width, scale.data, output.data); timer.rlog("时间");
和串行版本的比较结果如下:提升了将近 19 倍