彩色MT9V034摄像头 Bayer转RGB FPGA实现

1 图像bayer格式介绍

  bayer格式是伊士曼·柯达公司科学家Bryce Bayer发明的,Bryce Bayer所发明的拜耳阵列被广泛运用数字图像。Bayer格式是相机内部的原始数据, 一般后缀名为.raw。

  对于彩色图像,一般是三原色数据,rgb格式。但是摄像头一个像素点只有rgb中一种数据(下图为bayer色彩滤波阵列)。但是有很多摄像头直接输出rgb和yuv格式,如ov5640、ov7725等等,这是因为在Sensor模组的内部会有一个ISP模块,会将 Sensor采集到的数据进行插值和特效处理,所以直接输出彩色图像但也有摄像头没有ISP模块,直接输出Bayer数据,这就需要自己写Bayer转rgb算法。

2 MT9V034简单介绍

 做图像处理的朋友都知道,MT9V034是一款十分出色的做机器视觉的摄像头,一般都是灰度的。但是也有彩色款,当时我觉得灰度的效果那么好,一时头热就买一个彩色款的。mt9v034用起来很方便,可以不用寄存器配置,上电默认752*480分辨率。当然也可以iic配置。

  全局快门(相对滚动快门) 拍摄高速物体的效果:

  高动态效果:

3 MT9V034 datasheet 简单解析

  1) 有效图像 752x480;

    最大时钟为27Mhz;

    最大帧率为60fps;

    10位的adc(我的是八位的输出,店家只将高8位引出,有点影响最后图像的精度);

 2)这是mt9v034Bayer阵列,注意输出方向,从左到右,从上到下;

  3)摄像头ID号要需要查看摄像头模块PCB上的S_CTRL_ADR1和S_CTRL_ADR0引脚怎么连接的;

  4)下图是摄像头原理图,很明显S_CTRL_ADR1, S_CTRL_ADR0是被拉低了,所以摄像头ID为0x90.上面说到摄像头只有高8位被引出,在这里可以证实了;

  5)下面是大部分寄存器,mt9v034可配置的寄存器很少。0x00是芯片版本。03、04是摄像头分辨率

  6)datasheet就介绍到这里,更多信息需要自己去阅读。

4 Bayer转rgb算法解析

我是用shift_register IP缓存两行数据,形成2*2窗口(这是FPGA做图像算法最常用的方法和IP),不会的朋友可以搜一搜,有很多博客可以学习,一定要自己仿真一下,搞明白,这原理有点难理解。

根据窗口移动,不难发现,总结出一条重要的规律:总共只有四种窗口,而且与行和列的奇偶有关。

假设计数器从零开始记数:

 1)第一种{行偶,列偶}

 2)第二种{行偶,列奇}

3)第三种{行奇,列偶}

4)第四种{行奇,列奇}

5 算法实现

首先说明我是用Xilinx的ZYNQ FPGA,(Altera的也有类似的IP)。我直接说明一下IP的参数设置,其他的像怎么添加IP什么的我就不讲了,不会的自己网上学习。

  1)这是IP首页,蓝框自定义IP名,修改一下红框的参数,我们是8位数据,一行数据为640个。Clock enable端与sclr端可以根据自己的要求决定勾不勾选。其他默认就行,点击ok可以了。

  2)Vivado也提供端口例化模板,如下图操作就行;

  3)verilog源码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: 宏强子
//
// Create Date: 2019/02/04 10:29:56
// Design Name: colour MT98V034 bayer2rgb
// Module Name: MT_bayer2rgb
// Project Name: Colour_MT_bayer2rgb
// Target Devices: ZYNQ7020
// Tool Versions: vivado2018.3
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////




module MT_bayer2rgb(
//system singal
input s_rst_n ,
//cmos simgals
input vsync_i ,
input hsync_i ,
input pclk ,
input [7:0]      bayer_data ,
//输出
output vsync_o ,
output hsync_o ,
output [23:0]      rgb_data
);


//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/


reg [9:0] col_cnt ;
reg [8:0] row_cnt ;


reg hsync_i_1 ;
reg hsync_i_2 ;




reg vsync_i_1 ;
reg vsync_i_2 ;


wire [7:0] line_1 ;
wire [7:0] line_2 ;
reg [2:0] data_control      ;


reg [7:0] line1_1 ;
reg [7:0] line1_2 ;


reg [7:0] line2_1 ;
reg [7:0] line2_2 ;




reg [7:0] rgb_r ;
reg [8:0] rgb_g ;
reg [7:0] rgb_b ;
//=============================================================================
//**************************** Main Code *******************************
//=============================================================================


//列计数
always @ (posedge pclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
col_cnt <= 10'd0;
else if (hsync_i == 1'b1)
col_cnt <= col_cnt + 1'b1;
else
col_cnt <= 10'd0;
end


always @ (posedge pclk) begin
hsync_i_1 <= hsync_i;
hsync_i_2 <= hsync_i_1;
end




always @ (posedge pclk) begin
vsync_i_1 <= vsync_i;
vsync_i_2 <= vsync_i_1;
end


//行计数
always @ (posedge pclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
row_cnt <= 9'd0;
else if(~hsync_i && hsync_i_1)
row_cnt <= row_cnt + 1'b1;
else if (row_cnt >= 9'd481)
row_cnt <= 9'd0;
end




//data_control
always @ (posedge pclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
data_control <= 3'b100;
else if (hsync_i_1 == 1'b1 && hsync_i == 1'b1)
data_control <= {1'b0,row_cnt[0],~col_cnt[0]};
else
data_control <= 3'b100;
end


shift_ram shift_ram_1 (
.D (bayer_data      ), // input wire [7 : 0] D
.CLK (pclk ), // input wire CLK
.CE (hsync_i ), // input wire CE
.SCLR (~s_rst_n ), // input wire SCLR
.Q (line_1 ) // output wire [7 : 0] Q
);


shift_ram shift_ram_2 (
.D (line_1 ), // input wire [7 : 0] D
.CLK (pclk ), // input wire CLK
.CE (hsync_i ), // input wire CE
.SCLR (~s_rst_n ), // input wire SCLR
.Q (line_2 ) // output wire [7 : 0] Q
);




always @ (posedge pclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0) begin
line1_1 <= 8'd0;
line1_2 <= 8'd0;


line2_1 <= 8'd0;
line2_2 <= 8'd0;
end
else begin
line1_1 <= line_1;
line1_2 <= line1_1;


line2_1 <= line_2;
line2_2 <= line2_1;
end

end




always @ (data_control) begin
case(data_control)
3'b000 : begin
rgb_r = line1_1 + 8'd5;
rgb_g = line2_1 + line1_2 + 8'd10;
rgb_b = line2_2 + 8'd5;
end
3'b001 : begin
rgb_r = line1_2 + 8'd5;
rgb_g = line1_1 + line2_2 + 8'd10;
rgb_b = line2_1 + 8'd5;
end
3'b010 : begin
rgb_r = line2_1 + 8'd5;
rgb_g = line1_1 + line2_2 + 8'd10;
rgb_b = line1_2 + 8'd5;
end
3'b011 : begin
rgb_r = line2_2 + 8'd5;
rgb_g = line2_1 + line1_2 + 8'd10;
rgb_b = line1_1 + 8'd5;
end
default: begin
rgb_r = 8'd0;
rgb_g = 9'd0;
rgb_b = 8'd0;
end
endcase
end


assign rgb_data = {rgb_r,rgb_g[8:1],rgb_b};
assign vsync_o = vsync_i_2;
assign hsync_o = hsync_i_2;
endmodule

 4)最后欣赏一下效果 ,效果还不错,继承了灰度款的优良性能。

6 总结

最后说明一下,最后分辨率改为640*480,但是发现480指的是0~480,所以行计数器在481清零。由于我使用的是ZYNQ,所以直接使用PS端的IIC接口配置摄像头。如果用默认的分辨率就需要修改一下IP的深度和行计数器的清零的数值就行了。

交易担保 FPGA芯城 XILINX ALTERA 自营电商平台 小程序

一扫

FPGA之家

欢迎FPGA、嵌入式、信号处理等工程师关注公众号

注我

攻城狮们!

全国最大的FPGA技术群社区

欢迎大家加入全国最大最强的FPGA微信技术群,这个群体拥有数万工程师、一群热爱技术的工程师,这里的FPGA工程师相互帮助,相互分享,技术氛围浓厚!赶紧叫上小伙伴一起加入吧!

用手指按住就可以加入FPGA全国技术群哦!

国内最好的Xilinx和altera芯片供应商之一!

平台自营,进口原装品质保证!

持续供应多家军工研究所和上市公司!

最好的FPGA芯片价格和最好的售后服务!

拥有业内最顶尖的服务口碑!

全球顶尖供应商质量标准背书!

XILINX全系列订货或者现货优势!

XCVU9P-2FLGB2104I   200PCS

XCVU9P-2FLGA2104I   500PCS

XCVU13P-2FLGB2104I  300PCS

XC7K325T-2FFG900I   1500PCS

XC7K325T-2FFG676I  950PCS

XC7K160T-2FFG676I   850PCS

XC7VX690T-2FFG1927I  即将到货,敬请预定!

XC7VX690T-2FFG1761I  即将到货,敬请预定!

更多赛灵思型号需求请咨询我们!谢谢!扫码二维码即可!

FPGA技术群官方鸣谢品牌:Xilinx、 intel(Altera)、microsemi(,Actel)、LattIC e,Vantis,Quicklogic,Lucent等对技术群的支持!

(0)

相关推荐