基于EBAZ4205矿板的图像处理:07sobel边缘检测算法
项目文件
随后会上传项目全部文件,和之前一样免费下载
先看效果
如上所见,能够提取图像的边缘,这个sobel边缘检测算法的阈值(认定是否为边缘的阈值)一样是可调的,上图是96,下图是153.
算法讲解
sobel边缘检测,实际上就是对g=f(x,y)这函数求导,其中x和y是像素点的坐标,g是像素点的灰度值,求导嘛,就是找出像素随着位置变化的梯度,像素数值变化大的地方自然就是图像中的边缘、边界了。
算法的FPGA部署
具体到FPGA中自然还是卷积运算,用滑动窗口模块输出一个滑动窗口的所用像素的像素值,然后和sobel卷积模板进行卷积运算,运算后的结果进行阈值比较,大于阈值的才认为是图像的”边缘” 。
先是卷积乘法,然后把乘法的结果加在一起。再开方。开方的结果和阈值比较。
这里使用的是韩彬老师开源的代码,他的那本基于MATLAB与FPGA的图像处理教程,强烈推荐。
代码分享
图像处理顶层模块
module video_processor((* X_INTERFACE_IGNORE = "true" *) input frame_clk, //cmos 像素时钟(* X_INTERFACE_IGNORE = "true" *) input frame_rst_n, //预处理图像(* X_INTERFACE_IGNORE = "true" *) input pre_vsync, //预处理图像场同步信号(* X_INTERFACE_IGNORE = "true" *) input [23:0] pre_data, //预处理图像数据(* X_INTERFACE_IGNORE = "true" *) input pre_href, //预处理图像数据有效信号(* X_INTERFACE_IGNORE = "true" *) input pre_frame_ce, //预处理图像时钟使能信号//阈值控制(* X_INTERFACE_IGNORE = "true" *) input [7:0 ] loc_bin_thresh_coefficient, //来自PS端的局部二值化阈值系数(* X_INTERFACE_IGNORE = "true" *) input [7:0 ] sobel_thresh,//处理后图像(* X_INTERFACE_IGNORE = "true" *) output pos_vsync, //处理后图像场同步信号(* X_INTERFACE_IGNORE = "true" *) output [23:0] pos_data, //处理后图像数据(* X_INTERFACE_IGNORE = "true" *) output pos_href, //处理后图像数据有效信号(* X_INTERFACE_IGNORE = "true" *) output pos_frame_ce //处理后图像时钟使能信号
);//wire define
wire [7:0] gray_data ;
wire gray_vsync;
wire gray_frame_ce;
wire gray_href;//*****************************************************
//** main code
//*****************************************************
//rgb转ycbcr模块
rgb2gray u_rgb2gray(.cmos_frame_clk (frame_clk ),.cmos_rstn (frame_rst_n ),//同步复位.cmos_frame_vsync (pre_vsync ),.cmos_frame_data (pre_data ),.cmos_frame_href (pre_href ),.cmos_frame_ce (pre_frame_ce ),.dataout_frame_vsync(gray_vsync ),.dataout_frame_data (gray_data ),.dataout_frame_href (gray_href ),.dataout_frame_ce (gray_frame_ce )
);
//wire define
wire matrix_frame_vsync;
wire matrix_frame_href;
wire matrix_frame_ce;
wire [7:0] matrix_p11; //3X3 矩阵数据
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;VIP_matrix_generate_3x3_8bit u_VIP_matrix_generate_3x3_8bit(.clk (frame_clk ), .rst_n (frame_rst_n ),.per_frame_vsync (gray_vsync ),.per_frame_href (gray_href ), .per_frame_ce (gray_frame_ce ),.per_img_Y (gray_data ),//输出3x3矩阵.matrix_frame_vsync (matrix_frame_vsync ),.matrix_frame_href (matrix_frame_href ),.matrix_frame_ce (matrix_frame_ce ),.matrix_p11 (matrix_p11), .matrix_p12 (matrix_p12), .matrix_p13 (matrix_p13),.matrix_p21 (matrix_p21), .matrix_p22 (matrix_p22), .matrix_p23 (matrix_p23),.matrix_p31 (matrix_p31), .matrix_p32 (matrix_p32), .matrix_p33 (matrix_p33)
);
sobel_filter u_sobel_filter(.clk (frame_clk ),.rst_n (frame_rst_n ),.matrix_img_vsync (matrix_frame_vsync ),.matrix_img_href (matrix_frame_href ),.matrix_frame_ce (matrix_frame_ce ),.sobel_thresh(sobel_thresh),.matrix_p11 (matrix_p11 ),.matrix_p12 (matrix_p12 ),.matrix_p13 (matrix_p13 ),.matrix_p21 (matrix_p21 ),.matrix_p22 (matrix_p22 ),.matrix_p23 (matrix_p23 ),.matrix_p31 (matrix_p31 ),.matrix_p32 (matrix_p32 ),.matrix_p33 (matrix_p33 ),.dataout_vsync (pos_vsync ), .dataout_href (pos_href ), .dataout_gray (pos_data ), .dataout_frame_ce (pos_frame_ce ));
endmodule
我的vitis的代码
//作者:抢公主的大魔王
//功能:sobel边缘检测
//日期:24.5.17
//版本:1v0
//联系方式:2376635586@qq.com
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "display_ctrl_hdmi/display_ctrl.h"
#include "vdma_api/vdma_api.h"
#include "emio_sccb_cfg/emio_sccb_cfg.h"
#include "ov5640/ov5640_init.h"
#include "sleep.h"
#include "xuartps.h"
#include "string.h"//宏定义
#define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //动态时钟基地址
#define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
#define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
#define UART_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID //串口设备ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR //串口中断ID
#define THRESHOLD_BASEADDR XPAR_AXICTRLTHRESHOLD_0_S00_AXI_BASEADDR#define EMIO_SCL_NUM 54
#define EMIO_SDA_NUM 55
#define KEY1 56 //T19
#define KEY2 57 //P19
#define KEY3 58 //U20
#define KEY4 59 //U19
#define KEY5 60 //V20
#define LED1 61 //H18
#define LED2 62 //K17
#define LED3 63 //E19#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;
#define GPIO_BANK XGPIOPS_BANK0 /* Bank 0 of the GPIO Device */
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR//全局变量
//frame buffer的起始地址
unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR+ 0x1000000);
//u8 binary_threshold = 128;
u8 sobel_threshold = 153;XAxiVdma vdma;
DisplayCtrl dispCtrl;
VideoMode vd_mode;void Gpio_Init(void){XGpioPs_Config *ConfigPtr;ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);XGpioPs_SetDirectionPin(&Gpio, LED1, 1);XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1);XGpioPs_WritePin(&Gpio, LED1, 0);}int main(void)
{u32 status;u16 cmos_h_pixel; //ov5640 DVP 输出水平像素点数u16 cmos_v_pixel; //ov5640 DVP 输出垂直像素点数u16 total_h_pixel; //ov5640 水平总像素大小u16 total_v_pixel; //ov5640 垂直总像素大小cmos_h_pixel = 1280;cmos_v_pixel = 720;total_h_pixel = 2570;total_v_pixel = 980;emio_init();//控制hdmi的emio初始化//xil_printf("Uart and Key is init successful! \r\n");//xil_printf("ov5640 is initing! \r\n");status = ov5640_init( cmos_h_pixel, //初始化ov5640cmos_v_pixel,total_h_pixel,total_v_pixel);//设置OV5640输出分辨率为1280*720 PCLK = 72Mhzif(status == 0);//xil_printf("OV5640 init successful!\r\n");elsexil_printf("OV5640 detected failed!\r\n");xil_printf("Uart and OV5640 is init successful! \r\nInput any to run!\r\n");sleep(1);vd_mode = VMODE_1280x720;//配置VDMArun_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,frame_buffer_addr,0,0,BOTH);//初始化Display controllerDisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);//设置VideoModeDisplaySetMode(&dispCtrl, &vd_mode);DisplayStart(&dispCtrl);Gpio_Init();//按键和led的初始化//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);Xil_Out32(THRESHOLD_BASEADDR+4, sobel_threshold);while(1){XGpioPs_WritePin(&Gpio, LED1, !XGpioPs_ReadPin(&Gpio, LED3));sleep(1);}return 0;
}
可以参考我的前几篇博客,把那个sobel阈值改成能用串口实时调节的数值,是完全可以实现的。