首页 > 其他分享 >CPU指令集——bayer抽取r、g、b三通道(含镜像)

CPU指令集——bayer抽取r、g、b三通道(含镜像)

时间:2024-06-18 16:42:50浏览次数:22  
标签:__ index storeu shuffle mm bayer 指令集 m128i CPU

需求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

相关文章

  • ARMv7 寄存器 工作模式 和指令集 和 堆栈回溯
    因此,在图4-1中,如果处理器是在IRQ模式,我们可以看见R0,R1...R12(与在用户模式看到的相同的寄存器),加上SP_IRQ和LR_IRQ(仅在IRQ模式中可以访问的寄存器)和R15(程序计数器,PC)。我们通常不必指定模式中的寄存器名。如果我们在一行代码中引用R13,处理器会访问当前模式对应的SP寄存器。......
  • CPU指令集——bayer抽取r、g、b三通道
    需求:在高帧率场景下,一般拿到的是bayer格式数据。图像处理时,一般会先插值成rgb,再拆分为单通道。如果可以直接bayer中抽出r、g、b,那效率将大大提升。注意:抽取后r、g、b尺寸是原来的一半,没有做插值(插值只会让数据量变大,并没有引入有效信息)效果:CPU指令集优化后,速度是传统算法的8倍左......
  • 获取Windows设备CPUID信息
    CPUID目录简介代码实现基于C/C++的实现基于C#的代码实现基于Python的代码实现简介CPUID是一个CPU(中央处理器)的指令,用于获取处理器的详细信息。它通常用于操作系统、虚拟机监控程序(如虚拟机管理程序或超级监视器)、调试程序和性能分析工具等,来获取关于处理器硬件......
  • CPU指令集——VS打断点时注意事项
    在看内存中数据时,VS2015打断点碰到了数据读入不正确的问题uint8_tuint8_array[32]={00,07,04,04,02,03,06,02,02,05,04,02,06,05,04,03,00,07,04,05,00,02,00,03,04,05,02,02,04,03,04,06};__m256iresult=_mm256_loadu_si256((__m256i*......
  • CPU利用率和负载
    CPU利用率显示的是程序在运行期间实时占用的CPU百分比;cpu使用率反映的是当前cpu的繁忙程度,忽高忽低的原因在于占用cpu处理时间的进程可能处于io等待状态但却还未释放进入wait。CPU负载是指某段时间内占用cpu时间的进程和等待cpu时间的进程数,这里等待cpu时间的进程是指等待被唤醒......
  • CPU 时间片轮转机制
    CPU时间片轮转机制我们平时在开发的时候,感觉并没有受cpu核心数的限制,想启动线程就启动线程,哪怕是在单核CPU上,为什么?这是因为操作系统提供了一种CPU时间片轮转机制。时间片轮转调度是一种最古老、最简单、最公平且使用最广的算法,又称RR调度。每个进程被分配一个时间段,......
  • CPU 核心数和线程数的关系
    CPU核心数和线程数的关系多核心:也指单芯片多处理器(ChipMultiprocessors,简称CMP),CMP是由美国斯坦福大学提出的,其思想是将大规模并行处理器中的SMP(对称多处理器)集成到同一芯片内,各个处理器并行执行不同的进程。这种依靠多个CPU同时并行地运行程序是实现超高速计算......
  • CPU、核心,进程、线程,串行、并发、并行之间的关系
    转发:https://blog.csdn.net/menglei16/article/details/1075945741、计算机硬件基本组成一个计算机(冯·诺依曼结构)【主要】硬件组成:主板:是“交通枢纽”,各个部件工作的所在平台,它负责将各个部件紧密连接在一起,各部件通过主板进行数据传输CPU:中央处理器(CentralProcessingUnit......
  • 【服务器硬件由 CPU、RAM、硬盘等组成,选购时需考虑应用需求、预算等。散热、安全、监
    本人详解作者:王文峰,参加过CSDN2020年度博客之星,《Java王大师王天师》公众号:JAVA开发王大师,专注于天道酬勤的Java开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯山峯转载说明:务必注明来源(注明:作者:王文峰哦)【服务器硬件......
  • CPU指令集——获取数组的所有奇数位、所有偶数位
    为抽取bayer格式图像的r\g\b做准备#include<iostream>#include<intrin.h>intmain(){uint8_tuint8_array[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};//内存顺序012__m128ia=_mm_load_si128((__m128i*)uint8_array);......