由于芯片CAMERALINK输入RawData的Bayer格式转RGB功能收行缓存大小的限制,最大只支持4K模式下进行Bayer格式转RGB功能。所以相关的转换功能需要使用CPU或者GPU进行转换。下面介绍一下相关的格式的原理和转换程序编写方法。

图像格式和滤光片间的关系

对应很多CAMERALINK或普通相机COMS光原件本身只是对一个一个点对光强弱状态进行量化,并不会颜色进行区分。达到区分颜色的方式是使用滤光片产生对颜色的区分。类似下图经过滤光片后CMOS每个点对应的光强度就是对应颜色的光强度。

下图简单说明颜色设置和图像输出格式的关系

工业相机常用的格式说明:

Bayer转RGB算法

bayer一般使用插值算法转换成RGB格式

要将图像从bayer格式转换为 RGB 格式,我们需要插入两种缺失的颜色每个像素的值。几种标准插值方法(最近邻、线性、三次、三次样条、等)

  1. Bayer format to RGB
    将Bayer Pattern的格式转换为RGB,那就需要通过插值的方式将每个像素点中丢失的两个颜色找回来。有几种插值的方式可以使用,但是最常用的方法是线性插值的修正调节版本。


右边的rgb都是从左边的RGB通过公式转换出来的,不同的算法他们的对应关系会有不同。在程序中对对应关系进行解读。

简化版本的转换程序序分析

下面就是simple版本的转换代码,由于程序支持多种排列模式:RGGB , GBRG,GRBG,BGGR。由于模式太多不利于分析程序,我们认为前面的bayer图片按RGGB模式保存为文件,文件结构如下:
以设定为RGGB模式,一个简单的转换分析代码。
转换结果关系分析:

int bayer_Simple(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile)
{const int bayerStep = sx;const int rgbStep = 3 * sx;//rgb 一个像素的字节数int width = sx;int height = sy;//判读输入格式排列为BGGR  GBRG  blue =-1 其他 blue =1 int blue = tile == DC1394_COLOR_FILTER_BGGR //RGGB模式下该值为1|| tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1;//判读输入格式排列为GBRG  GRBG  start_with_green =1 ,其他 start_with_green =0int start_with_green = tile == DC1394_COLOR_FILTER_GBRG//RGGB模式下该值为0|| tile == DC1394_COLOR_FILTER_GRBG;int i, imax, iinc;if ((tile>DC1394_COLOR_FILTER_MAX)||(tile<DC1394_COLOR_FILTER_MIN))//判读tile 范围return DC1394_INVALID_COLOR_FILTER;/* add black border *///最右列和最底的行由于算法原因计算得出的值无法插值,使用黑色替代。imax = sx * sy * 3;for (i = sx * (sy - 1) * 3; i < imax; i++) {rgb[i] = 0;}iinc = (sx - 1) * 3;for (i = (sx - 1) * 3; i < imax; i += iinc) {rgb[i++] = 0;rgb[i++] = 0;rgb[i++] = 0;}rgb += 1;//将RGB指针先移到Gwidth -= 1;//宽有填0所以直接宽度减少1height -= 1;//高有填0所以直接宽度减少1//bayerfor (; height--; bayer += bayerStep, rgb += rgbStep) {//循环一次计算一行const uint8_t *bayerEnd = bayer + width;//bayer一行的最后// RGGB模式下0、2、4偶数行为红色先出blue=1,start_with_green=0    ,1、3、4 奇数行为blue=-1,start_with_green=1if (start_with_green) {//RGGB模式偶数行不运行rgb[-blue] = bayer[1];//RGGB模式保存蓝rgb[0] = (bayer[0] + bayer[bayerStep + 1] + 1) >> 1;//计算绿色的值rgb[blue] = bayer[bayerStep];//RGGB模式保存红bayer++;//bayer指向下个字节,RGGB模式指向的是蓝色rgb += 3;}//在本程序模式下,中间的红色点和蓝色点都会被两次使用if (blue > 0) {//本行红色在蓝色前出现,RGGB模式下0、2、4偶数行运行for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) {rgb[-1] = bayer[0];//保存红色   rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1;//计算绿色的平均值rgb[1] = bayer[bayerStep + 1];//用第二行的蓝色直接赋值两个蓝色一样rgb[2] = bayer[2];//保存下个点的红色rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1;//计算绿色的平均值rgb[4] = bayer[bayerStep + 1];//用第二行的蓝色直接赋值}} else {{//本行蓝色在红色前出现,RGGB模式下1、3、4 奇数行运行for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) {rgb[1] = bayer[0];//保存蓝色rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1;//计算绿色的平均值rgb[-1] = bayer[bayerStep + 1];//用第二行的红色直接赋值两个红色一样rgb[4] = bayer[2];//保存蓝色rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1;//计算绿色的平均值rgb[2] = bayer[bayerStep + 1];//用第二行的红色直接赋值两个红色一样}}if (bayer < bayerEnd) {//最后一个像素处理rgb[-blue] = bayer[0];//RGGB模式下0、2、4偶数行运行为红  ,RGGB模式下1、3、4 奇数行为蓝rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1;//计算绿色的平均值rgb[blue] = bayer[bayerStep + 1];//RGGB模式下0、2、4偶数行运行bayer++;rgb += 3;}bayer -= width;//回到行的第一个字节rgb -= width * 3;//rgb 回到本行处理的第一个字节// RGGB模式下0、2、4偶数行为红色先出blue=1,start_with_green=0    ,1、3、4 奇数行为blue=-1,start_with_green=1blue = -blue;//处理一行蓝色和红色先出的关系发生变化 start_with_green = !start_with_green;//处理一行绿色是不是第一个字节会发生变化,}return 1;
}

NEON编程

一个很好的NEON 指令查找说明的网站
大体实现RGB转换,代码有点长但是为了考虑运行效率和移植方便性没有进行函数封装化和使用.h方式简化。很多段内容重复可以优化可读性。其实最neon最核心的部分代码只有类似的一小段,程序运行时下面一小段实际一次能处理16个点。从代码上来看效率是非常高的应该有原有代码的6倍以上的效率,但是实际LINUX下运行效率提升不大可能是我的图像输入区域是无CACHE内存拷贝时间较长,对性能有2倍影响导致。下面是核心的取值转换部分。

/** neon.h**  Created on: 2021年12月6日*      Author: Administrator*/#if 1bayer_ODD_row = vld2_u8(&bayer[width]);//以两分量形式获取下一行数据
#if RG_GB==1//R//Bb_result= vzip_u8(bayer_ODD_row.val[1], bayer_ODD_row.val[1]); //将row1_b和row1_b本身合并为B0B0B1B1B2B2.....//Gg1_result= vrhadd_u8(bayer_EVEN_row.val[1],bayer_ODD_row.val[0]);//将G色row0的和row1的值相加初2除4舍5入row_g_chang = vext_u8(bayer_ODD_row.val[0], bayer_ODD_row.val[0], 1);//将G色的row1行去掉第一个绿色,后面值前移g2_result= vrhadd_u8(bayer_EVEN_row.val[1],row_g_chang);//将G色row0的和row1的值相加初2除4舍5入
#else//Rrow_r_chang = vext_u8(bayer_ODD_row.val[0], bayer_ODD_row.val[0], 1);//将ROW0 去掉R0 从R1开始r_result= vzip_u8(bayer_ODD_row.val[0], row_r_chang); //将row0_r_chang和r_result交替合并为R0R1R1R2R2....//B//Gg1_result= vrhadd_u8(bayer_EVEN_row.val[0],bayer_ODD_row.val[1]);//将G色row0的和row1的值相加初2除4舍5入row_g_chang = vext_u8(bayer_EVEN_row.val[0], bayer_EVEN_row.val[0], 1);//将G色的row1行去掉第一个绿色,后面值前移g2_result= vrhadd_u8(bayer_ODD_row.val[1],row_g_chang);//将G色row0的和row1的值相加初2除4舍5入
#endif//将G色row0的和移位后的row1的值相加除2后4舍5入g_result= vzip_u8(g1_result, g2_result);//将两次计算的G色交并保存rgb_result.val[R_OUT_NUMB]=r_result.val[0];rgb_result.val[G_OUT_NUMB]=g_result.val[0];rgb_result.val[B_OUT_NUMB]=b_result.val[0];//          printf("line &bayer[0]:0x%x  &bayer[width]:0x%x\n",&bayer[0],&bayer[width]);
//         printf("rg line 0x%x\n",rgb);
//         printf("rg line 0x%x\n",rgb+RGB_BYTE*8);WRITE_VSTrgb_result.val[R_OUT_NUMB]=r_result.val[1];rgb_result.val[G_OUT_NUMB]=g_result.val[1];rgb_result.val[B_OUT_NUMB]=b_result.val[1];WRITE_VST2bayer+=width;rgb+=rgb_width;
//1、2、3奇数行的处理bayer_EVEN_row = vld2_u8(&bayer[width]);//以两分量形式获取下一行数据
#if RG_GB==1//Rrow_r_chang = vext_u8(bayer_EVEN_row.val[0], bayer_EVEN_row.val[0], 1);//将ROW0 去掉R0 从R1开始r_result= vzip_u8(bayer_EVEN_row.val[0], row_r_chang); //将row0_r_chang和r_result交替合并为R0R1R1R2R2....//B//Gg1_result= vrhadd_u8(bayer_ODD_row.val[0],bayer_EVEN_row.val[1]);//将G色row0的和row1的值相加初2除4舍5入row_g_chang = vext_u8(bayer_ODD_row.val[0], bayer_ODD_row.val[0], 1);//将G色的row1行去掉第一个绿色,后面值前移g2_result= vrhadd_u8(bayer_EVEN_row.val[1],row_g_chang);//将G色row0的和row1的值相加初2除4舍5入
#else//R//Bb_result= vzip_u8(bayer_EVEN_row.val[1], bayer_EVEN_row.val[1]); //将row1_b和row1_b本身合并为B0B0B1B1B2B2.....//Gg1_result= vrhadd_u8(bayer_ODD_row.val[1],bayer_EVEN_row.val[0]);//将G色row0的和row1的值相加初2除4舍5入row_g_chang = vext_u8(bayer_EVEN_row.val[0], bayer_EVEN_row.val[0], 1);//将G色的row1行去掉第一个绿色,后面值前移g2_result= vrhadd_u8(bayer_ODD_row.val[1],row_g_chang);//将G色row0的和row1的值相加初2除4舍5入
#endif//将G色row0的和移位后的row1的值相加除2后4舍5入g_result= vzip_u8(g1_result, g2_result);//将两次计算的G色交并保存rgb_result.val[R_OUT_NUMB]=r_result.val[0];rgb_result.val[G_OUT_NUMB]=g_result.val[0];rgb_result.val[B_OUT_NUMB]=b_result.val[0];
//         printf("line &bayer[0]:0x%x  &bayer[width]:0x%x\n",&bayer[0],&bayer[width]);
//         printf("gb line 0x%x\n",rgb);
//         printf("gb line 0x%x\n",rgb+RGB_BYTE*8);WRITE_VSTrgb_result.val[R_OUT_NUMB]=r_result.val[1];rgb_result.val[G_OUT_NUMB]=g_result.val[1];rgb_result.val[B_OUT_NUMB]=b_result.val[1];WRITE_VST2
#endif

转换函数全部程序,带横向裁剪功能。
本人使用时对图像进行了裁剪,并对最后几个点色彩不正确不太关系,没有对行的最后几个点特殊处理(写0值),程序先竖向处理几个点再横向处理。减少了重复读取数据的次数又能兼顾cache的横向的CACHE 命中。
本人使用时行是8对齐的,且输入又多余一行没有对最后一行特殊处理(写0值)。

 #define RGB_BYTE 3
#define USE_BAYER_NUMB 8 //一次计算使用的BAYER 字节数
#define R_OUT_NUMB 0//0开始 R在输出的第几位
#define G_OUT_NUMB 1//0开始 R在输出的第几位
#define B_OUT_NUMB 2//0开始 R在输出的第几位
#define RG_GB 1   //RG_GB模式设置为1     ,GB_RG模式设置为0#if 0
#  define do_prefetch(_addr, _lvl) do { } while (0)
#else
#define do_prefetch(_addr, _lvl) do { \__builtin_prefetch(((void const *)(_addr)) + 128, 0, (_lvl)); \} while (0);
#endif#if RGB_BYTE==3
#define WRITE_VST       vst3_u8(rgb, rgb_result);//RGB的值一并写入内存
#define WRITE_VST2      vst3_u8(rgb+RGB_BYTE*8, rgb_result);//RGB的值一并写入内存
#else
#define WRITE_VST       vst4_u8(rgb, rgb_result);//RGB的值一并写入内存
#define WRITE_VST2      vst4_u8(rgb+RGB_BYTE*8, rgb_result);//RGB的值一并写入内存
#endif#define ONE_TIME_HIGH 8//一次处理几行  //有几个include 对应 *2个行void neon_bayer_Simple(uint8_t * in_bayer, uint8_t * out_rgb, uint16_t width, uint16_t height,uint16_t out_wide){// 6148->6144uint16_t i,j;uint32_t rgb_width;uint16_t row_cycle;//一行循环多少次,一行循环后剩余需要填充的点uint8_t row_left,higt_left;uint8_t * bayer;uint8_t * rgb;uint8x8x2_t bayer_EVEN_row ;uint8x8x2_t bayer_ODD_row ;//Ruint8x8_t row_r_chang ;uint8x8x2_t r_result;//Buint8x8x2_t b_result;//Guint8x8_t g1_result;uint8x8_t row_g_chang;uint8x8_t g2_result;//将G色row0的和移位后的row1的值相加除2后4舍5入uint8x8x2_t g_result;//将两次计算的G舍交并保存#if RGB_BYTE==3uint8x8x3_t rgb_result;//if 3通道rgb修改为#elseuint8x8x4_t rgb_result={0};//if 4通道rgb修改为#endif//testhigt_left=height&ONE_TIME_HIGH;height=height/ONE_TIME_HIGH;//处理耦数行,且最后一行数据不能用row_left=out_wide%(USE_BAYER_NUMB*2);row_cycle=out_wide-row_left;rgb_width=out_wide*RGB_BYTE;for(i=0;i<height;i++) {for (j=0;j<row_cycle;j=j+USE_BAYER_NUMB*2) {//循环只处理能整除的部分rgb=out_rgb+j*RGB_BYTE;bayer=in_bayer+j;bayer_EVEN_row = vld2_u8(bayer);//以两分量形式获取本行数据#if RG_GB==1//Rrow_r_chang = vext_u8(bayer_EVEN_row.val[0], bayer_EVEN_row.val[0], 1);//将ROW0 去掉R0 从R1开始r_result= vzip_u8(bayer_EVEN_row.val[0], row_r_chang); //将row0_r_chang和r_result交替合并为R0R1R1R2R2....#else//Bb_result= vzip_u8(bayer_EVEN_row.val[1], bayer_EVEN_row.val[1]); //将row1_b和row1_b本身合并为B0B0B1B1B2B2.....#endif//#include "neon.h"的数量需要和ONE_TIME_HIGH关系对应#include "neon.h"bayer+=width;rgb+=rgb_width;#include "neon.h"bayer+=width;rgb+=rgb_width;#include "neon.h"bayer+=width;rgb+=rgb_width;#include "neon.h"}in_bayer+=width*ONE_TIME_HIGH;out_rgb+=rgb_width*ONE_TIME_HIGH;}//后面的根据尺寸调整,最后2行是不能处理的,所以需要特殊处理根据高度和相关的关系
}

计算结果

bayer模式下RGGB输入16x16图像内容:

转换后的RGB 格式内容:

RawData是Bayer转RGB格式代码分析和NEON单元加速方法相关推荐

  1. OpenCV读出来的是按BGR存储的,如何转变成传统的RGB格式

    opencv 视频帧的数据是按按BGR存储的,想转变成传统的RGB格式便于和其他库对接使用 方法1 核心代码 cv2.cvtColor(Frame,cv2.COLOR_BGR2RGB) ,其中 Fra ...

  2. 分析比较图像RGB格式和YUV格式的存储概率分布

    分析比较图像RGB格式和YUV格式的存储概率分布 实验思路 两个图片文件down.yuv和down.rgb均为二进制存储的图片文件,像素为256* 256.yuv为4:2:0格式,即1份y对应0.25 ...

  3. php table转json,html table表数据转Json格式示例代码分析

    本文为大家介绍下html table表数据转Json格式,下面有个不错的示例,大家可以参考下 代码如下: var keysArr = new Array("key0", " ...

  4. 程序猿(媛)实用颜色表 [颜色图示,英文代码,形象颜色,HEX格式,RGB格式]

    非ie6 例如:if ( document.getElementById("XX").style.color == "rgb(184, 0, 0)") 注意:& ...

  5. Bayer到RGB,格式转换原理及具体实现

    缘起 使用海康彩色相机,采集到的图像数据是Bayer GR8格式的,如果在相机参数中改为其它彩色格式,那就是相机内部完成格式转化,就会导致采集帧率变慢.一般情况下这种方式是简单实用的,但如果想要追求更 ...

  6. s3c6410 uboot代码分析《一》

    来源:http://hi.baidu.com/__eabi/blog/item/be67533797bc73f014cecb49.html 以下用以记录uboot代码的分析过程,目标是s3c6410, ...

  7. 【DSP开发】【VS开发】YUV与RGB格式转换

    [视频处理]YUV与RGB格式转换 YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与 ...

  8. MediaInfo源代码分析 5:JPEG解析代码分析

    ===================================================== MediaInfo源代码分析系列文章列表: MediaInfo源代码分析 1:整体结构 Me ...

  9. 【Android Camera2】玩转图像数据 -- NV21图像旋转,镜像,转rgba代码分析,性能优化

    [Android Camera2]玩转图像数据 业务场景介绍 NV21数据旋转 逐像素遍历法 NV21数据镜像 逐像素遍历法 中心翻转法 NV21转RGB/RGBA数据 逐像素遍历法 NV21组合操作 ...

  10. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

最新文章

  1. 读书笔记 | 墨菲定律(一)
  2. 查看Eclipse32位还是64位以及Eclipse的编译版本号,查看JDK是32位还是64位
  3. 《区块链原理、设计与应用》一1.4 潜在的商业价值
  4. Java GC如何判断对象是否为垃圾
  5. 装饰器模式与java.io包
  6. 移动医疗未来还有多少红利?
  7. Python 5种方法实现单例模式
  8. DNS服务器的默认区域文件名,DNS服务器全攻略之三 :创建与管理DNS区域.doc
  9. 国自然申请初审中的注意事项
  10. RabbitMq的学习(一):VirtualHost
  11. pdf安装包_有么有pdf控件,不需要用户安装任何安装包直接打印的?
  12. Java 队列清空,如何清空Actor死信队列 - java
  13. union all会影响性能吗_哪些因素会影响悬臂式掘进机的性能?
  14. scrollTop 用法说明
  15. 学习进度条__软件工程概论第一周学习计划
  16. 钉钉自带浏览器版本过低,导致Object.assign不兼容...
  17. 关于ajax post请求跨域问题的解决心得
  18. 登录验证时第一次帐号密码错误,第二次提交出现错误404,Could not find action or result: /zyf_shop/user_login.action
  19. html文档标记语言,html是超文本标记语言标签有
  20. 手工脱壳之 ASPack压缩壳【随机基址】【重定位表加密】

热门文章

  1. 支付宝小程序生成二维码
  2. SSH连接工具-Tabby
  3. Java中native方法的使用
  4. Matlab2018如何画函数曲线,2018年Matlab画函数图像.doc
  5. 飞思卡尔单片机编程与c语言,飞思卡尔单片机C语言编程(中文).pdf
  6. 推荐一款在线文件对比工具
  7. java 偏向锁的撤销_源码解析-偏向锁撤销流程解读
  8. 网络基础知识之报文格式介绍
  9. linux驱动编译成kext,Hackintosh:制作AppleALC以驱动原生AppleHDA
  10. python 教程 w3 school_Python 模块 | w3cschool菜鸟教程