需求1:在高帧率场景下,一般拿到的是bayer格式数据。图像处理时,一般会先插值成rgb,再拆分为单通道。如果可以直接bayer中抽出r、g、b,那效率将大大提升。
需求2:抽取的单通道直接是镜像的
注意:抽取后r、g、b尺寸是原来的一半,没有做插值(插值只会让数据量变大,并没有引入有效信息)
效果:CPU指令集优化后,速度是传统算法的8倍左右。
应用举例:
#include<opencv.hpp> #include <Windows.h> int main() { cv::Mat img_bayerRG = cv::imread("1.bmp", 0); //单通道图像读取(1.bmp是bayerRG格式存储的单通道图像) const uint8_t *bayer = img_bayerRG.data; //指向bayerRG数据 int height = img_bayerRG.rows; int width = img_bayerRG.cols; uint8_t *r = new uint8_t[width*height / 4]; //抽完后尺寸为原来的1/2 uint8_t *g = new uint8_t[width*height / 4]; //g做特殊处理,2个g的均值合成1个g uint8_t *b = new uint8_t[width*height / 4]; LARGE_INTEGER nEndTime, nBeginTime, nFreq; double time; QueryPerformanceFrequency(&nFreq); QueryPerformanceCounter(&nBeginTime);//获取开始时刻计数值 for (int i = 0; i < 100; i++) { bayer2rgb_CPU(bayer, width, height, BayerFormat::bayerRG,Mirror::mirrorTB, r, g, b); } QueryPerformanceCounter(&nEndTime);//获取开始时刻计数值 time = (double)(nEndTime.QuadPart - nBeginTime.QuadPart) * 1000 / (double)nFreq.QuadPart;//ms(开始-停止)/频率即为秒数,精确到小数点后6位 printf("100次bayer2rgb耗时(ms): %f \n\n", time); cv::Mat R = cv::Mat(height / 2, width / 2, CV_8U, r); cv::Mat B = cv::Mat(height / 2, width / 2, CV_8U, b); cv::Mat G = cv::Mat(height / 2, width / 2, CV_8U, g); cv::waitKey(100000); delete[] r; delete[] g; delete[] b; return 0; }
函数封装:
#include <intrin.h> enum BayerFormat { bayerRG, bayerGR, bayerBG, bayerGB }; enum Mirror { mirrorNo, //不镜像 mirrorTB, //上下镜像 mirrorLR, //左右镜像 mirrorAll //全镜像 }; //使用要求,assert((nWidth % 32 == 0) && (nHeight % 2) == 0); void bayer2rgb_CPU(const unsigned char* pBayer, int nWidth,int nHeight,BayerFormat nBayerFormat,Mirror nMirror, const unsigned char* pR, const unsigned char* pG, const unsigned char* pB) { __m256i shuffle_oe = _mm256_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15, 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15); __m128i shuffle_reserseOrder = _mm_setr_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); //用于左右镜像 int index = 0; for (int row2 = 0; row2 < nHeight / 2; row2++) { for (int col32 = 0; col32 < nWidth / 32; col32++) { __m256i line1 = _mm256_load_si256((__m256i*)(pBayer + nWidth*row2 * 2) + col32); __m256i line2 = _mm256_load_si256((__m256i*)(pBayer + nWidth*(row2 * 2 + 1)) + col32); __m256i line1_128oe = _mm256_shuffle_epi8(line1, shuffle_oe); //前16字节与后16字节是分开处理的,得到:前16字节的奇数位元素A、前16字节的偶数位元素B、后16字节的奇数位元素C、后16字节的偶数位元素D __m256i line2_128oe = _mm256_shuffle_epi8(line2, shuffle_oe); __m256i line1_oe = _mm256_permute4x64_epi64(line1_128oe, 0b11011000);//将ABCD重排,得到ACBD,即32字节里所有奇数位元素E、所有偶数位元素F __m256i line2_oe = _mm256_permute4x64_epi64(line2_128oe, 0b11011000); __m128i line11 = _mm256_extracti128_si256(line1_oe, 0); //得到EF中的E __m128i line12 = _mm256_extracti128_si256(line1_oe, 1); //得到EF中的F __m128i line21 = _mm256_extracti128_si256(line2_oe, 0); __m128i line22 = _mm256_extracti128_si256(line2_oe, 1); switch (nMirror) { case mirrorNo: index = nWidth / 32 * row2 + col32; //不镜像 break; case mirrorTB: index = nWidth / 32 * (nHeight / 2 - 1 - row2) + col32; //上下镜像 break; case mirrorLR: index = nWidth / 32 * row2 + (nWidth / 32 - 1 - col32); //左右镜像 line11 = _mm_shuffle_epi8(line11, shuffle_reserseOrder); line12 = _mm_shuffle_epi8(line12, shuffle_reserseOrder); line21 = _mm_shuffle_epi8(line21, shuffle_reserseOrder); line22 = _mm_shuffle_epi8(line22, shuffle_reserseOrder); break; case mirrorAll: index = nWidth / 32 * (nHeight / 2 - 1 - row2) + (nWidth / 32 - 1 - col32); line11 = _mm_shuffle_epi8(line11, shuffle_reserseOrder); line12 = _mm_shuffle_epi8(line12, shuffle_reserseOrder); line21 = _mm_shuffle_epi8(line21, shuffle_reserseOrder); line22 = _mm_shuffle_epi8(line22, shuffle_reserseOrder); break; default: break; } switch (nBayerFormat) { case bayerRG: _mm_storeu_si128((__m128i*)pR + index, line11); _mm_storeu_si128((__m128i*)pB + index, line22); _mm_storeu_si128((__m128i*)pG + index, _mm_avg_epu8(line12, line21));//对g通道求均值 break; case bayerGR: _mm_storeu_si128((__m128i*)pR + index, line12); _mm_storeu_si128((__m128i*)pB + index, line21); _mm_storeu_si128((__m128i*)pG + index, _mm_avg_epu8(line11, line22));//对g通道求均值 break; case bayerBG: _mm_storeu_si128((__m128i*)pR + index, line22); _mm_storeu_si128((__m128i*)pB + index, line11); _mm_storeu_si128((__m128i*)pG + index, _mm_avg_epu8(line12, line21));//对g通道求均值 break; case bayerGB: _mm_storeu_si128((__m128i*)pR + index, line21); _mm_storeu_si128((__m128i*)pB + index, line12); _mm_storeu_si128((__m128i*)pG + index, _mm_avg_epu8(line11, line22));//对g通道求均值 break; default: break; } } } }
不含镜像版本参考 CPU指令集——bayer抽取r、g、b三通道 - 夕西行 - 博客园 (cnblogs.com)
标签:__,index,storeu,shuffle,mm,bayer,指令集,m128i,CPU From: https://www.cnblogs.com/xixixing/p/18254669