帧同步信号:
// ---------------------------------------------
// Frame and line housekeeping
// ---------------------------------------------
reg vs_frame_d0;
always @(posedge video_clk or posedge rst) begin
if (rst) begin
vs_frame_d0 <= 1'b0;
end else begin
vs_frame_d0 <= i_vs;
end
end
wire frame_start = vs_frame_d0 & ~i_vs; // 场同步下降沿(一帧开始)
reg prev_de;
always @(posedge video_clk or posedge rst) begin
if (rst) begin
prev_de <= 1'b0;
end else begin
prev_de <= i_de;
end
end
wire line_start = i_de & ~prev_de; // 数据有效信号上升沿(一行开始)
wire line_end = ~i_de & prev_de; // 数据有效信号下降沿(一行结束)
reg [11:0] col_cnt; // 列计数器
reg [11:0] row_cnt; // 行计数器
对
i_vs(场同步)和 i_de(数据有效)信号进行边沿检测,生成 frame_start(帧开始)和 line_start(行开始)信号。使用计数器 col_cnt 和 row_cnt 跟踪当前像素的列和行位置。
input video_clk,
input rst,
input i_hs,
input i_vs,
input i_de,
input [DATA_WIDTH-1:0] i_data,
input sw_gray,
input sw_filter,
input sw_edge,
input sw_dilate,
// 新增按钮检测相关信号
input btn_detect_en, // 按钮检测使能
output reg btn_pressed, // 按钮按下状态
output reg detection_active, // 检测进行中状态
output reg [11:0] btn_pixel_count, // 按钮区域内像素计数
output o_hs,
output o_vs,
output o_de,
output reg [DATA_WIDTH-1:0] o_dataDATA_WIDTH:输入视频数据宽度(24 位 RGB)。H_ACTIVE:视频水平有效像素数。THRESHOLD:二值化阈值。BTN_*:按钮检测区域的坐标参数。video_clk:视频像素时钟。rst:全局复位。i_hs/i_vs/i_de:输入视频的行同步、场同步和数据有效信号。i_data:输入的 24 位 RGB 像素数据。sw_*:功能选择开关(灰度、腐蚀、边缘检测、膨胀)。btn_detect_en:按钮检测使能信号。btn_pressed:按钮按下状态输出。o_hs/o_vs/o_de/o_data:处理后的视频输出信号。// ---------------------------------------------
// RGB -> Grayscale conversion for image processing
// ---------------------------------------------
wire [7:0] r_comp = i_data[23:16];
wire [7:0] g_comp = i_data[15:8];
wire [7:0] b_comp = i_data[7:0];
wire [17:0] gray_sum = (r_comp * 77) + (g_comp * 150) + (b_comp * 29);
wire [7:0] gray_value = (gray_sum + 128) >> 8; // 灰度值(标准ITU-R 601转换公式)
将 24 位 RGB 像素转换为 8 位灰度值。转换公式:
Y = 0.299*R + 0.587*G + 0.114*B,为了避免浮点数运算,使用整数乘法实现(77=299/4, 150=587/4, 29=114/4)。结果右移 8 位(除以 256)并加上 128 进行四舍五入。
// ---------------------------------------------
// 二值化处理
// ---------------------------------------------
wire binary_pixel = (gray_value > THRESHOLD) ? 1'b1 : 1'b0;
将灰度值转换为二值图像(1 表示白色,0 表示黑色)。阈值由参数
THRESHOLD 设定(默认 128)。
// ------------------------------------------------------------- //
// Single-line delay for streaming video data
// ------------------------------------------------------------- //
module line_delay
#(
parameter DATA_WIDTH = 8,
parameter LINE_LENGTH = 1024
)
(
input clk,
input rst,
input frame_start,
input enable,
input [DATA_WIDTH-1:0] din,
output reg [DATA_WIDTH-1:0] dout,
output reg valid
);
reg [DATA_WIDTH-1:0] buffer [0:LINE_LENGTH-1]; // 行缓冲
reg [ADDR_WIDTH-1:0] addr; // 地址计数器
reg filled; // 缓冲填满标志
always @(posedge clk or posedge rst) begin
if (rst) begin
addr <= 0;
filled <= 1'b0;
valid <= 1'b0;
end else begin
if (frame_start) begin
addr <= 0;
filled <= 1'b0;
valid <= 1'b0;
end
if (enable) begin
dout <= buffer[addr]; // 输出延迟一行的数据
buffer[addr] <= din; // 写入当前数据
addr <= addr + 1;
if (addr == LINE_LAST) begin
filled <= 1'b1; // 第一行写入完成后,后续输出有效
end
valid <= filled; // 缓冲填满后,输出有效
end else begin
valid <= 1'b0;
end
end
end
endmodule
实现一行像素的延迟,用于构建 3x3 窗口(需要当前行、上一行和上上行的数据)。使用一个寄存器数组
buffer 存储一行像素。enable 信号由 i_de 控制,只有在数据有效时才写入 / 读出。
valid 信号表示输出数据是否有效(前两行数据无效,从第三行开始有效)。应用:在 image_processing 模块中被实例化两次,分别用于存储二值图像和灰度图像的行数据。
// ---------------------------------------------
// Erosion algorithm for binary image
// ---------------------------------------------
wire erosion_binary;
assign erosion_binary = r0c0_bin & r0c1_bin & r0c2_bin &
r1c0_bin & r1c1_bin & r1c2_bin &
r2c0_bin & r2c1_bin & r2c2_bin;
wire [7:0] erosion_value = erosion_binary ? 8'hFF : 8'h00;
对二值图像进行腐蚀操作,用于消除小的白色噪点。原理:3x3 窗口内所有像素都为 1 时,输出才为 1,否则为 0。结果是使白色区域收缩。
// ---------------------------------------------
// Dilation algorithm for binary image
// ---------------------------------------------
wire dilation_binary;
wire row0_or = r0c0_bin | r0c1_bin | r0c2_bin;
wire row1_or = r1c0_bin | r1c1_bin | r1c2_bin;
wire row2_or = r2c0_bin | r2c1_bin | r2c2_bin;
assign dilation_binary = row0_or | row1_or | row2_or;
wire [7:0] dilation_value = dilation_binary ? 8'hFF : 8'h00;
对二值图像进行膨胀操作,用于填补白色区域的空洞。原理:3x3 窗口内任意一个像素为 1 时,输出就为 1。结果是使白色区域扩张。
// ---------------------------------------------
// Sobel edge detection (使用灰度数据)
// ---------------------------------------------
// 计算 Sobel 算子的 Gx 和 Gy
wire signed [11:0] sobel_gx = (r0c2_s + (r1c2_s <<< 1) + r2c2_s) -
(r0c0_s + (r1c0_s <<< 1) + r2c0_s);
wire signed [11:0] sobel_gy = (r2c0_s + (r2c1_s <<< 1) + r2c2_s) -
(r0c0_s + (r0c1_s <<< 1) + r0c2_s);
// 计算梯度绝对值之和
wire [11:0] abs_gx = (sobel_gx < 0) ? -sobel_gx : sobel_gx;
wire [11:0] abs_gy = (sobel_gy < 0) ? -sobel_gy : sobel_gy;
wire [12:0] edge_sum = abs_gx + abs_gy;
// 边缘强度阈值判断
wire [7:0] edge_value = (edge_sum > 255) ? 8'hFF : edge_sum[7:0];
对灰度图像进行边缘检测,找出图像中的轮廓。原理:使用 Sobel 算子计算水平(Gx)和垂直(Gy)方向的梯度,然后求梯度的绝对值之和。如果梯度和超过 255,则输出白色(边缘),否则输出对应的灰度值。
// ---------------------------------------------
// 结果选择与输出
// ---------------------------------------------
always @* begin
mux_data = color_rgb; // 默认输出原始彩色图像
mux_de = color_de;
if (btn_detect_en) begin
mux_data = btn_highlight_rgb; // 按钮检测模式下高亮显示按钮区域
mux_de = btn_highlight_de;
end else if (sw_edge) begin
mux_data = edge_rgb; // 边缘检测结果
mux_de = edge_de;
end else if (sw_dilate) begin
mux_data = dilation_rgb; // 膨胀结果
mux_de = dilation_de;
end else if (sw_filter) begin
mux_data = erosion_rgb; // 腐蚀结果
mux_de = erosion_de;
end else if (sw_gray) begin
mux_data = gray_rgb; // 灰度图像
mux_de = gray_de;
end
end
// 输出同步
always @(posedge video_clk or posedge rst) begin
if (rst) begin
o_data <= 0;
o_de_r <= 1'b0;
end else begin
o_data <= mux_data;
o_de_r <= mux_de;
end
end
assign o_hs = hs_d0; // 行同步延迟一拍(与数据对齐)
assign o_vs = vs_d0; // 场同步延迟一拍
assign o_de = o_de_r;
根据开关信号(
sw_*)和按钮检测使能(btn_detect_en)选择不同的处理结果输出。优先级:按钮检测 > 边缘检测 > 膨胀 > 腐蚀 > 灰度 > 原始彩色。
为了保证输出的同步性,对行同步(
hs_d0)和场同步(vs_d0)信号进行了一拍延迟,确保它们与处理后的数据对齐。
扫码加好友,拉您进群



收藏
