紫光同创PGL22G开发平台试用连载(3)——驱动OV5640 摄像头实现sobel算子边缘检测算法

千年一叹 提交于 周一, 10/12/2020
紫光同创PGL22G开发平台试用连载(3)——驱动OV5640 摄像头实现sobel算子边缘检测算法

通过近一个月的学习,紫光同创PGL22Gfpga已经和spartan6和kintex7不相上下了,紫光EDA生态也趋于完善。EDA是FPGA皇冠上的明珠。FPGA更大的逻辑规模和更高的性能,意味着指数级增长的软件算法复杂度。Pango Design Suite是紫光同创基于多年FPGA开发软件技术攻关与工程实践经验而研发的一款拥有国产自主知识产权的大规模FPGA开发软件,已经可以支持数千万门级FPGA器件的设计,并可实现从RTL综合到配置数据流生成下载的全套操作,形成了完全自主知识产权的FPGA开发工具套件,更适合国内研发需要,某些方面比Altera 和Xilinx公司Quartus II Vivado和ISE更加人性化。

边缘检测算法是图像处理中最为基本的问题。其目的是标志图像出亮度变化明显的点,从而反映出图像中重要变化。边缘是图像最基本的特征,其在计算机视觉、图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段。在图像中,“边缘”指的是临界的意思。一幅图像的“临界”表示为图像上亮度显著变化的地方,边缘指的是一个区域的结束,也是另一个区域的开始。“边缘点”指的是图像中具有坐标[x,y],且处在强度显著变化的位置上的点。常用的边缘检测算法大多是以原始图像灰度值为基础,通过考察图像的每个像素的某个邻域内灰度的变化,利用边缘一阶或二阶导数的规律来检测边缘。
    
实现边缘检测有很多不同的方法,也一直是图像处理中的研究热点,人们期望找到一种抗噪强、定位准、不漏检、不误检的检测算法。边缘检测算子,一阶:Roberts Cross算子,Prewitt算子,Sobel算子, Kirsch算子,罗盘算子;二阶: Marr-Hildreth,在梯度方向的二阶导数过零点,Canny算子,Laplacian算子。其中 Sobel 算子效果较好,边缘检测算法比较简单,实际应用中效率比 canny 边缘检测效率要高,但是边缘不如 Canny 检测的准确,但是很多实际应用的场合, sobel 边缘却是首选,尤其是对效率要求较高,而对细纹理不太关心的时候。采用 Sobel 的算法来实现视频图像的边缘检测。Sobel 算子是像素图像边缘检测中最重要的算子之一,该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如下图,Gx和Gy分别是在横向及纵向的灰度偏导的近似值。 为了在一副图像f的(x,y)位置处寻找边缘的强度和方向,所选择的工具就是梯度,向量定义为:

公式

   公式

Sobel算法:

1、把图像每三行三列的数据分别乘上算子中对应位置的值再相加。然后进行如下运算,得到相应方向(x和y)的Dx和Dy。
Dx=(a3-a1)+(b3-b1)*2+c3-c1;
Dy=(a1-c1)+(a2-c2)*2+a3-c3;

2、对上面求得的Dx和Dy做平方和的平方根,再取近似值Dx和Dy的绝对值的和得到Dxy:
Dxy=Dx2+Dy2=(∣Dx∣+∣Dy∣)Dxy = \sqrt{Dx^2+Dy^2} =(\left|Dx\right|+\left|Dy\right|)Dxy=Dx2+Dy2​=(∣Dx∣+∣Dy∣)

3、如果Dxy的值大于一个阈值,表示该点为边界点,就让VGA显示一个白点,否则显示黑点。

4、把计算的结果通过vga显示,显示器会把是边界点的以白色像素显示,不是边界点的以黑色像素点显示,于是得到了一幅图像的轮廓。

sobel 算法的实现,首先需要一个 3x3 的像素窗口,本实验利用 xilinx 提供的VHDL 程序,做了一个 3 行的图像缓存,这样就可以轻松实现 3x3 的窗口。
然后按照简化公式,采用绝对值的方式计算 sobel。

架构

架构

module sobel (

input rst,

input pclk,

input[7:0] threshold,

input de,

input[7:0] data_in,

output reg[7:0] data_out

);

reg[7:0] p11,p12,p13;

reg[7:0] p21,p22,p23;

reg[7:0] p31,p32,p33;

wire[7:0] p1,p2,p3;

reg[9:0] x1,x3;

reg[9:0] y1,y3;

reg[9:0] abs_x,abs_y;

reg[10:0] abs_g;

linebuffer_Wapper#

(

.no_of_lines(3),

.samples_per_line(1024),

.data_width(8)

)

linebuffer_Wapper_m0(

.ce (1'b1 ),

.wr_clk (pclk ),

.wr_en (de ),

.wr_rst (rst ),

.data_in (data_in),

.rd_en (de ),

.rd_clk (pclk ),

.rd_rst (rst ),

.data_out ({p3,p2,p1} )

);

always@(posedge pclk)

begin

p11 <= p1;

p21 <= p2;

p31 <= p3;

 

p12 <= p11;

p22 <= p21;

p32 <= p31;

 

p13 <= p12;

p23 <= p22;

p33 <= p32;

end

 

always@(posedge pclk)

begin

x1 <= {2'b00,p11} + {2'b00,p31} + {1'b0,p21,1'b0};

x3 <= {2'b00,p13} + {2'b00,p33} + {1'b0,p23,1'b0};

 

y1 <= {2'b00,p11} + {2'b00,p13} + {1'b0,p12,1'b0};

y3 <= {2'b00,p31} + {2'b00,p33} + {1'b0,p32,1'b0};

end

 

always@(posedge pclk)

begin

abs_x <= (x1 > x3) ? x1 - x3 : x3 - x1;

abs_y <= (y1 > y3) ? y1 - y3 : y3 - y1;

abs_g <= abs_x + abs_y;

end

 

always@(posedge pclk)

begin

data_out <= (abs_g > threshold) ? 8'h00 : 8'hff;

end

 

endmodule

module top(

input sys_clk,

input rst_n,

inout cmos_scl,

inout cmos_sda,

input cmos_vsync,

input cmos_href,

input cmos_pclk,

output cmos_xclk,

input [7:0] cmos_db,

//ddr

input pad_loop_in ,

input pad_loop_in_h ,

output pad_rstn_ch0 ,

output pad_ddr_clk_w ,

output pad_ddr_clkn_w ,

output pad_csn_ch0 ,

output [15:0] pad_addr_ch0 ,

inout [16-1:0] pad_dq_ch0 ,

inout [16/8-1:0] pad_dqs_ch0 ,

inout [16/8-1:0] pad_dqsn_ch0 ,

output [16/8-1:0] pad_dm_rdqs_ch0 ,

output pad_cke_ch0 ,

output pad_odt_ch0 ,

output pad_rasn_ch0 ,

output pad_casn_ch0 ,

output pad_wen_ch0 ,

output [2:0] pad_ba_ch0 ,

output pad_loop_out ,

output pad_loop_out_h ,

//hdmi output

output tmds_clk_p,

output tmds_clk_n,

output [2:0] tmds_data_p,

output [2:0] tmds_data_n

);

 

parameter MEM_DATA_BITS = 64; //external memory user interface data width

parameter ADDR_BITS = 25; //external memory user interface address width

parameter BUSRT_BITS = 10; //external memory user interface burst width

wire wr_burst_data_req;

wire wr_burst_finish;

wire rd_burst_finish;

wire rd_burst_req;

wire wr_burst_req;

wire[BUSRT_BITS - 1:0] rd_burst_len;

wire[BUSRT_BITS - 1:0] wr_burst_len;

wire[ADDR_BITS - 1:0] rd_burst_addr;

wire[ADDR_BITS - 1:0] wr_burst_addr;

wire rd_burst_data_valid;

wire[MEM_DATA_BITS - 1 : 0] rd_burst_data;

wire[MEM_DATA_BITS - 1 : 0] wr_burst_data;

wire read_req;

wire read_req_ack;

wire read_en;

wire[15:0] read_data;

wire write_en;

wire[15:0] write_data;

wire write_req;

wire write_req_ack;

wire video_clk; //video pixel clock

wire video_clk5x;

wire hs;

wire vs;

wire de;

wire[15:0] vout_data;

wire[15:0] cmos_16bit_data;

wire cmos_16bit_wr;

wire[1:0] write_addr_index;

wire[1:0] read_addr_index;

wire[9:0] lut_index;

wire[31:0] lut_data;

 

wire ui_clk;

wire ui_clk_sync_rst;

wire init_calib_complete;

// Master Write Address

wire [3:0] s00_axi_awid;

wire [63:0] s00_axi_awaddr;

wire [7:0] s00_axi_awlen; // burst length: 0-255

wire [2:0] s00_axi_awsize; // burst size: fixed 2'b011

wire [1:0] s00_axi_awburst; // burst type: fixed 2'b01(incremental burst)

wire s00_axi_awlock; // lock: fixed 2'b00

wire [3:0] s00_axi_awcache; // cache: fiex 2'b0011

wire [2:0] s00_axi_awprot; // protect: fixed 2'b000

wire [3:0] s00_axi_awqos; // qos: fixed 2'b0000

wire [0:0] s00_axi_awuser; // user: fixed 32'd0

wire s00_axi_awvalid;

wire s00_axi_awready;

// master write data

wire [63:0] s00_axi_wdata;

wire [7:0] s00_axi_wstrb;

wire s00_axi_wlast;

wire [0:0] s00_axi_wuser;

wire s00_axi_wvalid;

wire s00_axi_wready;

// master write response

wire [3:0] s00_axi_bid;

wire [1:0] s00_axi_bresp;

wire [0:0] s00_axi_buser;

wire s00_axi_bvalid;

wire s00_axi_bready;

// master read address

wire [3:0] s00_axi_arid;

wire [63:0] s00_axi_araddr;

wire [7:0] s00_axi_arlen;

wire [2:0] s00_axi_arsize;

wire [1:0] s00_axi_arburst;

wire [1:0] s00_axi_arlock;

wire [3:0] s00_axi_arcache;

wire [2:0] s00_axi_arprot;

wire [3:0] s00_axi_arqos;

wire [0:0] s00_axi_aruser;

wire s00_axi_arvalid;

wire s00_axi_arready;

// master read data

wire [3:0] s00_axi_rid;

wire [63:0] s00_axi_rdata;

wire [1:0] s00_axi_rresp;

wire s00_axi_rlast;

wire [0:0] s00_axi_ruser;

wire s00_axi_rvalid;

wire s00_axi_rready;

 

wire hdmi_hs;

wire hdmi_vs;

wire hdmi_de;

wire[7:0] hdmi_r;

wire[7:0] hdmi_g;

wire[7:0] hdmi_b;

wire[7:0] sobel_out;

wire[7:0] ycbcr_y;

wire ycbcr_hs;

wire ycbcr_vs;

wire ycbcr_de;

assign hdmi_hs = ycbcr_hs;

assign hdmi_vs = ycbcr_vs;

assign hdmi_de = ycbcr_de;

assign hdmi_r = sobel_out[7:0];

assign hdmi_g = sobel_out[7:0];

assign hdmi_b = sobel_out[7:0];

 

assign write_en = cmos_16bit_wr;

assign write_data = {cmos_16bit_data[4:0],cmos_16bit_data[10:5],cmos_16bit_data[15:11]};

 

video_pll video_pll_m0

(

.clkin1 (sys_clk ),

.clkout0 (video_clk ),

.clkout1 (video_clk5x ),

.clkout2 (cmos_xclk ),

.pll_rst (1'b0 ),

.pll_lock ( )

);

wire done_init;

dvi_encoder dvi_encoder_m0

(

.pixelclk (video_clk ),// system clock

.pixelclk5x (video_clk5x ),// system clock x5

.rstin (~done_init ),// reset

.blue_din (hdmi_b ),// Blue data in

.green_din (hdmi_g ),// Green data in

.red_din (hdmi_r ),// Red data in

.hsync (hdmi_hs ),// hsync data

.vsync (hdmi_vs ),// vsync data

.de (hdmi_de ),// data enable

.tmds_clk_p (tmds_clk_p ),

.tmds_clk_n (tmds_clk_n ),

.tmds_data_p (tmds_data_p ),//rgb

.tmds_data_n (tmds_data_n ) //rgb

);

 

//I2C master controller

i2c_config i2c_config_m0(

.rst (~rst_n ),

.clk (sys_clk ),

.clk_div_cnt (16'd499 ),

.i2c_addr_2byte (1'b1 ),

.lut_index (lut_index ),

.lut_dev_addr (lut_data[31:24] ),

.lut_reg_addr (lut_data[23:8] ),

.lut_reg_data (lut_data[7:0] ),

.error ( ),

.done (done_init ),

.i2c_scl (cmos_scl ),

.i2c_sda (cmos_sda )

);

//configure look-up table

lut_ov5640_rgb565_1024_768 lut_ov5640_rgb565_1024_768_m0(

.lut_index (lut_index ),

.lut_data (lut_data )

);

//CMOS sensor 8bit data is converted to 16bit data

cmos_8_16bit cmos_8_16bit_m0(

.rst (~done_init ),

.pclk (cmos_pclk ),

.pdata_i (cmos_db ),

.de_i (cmos_href ),

.pdata_o (cmos_16bit_data ),

.hblank ( ),

.de_o (cmos_16bit_wr )

);

//CMOS sensor writes the request and generates the read and write address index

cmos_write_req_gen cmos_write_req_gen_m0(

.rst (~done_init ),

.pclk (cmos_pclk ),

.cmos_vsync (cmos_vsync ),

.write_req (write_req ),

.write_addr_index (write_addr_index ),

.read_addr_index (read_addr_index ),

.write_req_ack (write_req_ack )

);

 

//The video output timing generator and generate a frame read data request

video_timing_data video_timing_data_m0

(

.video_clk (video_clk ),

.rst (~done_init ),

.read_req (read_req ),

.read_req_ack (read_req_ack ),

.read_en (read_en ),

.read_data (read_data ),

.hs (hs ),

.vs (vs ),

.de (de ),

.vout_data (vout_data )

);

 

rgb_to_ycbcr rgb_to_ycbcr_m0(

.clk (video_clk ),

.rst (~done_init ),

.rgb_r ({vout_data[15:11],3'd0} ),

.rgb_g ({vout_data[10:5],2'd0} ),

.rgb_b ({vout_data[4:0],3'd0} ),

.rgb_hs (hs ),

.rgb_vs (vs ),

.rgb_de (de ),

.ycbcr_y (ycbcr_y ),

.ycbcr_cb ( ),

.ycbcr_cr ( ),

.ycbcr_hs (ycbcr_hs ),

.ycbcr_vs (ycbcr_vs ),

.ycbcr_de (ycbcr_de )

);

 

sobel sobel_m0(

.rst (~done_init ),

.pclk (video_clk ),

.threshold (8'd40 ),

.de (ycbcr_de ),

.data_in (ycbcr_y ),

.data_out (sobel_out )

);

//video frame data read-write control

frame_read_write frame_read_write_m0

(

.rst (~done_init ),

.mem_clk (ui_clk ),

.rd_burst_req (rd_burst_req ),

.rd_burst_len (rd_burst_len ),

.rd_burst_addr (rd_burst_addr ),

.rd_burst_data_valid (rd_burst_data_valid ),

.rd_burst_data (rd_burst_data ),

.rd_burst_finish (rd_burst_finish ),

.read_clk (video_clk ),

.read_req (read_req ),

.read_req_ack (read_req_ack ),

.read_finish ( ),

.read_addr_0 (24'd0 ), //The first frame address is 0

.read_addr_1 (24'd2073600 ), //The second frame address is 24'd2073600 ,large enough address space for one frame of video

.read_addr_2 (24'd4147200 ),

.read_addr_3 (24'd6220800 ),

.read_addr_index (read_addr_index ),

.read_len (24'd196608 ),//frame size

.read_en (read_en ),

.read_data (read_data ),

 

.wr_burst_req (wr_burst_req ),

.wr_burst_len (wr_burst_len ),

.wr_burst_addr (wr_burst_addr ),

.wr_burst_data_req (wr_burst_data_req ),

.wr_burst_data (wr_burst_data ),

.wr_burst_finish (wr_burst_finish ),

.write_clk (cmos_pclk ),

.write_req (write_req ),

.write_req_ack (write_req_ack ),

.write_finish ( ),

.write_addr_0 (24'd0 ),

.write_addr_1 (24'd2073600 ),

.write_addr_2 (24'd4147200 ),

.write_addr_3 (24'd6220800 ),

.write_addr_index (write_addr_index ),

.write_len (24'd196608 ), //frame size

.write_en (write_en ),

.write_data (write_data )

);

ddr3 u_ipsl_hmemc_top (

.pll_refclk_in (sys_clk ),

.ddr_rstn_key (done_init ),

.pll_aclk_0 ( ),

.pll_aclk_1 (ui_clk ),

.pll_aclk_2 ( ),

.pll_lock ( ),

.ddrphy_rst_done ( ),

 

.ddrc_init_done (ui_clk_sync_rst),

.ddrc_rst (0),

 

.areset_1 (0),

.aclk_1 (ui_clk),

.awid_1 (s00_axi_awid),

.awaddr_1 (s00_axi_awaddr),

.awlen_1 (s00_axi_awlen),

.awsize_1 (s00_axi_awsize),

.awburst_1 (s00_axi_awburst),

.awlock_1 (s00_axi_awlock),

.awvalid_1 (s00_axi_awvalid),

.awready_1 (s00_axi_awready),

.awurgent_1 (1'b0), //?

.awpoison_1 (1'b0), //?

.wdata_1 (s00_axi_wdata),

.wstrb_1 (s00_axi_wstrb),

.wlast_1 (s00_axi_wlast),

.wvalid_1 (s00_axi_wvalid),

.wready_1 (s00_axi_wready),

.bid_1 (s00_axi_bid),

.bresp_1 (s00_axi_bresp),

.bvalid_1 (s00_axi_bvalid),

.bready_1 (s00_axi_bready),

.arid_1 (s00_axi_arid ),

.araddr_1 (s00_axi_araddr ),

.arlen_1 (s00_axi_arlen ),

.arsize_1 (s00_axi_arsize ),

.arburst_1 (s00_axi_arburst ),

.arlock_1 (s00_axi_arlock ),

.arvalid_1 (s00_axi_arvalid ),

.arready_1 (s00_axi_arready ),

.arpoison_1 (1'b0 ), //?

.rid_1 (s00_axi_rid ),

.rdata_1 (s00_axi_rdata ),

.rresp_1 (s00_axi_rresp ),

.rlast_1 (s00_axi_rlast ),

.rvalid_1 (s00_axi_rvalid ),

.rready_1 (s00_axi_rready ),

.arurgent_1 (1'b0), //?

.csysreq_1 (1'b1),

.csysack_1 (),

.cactive_1 (),

 

.csysreq_ddrc (1'b1),

.csysack_ddrc (),

.cactive_ddrc (),

 

.pad_loop_in (pad_loop_in),

.pad_loop_in_h (pad_loop_in_h),

.pad_rstn_ch0 (pad_rstn_ch0),

.pad_ddr_clk_w (pad_ddr_clk_w),

.pad_ddr_clkn_w (pad_ddr_clkn_w),

.pad_csn_ch0 (pad_csn_ch0),

.pad_addr_ch0 (pad_addr_ch0),

.pad_dq_ch0 (pad_dq_ch0),

.pad_dqs_ch0 (pad_dqs_ch0),

.pad_dqsn_ch0 (pad_dqsn_ch0),

.pad_dm_rdqs_ch0 (pad_dm_rdqs_ch0),

.pad_cke_ch0 (pad_cke_ch0),

.pad_odt_ch0 (pad_odt_ch0),

.pad_rasn_ch0 (pad_rasn_ch0),

.pad_casn_ch0 (pad_casn_ch0),

.pad_wen_ch0 (pad_wen_ch0),

.pad_ba_ch0 (pad_ba_ch0),

.pad_loop_out (pad_loop_out),

.pad_loop_out_h (pad_loop_out_h)

);

aq_axi_master u_aq_axi_master

(

.ARESETN (ui_clk_sync_rst ),

.ACLK (ui_clk ),

.M_AXI_AWID (s00_axi_awid ),

.M_AXI_AWADDR (s00_axi_awaddr ),

.M_AXI_AWLEN (s00_axi_awlen ),

.M_AXI_AWSIZE (s00_axi_awsize ),

.M_AXI_AWBURST (s00_axi_awburst ),

.M_AXI_AWLOCK (s00_axi_awlock ),

.M_AXI_AWCACHE (s00_axi_awcache ),

.M_AXI_AWPROT (s00_axi_awprot ),

.M_AXI_AWQOS (s00_axi_awqos ),

.M_AXI_AWUSER (s00_axi_awuser ),

.M_AXI_AWVALID (s00_axi_awvalid ),

.M_AXI_AWREADY (s00_axi_awready ),

.M_AXI_WDATA (s00_axi_wdata ),

.M_AXI_WSTRB (s00_axi_wstrb ),

.M_AXI_WLAST (s00_axi_wlast ),

.M_AXI_WUSER (s00_axi_wuser ),

.M_AXI_WVALID (s00_axi_wvalid ),

.M_AXI_WREADY (s00_axi_wready ),

.M_AXI_BID (s00_axi_bid ),

.M_AXI_BRESP (s00_axi_bresp ),

.M_AXI_BUSER (s00_axi_buser ),

.M_AXI_BVALID (s00_axi_bvalid ),

.M_AXI_BREADY (s00_axi_bready ),

.M_AXI_ARID (s00_axi_arid ),

.M_AXI_ARADDR (s00_axi_araddr ),

.M_AXI_ARLEN (s00_axi_arlen ),

.M_AXI_ARSIZE (s00_axi_arsize ),

.M_AXI_ARBURST (s00_axi_arburst ),

.M_AXI_ARLOCK (s00_axi_arlock ),

.M_AXI_ARCACHE (s00_axi_arcache ),

.M_AXI_ARPROT (s00_axi_arprot ),

.M_AXI_ARQOS (s00_axi_arqos ),

.M_AXI_ARUSER (s00_axi_aruser ),

.M_AXI_ARVALID (s00_axi_arvalid ),

.M_AXI_ARREADY (s00_axi_arready ),

.M_AXI_RID (s00_axi_rid ),

.M_AXI_RDATA (s00_axi_rdata ),

.M_AXI_RRESP (s00_axi_rresp ),

.M_AXI_RLAST (s00_axi_rlast ),

.M_AXI_RUSER (s00_axi_ruser ),

.M_AXI_RVALID (s00_axi_rvalid ),

.M_AXI_RREADY (s00_axi_rready ),

.MASTER_RST (1'b0 ),

.WR_START (wr_burst_req ),

.WR_ADRS ({wr_burst_addr,3'd0} ),

.WR_LEN ({wr_burst_len,3'd0} ),

.WR_READY ( ),

.WR_FIFO_RE (wr_burst_data_req ),

.WR_FIFO_EMPTY (1'b0 ),

.WR_FIFO_AEMPTY (1'b0 ),

.WR_FIFO_DATA (wr_burst_data ),

.WR_DONE (wr_burst_finish ),

.RD_START (rd_burst_req ),

.RD_ADRS ({rd_burst_addr,3'd0} ),

.RD_LEN ({rd_burst_len,3'd0} ),

.RD_READY ( ),

.RD_FIFO_WE (rd_burst_data_valid ),

.RD_FIFO_FULL (1'b0 ),

.RD_FIFO_AFULL (1'b0 ),

.RD_FIFO_DATA (rd_burst_data ),

.RD_DONE (rd_burst_finish ),

.DEBUG ( )

);

endmodule

 

软件界面

  软件界面结果:

相关文章

Digi-Key