基于 Verilog HDL 设计真彩图的灰度处理模块



引言

FPGA比较擅长的是作定点数整数运算,那么对于带有小数部分的乘加运算。一般都选择先扩大若干倍,而后将运算结果缩小若干倍实现。

应用案例,真彩图转灰度图的心理学计算公式:

Gray = 0.299R + 0.587G + 0.114B

本文给出具体的设计、仿真源码(Verilog HDL)。结合MATLAB平台验证结果的准确性。

Verilog 编译仿真平台:Vivado 2018.3

MATLAB版本:2022a


设计

// ==============================================================================
// 功能描述:真彩图 转 灰度图
// 作 	 者:Xu Y. B.
// 计算公式:Gray = 0.299R + 0.587G + 0.114B
// 思	 路:采用定点运算,先将系数扩大若干倍,最后的结果在缩小若干倍
// ==============================================================================

`timescale 1ns / 1ps

module RGB_2_GRAY_MDL #(
// ---- ---- ---- ---- 模块可重载参数 ---- ---- ---- ---- 
parameter		P_PIXEL_DATA_WIDTH 		= 		24,    //像素数据位宽 ,RGB 分量等位宽
parameter		P_SCALE_FACTOR     		= 		1024,  //系数缩放因子
parameter		P_GRAY_DATA_WIDTH 		=		8      //输出灰度数据位宽
)(
// ---- ---- ---- ----     模块端口    ---- ---- ---- ----
input 											I_OPR_CLK,
input 											I_OPR_RSTN,
input 											I_PIXEL_VAL,
input			[P_PIXEL_DATA_WIDTH-1:0]		I_PIXEL_DATA,// R:MSB B:LSB

output 	reg										O_GRAY_VAL,
output 			[P_GRAY_DATA_WIDTH-1:0]			O_GRAY_DATA

    );
// ---- ---- ---- ----     内部参数    ---- ---- ---- ----
localparam 		LP_RIGHT_SHIFT_VALUE 	=		$clog2(P_SCALE_FACTOR);
integer 		INT_COEF_R				=		0.299*P_SCALE_FACTOR;
integer 		INT_COEF_G				=		0.587*P_SCALE_FACTOR;
integer 		INT_COEF_B				=		0.114*P_SCALE_FACTOR;
localparam 		LP_GRAY_RES_WIDTH 		=		FUNC_CAL_GRAY_RES_WIDTH(P_SCALE_FACTOR,P_PIXEL_DATA_WIDTH);

// ---- ---- ---- ----     内部信号    ---- ---- ---- ----
reg 			[LP_GRAY_RES_WIDTH-1:0] 		R_GRAY_RES;
// ---- ---- ---- ----     内部逻辑    ---- ---- ---- ----
always @ (posedge I_OPR_CLK)
begin
	if(~I_OPR_RSTN)
	begin
		O_GRAY_VAL <= 1'b0;
		R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};
	end
	else
	begin
		if(I_PIXEL_VAL)
		begin
			O_GRAY_VAL <= I_PIXEL_VAL;
			R_GRAY_RES <= (I_PIXEL_DATA[P_PIXEL_DATA_WIDTH-1-:8] * INT_COEF_R
						  +I_PIXEL_DATA[P_PIXEL_DATA_WIDTH/3+:8] * INT_COEF_G
						  +I_PIXEL_DATA[0+:8] * INT_COEF_B)>>LP_RIGHT_SHIFT_VALUE;//此处组合逻辑延迟较大 , 待优化
		end
		else
		begin
			O_GRAY_VAL <= 1'b0;
			R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};			
		end
	end
end
assign O_GRAY_DATA = R_GRAY_RES[0+:P_GRAY_DATA_WIDTH];

// ---- ---- ---- ----     函数/任务   ---- ---- ---- ----
// 计算最终结果的右移数值
function integer FUNC_CAL_GRAY_RES_WIDTH;
	input integer SCALE_FACTOR;
	input integer PIXEL_DATA_WIDTH;

	begin
		FUNC_CAL_GRAY_RES_WIDTH = PIXEL_DATA_WIDTH/3 + $clog2(SCALE_FACTOR) + 1 + 1;
	end
endfunction 

endmodule

设计源码遵循参数化程序设计的规范,可以设置缩放因子以及输出位宽等。在使用时,直接调用即可,不用调整内部逻辑和参数。

注意,3项乘加运算的逻辑在较高频率的时钟下,可能会存在建立时间/保持时间为例。

此处提供一个思路,采用流水线结构进行运算,即先分别计算3项的乘法,然后再计算3项的加法,计算加法时需要注意,由于相加的加数个数不等于2的整数次幂,故可计算两个加数的和,然后将第三个加数延迟一拍后再与和相加,得到最终的结果。如此一来,时序的问题可以缓解,但是带来的后果就是计算延迟增大。 

仿真

// ==============================================================================
// 功能描述:测试 RGB_2_GRAY_MDL 模块
// 作 	 者:Xu Y. B.
// 计算公式:像素数据激励
// 思	 路:
// ==============================================================================


`timescale 1ns / 1ps
module TB_RGB_2_GRAY_MDL();

parameter		P_PIXEL_DATA_WIDTH 		= 		24;    //像素数据位宽 ,RGB 分量等位宽
parameter		P_SCALE_FACTOR     		= 		1024;  //系数缩放因子
parameter		P_GRAY_DATA_WIDTH 		=		8;     //输出灰度数据位宽


reg											I_OPR_CLK;
reg											I_OPR_RSTN;
reg											I_PIXEL_VAL;
reg			[P_PIXEL_DATA_WIDTH-1:0]		I_PIXEL_DATA;// R:MSB B:LSB

wire 										O_GRAY_VAL;
wire 		[P_GRAY_DATA_WIDTH-1:0]			O_GRAY_DATA;

reg 		[P_PIXEL_DATA_WIDTH-1:0] 		R_PIXEL_DATA[24366:1];


// 产生激励时钟
initial I_OPR_CLK = 1'b0;
always #5 I_OPR_CLK = ~I_OPR_CLK;

// 数据读取
initial
begin
	$readmemb("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/IMAGE_PIXEL_DATA.txt",R_PIXEL_DATA);
end

// 复位、数据控制
initial
begin
	I_OPR_RSTN = 1'b0;
	// I_PIXEL_VAL  = 0;
	// I_PIXEL_DATA = 0;
	#109;
	I_OPR_RSTN = 1'b1;
	// @(posedge I_OPR_CLK)
	// I_PIXEL_VAL  <= 1;
	// I_PIXEL_DATA <= {8'd121,8'd99,8'd230};
	// @(posedge I_OPR_CLK)
	// I_PIXEL_VAL  <= 1;
	// I_PIXEL_DATA <= {8'd101,8'd90,8'd210};
	// @(posedge I_OPR_CLK)
	// I_PIXEL_VAL  <= 1;
	// I_PIXEL_DATA <= {8'd151,8'd69,8'd240};
	// @(posedge I_OPR_CLK)
	// I_PIXEL_VAL  <= 1;
	// I_PIXEL_DATA <= {8'd221,8'd109,8'd20};
	@(negedge O_GRAY_VAL)
	I_PIXEL_VAL  <= 0;
	#290;
	$finish;	
end

integer K=1;

always @ (posedge I_OPR_CLK)
begin
	if(~I_OPR_RSTN)
	begin
		I_PIXEL_VAL  <= 0;
		I_PIXEL_DATA <= 0;
	end
	else if(K<=24366)
	begin
		I_PIXEL_VAL  <= 1;
		I_PIXEL_DATA <= R_PIXEL_DATA[K];
		K <= K+1;		
	end
	else
	begin
		K = K;
		I_PIXEL_VAL  <= 0;
		I_PIXEL_DATA <= 0;		
	end
end

integer FILE_ID ;


initial
begin
	FILE_ID = $fopen("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/GRAY_DATA.txt","w+");
	while(~O_GRAY_VAL | ~I_OPR_RSTN) @(posedge I_OPR_CLK);

	while(O_GRAY_VAL)
	begin
		$fdisplayb(FILE_ID,O_GRAY_DATA);
		@(posedge I_OPR_CLK);
	end
	$fclose(FILE_ID);
end

RGB_2_GRAY_MDL #(
		.P_PIXEL_DATA_WIDTH(P_PIXEL_DATA_WIDTH),
		.P_SCALE_FACTOR    (P_SCALE_FACTOR),
		.P_GRAY_DATA_WIDTH (P_GRAY_DATA_WIDTH)
	) INST_RGB_2_GRAY_MDL (
		.I_OPR_CLK    (I_OPR_CLK),
		.I_OPR_RSTN   (I_OPR_RSTN),
		.I_PIXEL_VAL  (I_PIXEL_VAL),
		.I_PIXEL_DATA (I_PIXEL_DATA),
		.O_GRAY_VAL   (O_GRAY_VAL),
		.O_GRAY_DATA  (O_GRAY_DATA)
	);

endmodule

此部分的验证提供了两种方式:

1、已经被注释的部分,只是给几个数据送入模块中验证计算的准确性;

2、读取txt文件中的像素数据,送入模块中进行运算,运算结果保存至另一个txt文件中,然后对比MATLAB和FPGA计算的误差。(此部分具体的实现往下看) 

对比验证

matlab 生成测试数据以及分析对比的源码:

%% ==================== 像素数据读取存储 ====================
% 作者:Xu Y. B.
% 说明:
%       -1- 读取真彩图的RGB数据,拼接为24位二进制数,存入txt文件
%       -2- 对真彩图作灰度处理,并与FPGA处理结果作对比
% =============================================================

%% CLEAR
clc;
clearvars;
close all;

%% 图片读取
FILE_PATH = "C:\Users\XYB\Pictures\高清壁纸Z\wallhaven-5758y8.jpg";
IMAGE_DATA = imread (FILE_PATH,"jpg");
figure;
imshow(IMAGE_DATA);
title("原真彩图")
% 图片截取
PIXEL_DATA = IMAGE_DATA(240:425,868:998,:);
figure;
subplot(121)
imshow(PIXEL_DATA);
title("截取的真彩图")
subplot(122)
IM2GRAY = im2gray(PIXEL_DATA);
imshow(IM2GRAY)
title("截取的真彩图转灰度图")

%% 数据转化存储
PIXEL_DATA_R = PIXEL_DATA(:,:,1);
PIXEL_DATA_G = PIXEL_DATA(:,:,2);
PIXEL_DATA_B = PIXEL_DATA(:,:,3);

PIXEL_DATA_R_BIN = dec2bin(reshape(PIXEL_DATA_R,[],1));
PIXEL_DATA_G_BIN = dec2bin(reshape(PIXEL_DATA_G,[],1));
PIXEL_DATA_B_BIN = dec2bin(reshape(PIXEL_DATA_B,[],1));

PIXEL_DATA_BIN   = strcat(strcat(PIXEL_DATA_R_BIN,PIXEL_DATA_G_BIN),PIXEL_DATA_B_BIN);

WRITE_PATH = "D:\A_Vivado_WorkSpace\DSP_BASIC_STUDY\MAT_FILE\IMAGE_PIXEL_DATA.txt";
writematrix(PIXEL_DATA_BIN,WRITE_PATH,"WriteMode","overwrite","FileType","text")

%% FPGA处理结果读取
READ_PATH = "D:\A_Vivado_WorkSpace\DSP_BASIC_STUDY\MAT_FILE\GRAY_DATA.txt";
GRAY_DATA_FPGA = uint8(bin2dec(readmatrix(READ_PATH,"OutputType","string")));
GRAY_IMAG = reshape(GRAY_DATA_FPGA,186,[]);
figure;
subplot(121)
imshow(GRAY_IMAG)
title("FPGA处理结果")
subplot(122)
imshow(IM2GRAY)
title("MATLAB处理结果")
GRAY_PROC_ERR = uint8(abs(double(GRAY_IMAG)-double(IM2GRAY)));
figure;
mesh(GRAY_PROC_ERR);
title("FPGA MATLAB处理结果差异三维图")
figure;
imagesc(GRAY_PROC_ERR)
title("FPGA MATLAB处理结果差异平面")

原图:

图片截取以及MATLAB的灰度处理:

FPGA处理结果与MATLAB处理结果对比:

误差分析:

综合以上的分析验证结果,基于Verilog HDL 设计的模块,可以准确地将真彩图转化为灰度图,与MATLAB计算结果相差的最大值为1,大概率是由于四舍五入的精度问题导致。此问题只能通过增大缩放因子来缓解,比如将1024倍的缩放因子改为2048。

说明

以上所有代码中对应的文件路径都需要个性化更改,不可以直接复制粘贴使用,否则会报错:找不到路径对应的文件。

由于本人能力有限,如果有更好的想法或者在使用中遇到问题,都可以在评论区里留言交流~~~

猜你喜欢

转载自blog.csdn.net/qq_43045275/article/details/130182104