基于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, //初始化ov5640
cmos_v_pixel,
total_h_pixel,
total_v_pixel);//设置OV5640输出分辨率为1280*720 PCLK = 72Mhz
if(status == 0)
;
//xil_printf("OV5640 init successful!\r\n");
else
xil_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;
//配置VDMA
run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
frame_buffer_addr,0,0,BOTH);
//初始化Display controller
DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
//设置VideoMode
DisplaySetMode(&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阈值改成能用串口实时调节的数值,是完全可以实现的。