Design of Grayscale Processing Module of True Color Image Based on Verilog HDL



introduction

FPGA is better at doing fixed-point integer operations, so for multiplication and addition operations with fractional parts. Generally choose to expand several times first, and then reduce the operation result by several times to achieve.

Application case, psychological calculation formula for converting true color image to grayscale image:

Gray = 0.299R + 0.587G + 0.114B

This article gives the specific design and simulation source code (Verilog HDL). Combined with the MATLAB platform to verify the accuracy of the results.

Verilog compilation simulation platform: Vivado 2018.3

MATLAB version: 2022a


design

// ==============================================================================
// 功能描述:真彩图 转 灰度图
// 作 	 者: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

The design source code follows the specification of parametric programming, and the scaling factor and output bit width can be set. When in use, it can be called directly without adjusting the internal logic and parameters.

Note that the logic of the three multiplication and addition operations may have a setup time/hold time as an example at a higher frequency clock.

Here is an idea to use the pipeline structure for calculation, that is, first calculate the multiplication of the three items, and then calculate the addition of the three items. When calculating the addition, you need to pay attention, because the number of addends to be added is not equal to the integer power of 2 , so the sum of the two addends can be calculated, and then the third addend is delayed by one beat and then added to the sum to obtain the final result. In this way, the timing problem can be alleviated, but the consequence is that the calculation delay increases. 

simulation

// ==============================================================================
// 功能描述:测试 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

The verification of this part provides two ways:

1. The part that has been annotated is just to send a few data into the module to verify the accuracy of the calculation;

2. Read the pixel data in the txt file, send it to the module for calculation, save the calculation result to another txt file, and then compare the errors calculated by MATLAB and FPGA. (See below for the specific implementation of this part) 

Compare and verify

Matlab generates test data and source code for analysis and comparison:

%% ==================== 像素数据读取存储 ====================
% 作者: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处理结果差异平面")

Original image:

Image interception and grayscale processing of MATLAB:

Comparison of FPGA processing results with MATLAB processing results:

Error Analysis:

Based on the above analysis and verification results, the module designed based on Verilog HDL can accurately convert the true color image into a grayscale image, and the maximum difference from the MATLAB calculation result is 1, which is most likely due to the rounding accuracy problem. This problem can only be alleviated by increasing the scaling factor, such as changing the scaling factor from 1024 to 2048.

illustrate

The corresponding file paths in all the above codes need to be personalized and cannot be copied and pasted directly, otherwise an error will be reported: the file corresponding to the path cannot be found.

Due to my limited ability, if you have better ideas or encounter problems in use, you can leave a message in the comment area to communicate~~~

Guess you like

Origin blog.csdn.net/qq_43045275/article/details/130182104