视觉图像信息处理与FPGA实现第九次作业——直方图均衡

RAM的B站视频解析

RAM的文档

一、65536x8位的单端口RAM

`timescale 1ns / 1ps
//SPRF Single Port Read/Write Function
//65535 是RAM中总的字数,也就是存储深度,X8表示每个字是8位的
module SPRF65536X8(
  Q,
  CLK,
  CEN,
  WEN,
  A,
  D
);

  //输出寄存器Q
  output [7:0]	Q;
  input 		CLK;
  //chip enable 片上使能
  input			CEN;
  //write enable 写使能
  input			WEN;
  //16位地址输入
  input [15:0]	A;
  //8位数据输入
  input [7:0]	D;
 
  reg [7:0] 	mem[0:65535];
  reg [7:0] 	Q;
 
 //控制存储体men[]
 //低电平触发CEN和WEN
  always @ (posedge CLK)
	if(!CEN & !WEN)
	  mem[A] <= D;
  

  always @ (posedge CLK)
  //片上使能OK,写使能不OK的时候
	if(!CEN & WEN)
	  Q <= mem[A];
	else if(!CEN & !WEN)
	  Q <= D;
	else
	  Q <= Q;
	  
endmodule

二、256*11的双端口RAM

双端口RAM相比单端口RAM,允许同时进行读写操作,读写过程不会相互影响。这使得它更适用于需要并行读写访问的应用场景,如FIFO(先进先出队列)、缓存等。但相应的,双端口RAM的资源消耗也更高。

`timescale 1ns / 1ps

// Two Port Read/Write Function
// 256 deep , 11bit  
// AA: 8位读地址输入端口A
// AB: 8位写地址输入端口B
// DB: 11位数据输入端口B
// CENA: 读使能端口A
// CENB: 写使能端口B
// CLKA: 读时钟输入端口A
// CLKB: 写时钟输入端口B
// QA: 11位数据输出端口A
module TPRF256X11 (
  AA,
  AB,
  DB,
  CENA,
  CENB,
  CLKA,
  CLKB,
  QA
);

  input [7:0]	AA;
  input [7:0]	AB;
  input			CENA;
  input			CENB;
  input 		CLKA;
  input 		CLKB;
  input [10:0]	DB;
  output [10:0]	QA;
 
  reg [10:0] 	mem[0:255];
  reg [10:0] 	QA;
 
 //当B写使能OK时,(DB: 11位数据输入端口B)把值给到
 //存储器的men(AB) ,(AB: 8位写地址输入端口B)
  always @ (posedge CLKB)
	if(!CENB)
	  mem[AB] <= DB;
  

 //当A读使能OK时,(AA: 8位读地址输入端口A)把值给到
 //QA (QA: 11位数据输出端口A)
  always @ (posedge CLKA)
	if(!CENA)
	  QA <= mem[AA];
	else
	  QA <= QA;

endmodule
//最终实现在CLKA和CLKB两个时钟域里面完成写和读的同时进行

三、直方图均衡代码

代码中有四处需要修改的地方,可以在vscode搜索“修改”即可找到需要修改的参数,需要根据灰度图的分辨率,调整相应的参数。以下是200*200灰度图直方图均衡的代码。

`timescale 1ns / 1ps
//*****************************************************************************	
//
// Project           	: FPGA image process class
// Module	    		: histogram_equalization    
// Description			: This module is the histogram equalization of image.
//    
// FileName	     		: histogram_equalization.v   
// Call Modules      	: -
// Called by Modules 	: histogram_equalization_tb.v 
//
// --------------------------------------------------------------------------
//
// Created Date      	: 2022.06.27
// Author            	:     Tester:        Supervisor:
// Revision History  	: V1.0 2022.06.27
//
//****************************************************************************

module histogram_equalization
//======================<port>=====================
(
//======================<input>====================
  input					clk,
  input					rst_n,
  input					valid_in,
  input					image_write_done,
  input					bmp_write_done,
  input			[7:0]	point_data_in,  
//======================<output>===================
  output		[7:0]	point_data_out,
  output	reg			init_done,
  output	reg			data_read_start,
  output	reg			data_read_done

);

  parameter				ST_IDLE = 3'd0;
  parameter				ST_INIT = 3'd1;
  parameter				ST_SAMPLE = 3'd2;
  parameter				ST_COUNT = 3'd3;
  parameter				ST_UPDATE = 3'd4;
  parameter				ST_WRITE_BMP = 3'd5;
  
//======================<signal>===================
  reg 			[2:0]	state;
  reg 			[2:0]	count;
  reg 					count_done;
  reg 			[15:0]	count_sum;
  reg 					update_done;
  
  wire 					image_process_done;
  wire 			[27:0]	update_conut_temp;
  wire 			[7:0]	update_conut;

  // sample memory
  //采样使能信号
  reg 					sample_cen;  
  reg 					sample_wen;  
  reg 			[15:0]	sample_addr;
  wire 			[7:0]	sample_wr_data;
  wire 			[7:0]	sample_rd_data;
  reg 			[7:0]	sample_rd_data_d1;
  reg 			[7:0]	sample_rd_data_d2;
  
  // count memory
  reg 					count_wr_cen;   
  reg 			[7:0]	count_wr_addr;
  reg 			[10:0]	count_wr_data;
  
  reg 					count_rd_cen;   
  reg 			[7:0]	count_rd_addr;
  wire 			[10:0]	count_rd_data;
  
  reg 			[7:0]	count_rd_addr_d1;

  assign sample_wr_data = point_data_in;

  always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		state <= ST_IDLE;
	end
	else
		case(state)
			ST_IDLE: begin
				if(valid_in)
					state <= ST_INIT;
				else
					state <= ST_IDLE;
			end
			ST_INIT: begin
				if(init_done)
					state <= ST_SAMPLE;
				else
					state <= ST_INIT;
			end
			ST_SAMPLE: begin
				if(bmp_write_done)
					state <= ST_COUNT;
				else
					state <= ST_SAMPLE;
			end
			ST_COUNT: begin
				if(count_done)
					state <= ST_UPDATE;
				else
					state <= ST_COUNT;
			end
			ST_UPDATE: begin
				if(update_done)
					state <= ST_WRITE_BMP;
				else	
					state <= ST_UPDATE;
			end
			ST_WRITE_BMP: begin
				if(data_read_done)
					state <= ST_IDLE;
				else	
					state <= ST_WRITE_BMP;
			end
		endcase
  end

  always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		count_wr_cen <= 1'b1;
		count_wr_addr <= 8'b0;
		count_wr_data <= 11'b0;
		count_rd_cen <= 1'b1;
		count_rd_addr <= 8'b0;
		init_done <= 1'b0;
		sample_cen <= 1'b1;
		sample_wen <= 1'b0;
		sample_addr <= 16'b0;
		//读地址计数器,d1应该是delay 1个周期
		count_rd_addr_d1 <= 8'b0;
		count_done <= 1'b0;
		count <= 3'b0;
		count_sum <= 16'b0;
		update_done <= 1'b0;
		sample_rd_data_d1 <= 8'b0;
		sample_rd_data_d2 <= 8'b0;
		data_read_start <= 1'b0;
		data_read_done <= 1'b0;
	end
	else 
		case(state)
			ST_IDLE: begin
				count_wr_cen <= 1'b0;
			end	
			ST_INIT: begin
				// start to init
				count_wr_addr <= count_wr_addr + 1'b1;
				if(count_wr_addr == 255) begin
					// init done
					init_done <= 1'b1;
					// finish init
					count_wr_cen <= 1'b1;
					count_wr_addr <= 0;
					// start to sample
					sample_cen <= 1'b0;
				end
			end	
			ST_SAMPLE: begin
				// sample 

				
				sample_addr <= sample_addr + 1'b1;
				if(bmp_write_done) begin
					//需要修改
					sample_addr <= 1078;  // 数据起始索引地址data_start_index,由BMP图片格式规定
					sample_wen <= 1'b1;
					// start to count
					count <= count + 1'b1;
				end
				
			end		
			ST_COUNT: begin
				// count
				count <= count + 1'b1;
				if(count == 5)
					count <= 0;
				
				case(count)
					// read the sample data
					0: begin
						sample_cen <= 1'b0;
						sample_addr <= sample_addr + 1'b1;
					end
					// read the count 
					1: begin
						sample_cen <= 1'b1;
					end
					// count the data
					2: begin
						count_rd_cen <= 1'b0;
						count_rd_addr <= sample_rd_data;
					end
					3: begin
						count_rd_cen <= 1'b1;
					end
					// write back
					4: begin
						count_wr_cen <= 1'b0;
						count_wr_data <= count_rd_data + 1'b1;
						count_wr_addr <= count_rd_addr;
						//需要修改
						if(sample_addr == 41078) begin //读入的BMP图片的所有数据地址 file_read = $fread(bmp_data,bmp_file_read)
							sample_addr <= 0;
							count_done <= 1'b1;
							count_wr_addr <= 0;
							count_rd_cen <= 1'b0;
							count_rd_addr <= 0;
							count_wr_cen <= 1'b1;
						end
					end
					5: begin
						count_wr_cen <= 1'b1;
					end				
				endcase
			end		
			ST_UPDATE: begin
				// read the count data
				count_rd_addr <= count_rd_addr + 1'b1;
				count_sum <= count_sum + count_rd_data;
				// update and write
				count_wr_cen <= count_rd_cen;
				count_rd_addr_d1 <= count_rd_addr;
				count_wr_addr <= count_rd_addr_d1;
				count_wr_data <= update_conut;
				if(count_wr_addr == 255) begin
					update_done <= 1'b1;
					count_rd_cen <= 1'b1;
					count_wr_cen <= 1'b1;
					// start to write bmp
					sample_cen <= 1'b0;
					sample_wen <= 1'b1;
					sample_addr <= 0;
				end

			end	
			ST_WRITE_BMP: begin
				//需要修改
				if(sample_addr < 1078) begin // 地址小于1078时为BMP的头文件数据
					sample_addr <= sample_addr + 1;
					sample_rd_data_d1 <= sample_rd_data;
					sample_rd_data_d2 <= sample_rd_data_d1;
					if(sample_addr == 1)
						data_read_start <= 1'b1;
				end
				else begin
					sample_addr <= sample_addr + 1;
					sample_rd_data_d1 <= sample_rd_data;
					sample_rd_data_d2 <= sample_rd_data_d1;
					
					count_rd_cen <= 1'b0;
					count_rd_addr <= sample_rd_data;
					//修改为400*400的结束数据,这个是200*200的
					if(sample_addr == 41079) begin // 除BMP的头文件数据的其余数据
						data_read_done <= 1'b1;
						data_read_start <= 1'b0;
						sample_cen <= 1'b1;
						count_rd_cen <= 1'b1;
					end
				end
			end	
		endcase
  end

  // 255/40000 * count_sum
  // 20bit fixed,这里应该是为了保证灰度的值在255之内,同时又要避免除法
  assign update_conut_temp = 6684 * count_sum; //20240327:255/4000x1024x1024=6684
  assign update_conut = update_conut_temp[27:20]; 
  //需要修改 1081
  //  
  assign point_data_out = ~update_done ? 0 : (sample_addr < 1081)? sample_rd_data_d2 : count_rd_data;


  SPRF65536X8 u_sample_mem(
	.Q		(sample_rd_data),
	.CLK	(clk),
	.CEN	(sample_cen),
	.WEN	(sample_wen),
	.A		(sample_addr),
	.D		(sample_wr_data)
  );


  TPRF256X11 u_count_mem(
	.AA		(count_rd_addr),
	.AB		(count_wr_addr),
	.DB		(count_wr_data),
	.CENA	(count_rd_cen),
	.CENB	(count_wr_cen),
	.CLKA	(clk),
	.CLKB	(clk),
	.QA		(count_rd_data)
  );


endmodule

四、tb文件

因为vivado的testbench使用$open函数的路径问题,详情见我的CSDN博客:https://blog.csdn.net/weixin_44357071/article/details/137203642

,我的操作如下:

  • 代码正常放进来,然后点一下vivado里面的"run simulation"仿真(产生sim1这个文件夹)

  • 然后把图片放在正确的地方,我的路径如图image-20240507131542692

`timescale 1ns / 1ps
//*****************************************************************************	
//
// Project           	: FPGA image process class
// Module	    		: histogram_equalization_tb     
// Description			: This module is the tb module of histogram equalization.
//    
// FileName	     		: histogram_equalization_tb.v   
// Call Modules      	: histogram_equalization.v
// Called by Modules 	: -
//
// --------------------------------------------------------------------------
//
// Created Date      	: 2022.06.19
// Author            	:     Tester:        Supervisor:
// Revision History  	: V1.0 2022.06.19
//
//****************************************************************************
`define Clock 20

module histogram_equalization_tb;

//======================<port>=================================
  reg						clk;
  reg						rst_n;
  reg		[7:0]			point_data_in;
  reg						image_write_done;
  reg						bmp_write_done;
  reg						valid_in;
  reg 		[7:0] 			bmp_data[0:50000];
  reg						data_read_start_d1;
  
  wire		[7:0]			point_data_out;
  wire		[7:0]			bmp_data_out;
  wire						init_done;
  wire						data_read_done;
  wire						data_read_start;

//======================<clock and reset>======================
  initial begin
	clk = 1;
	forever
		#(`Clock/2) clk = ~clk;
  end

  initial begin
	rst_n = 0;
	#(`Clock*20 + 1);
	rst_n = 1;
end

// read the bmp data
  integer bmp_file_read;
  integer file_read;
  integer data_start_index;
  integer bmp_size;

  initial begin
	bmp_file_read = $fopen(".\\picture_copy.bmp","rb");
	file_read = $fread(bmp_data,bmp_file_read);
	// get the data start index
	data_start_index = {bmp_data[13], bmp_data[12], bmp_data[11], bmp_data[10]};
	// get the bmp size
	bmp_size = {bmp_data[5], bmp_data[4], bmp_data[3], bmp_data[2]};
  end


//======================<input signal>=========================
  initial begin
  end  

  integer index;
  always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		index <= 0;
		image_write_done <= 0;
		bmp_write_done <= 0;
		point_data_in <= 0;
		valid_in <= 0;
	end
	// when index=data_start_index, it starts to process image
	else begin
		valid_in <= 1;
		if(init_done) begin
			if (index == data_start_index) begin
				image_write_done <= 1;
				index <= index + 1;
				point_data_in <= bmp_data[index];
			end
			else if(index == bmp_size)
				bmp_write_done <= 1;
			else begin
				index <= index + 1;
				point_data_in <= bmp_data[index];
			end
		end
	end
  end

// if not process image, select the point_data_out, else select point_data_in
//  assign bmp_data_out = image_write_done ? point_data_out : point_data_in;
  assign bmp_data_out = point_data_out;
  
  always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		data_read_start_d1 <= 1'b0;
	end
	else 
		data_read_start_d1 <= data_read_start;
  end

  
// write the bmp data
  integer bmp_file_write;
  initial begin
	bmp_file_write = $fopen(".\\picture_histogram_equalization.bmp","wb");
  end
  // write the data every clock
  always @ (posedge clk) begin
	if(rst_n) begin
		// when index=0, not write
		if(data_read_start_d1)
			$fwrite(bmp_file_write, "%c", bmp_data_out);
		else if(data_read_done) begin
			$fclose(bmp_file_write);
			$fclose(bmp_file_read);
			$display("Write bmp file complete, Close the file");	
			$finish;
		end
	end
  end
 
//======================<Module Instance>======================
histogram_equalization u_histogram_equalization
  (
    .clk					(clk),
    .rst_n					(rst_n),
    .valid_in				(valid_in),
    .image_write_done		(image_write_done),
    .bmp_write_done			(bmp_write_done),
    .point_data_in			(point_data_in),
    .point_data_out	    	(point_data_out),
    .init_done	  			(init_done),
    .data_read_start	    (data_read_start),
    .data_read_done	    	(data_read_done)
  );


endmodule


五、产生合适的灰度图片

将图片用软件“画图打开”,点击"属性",按照如下设置。

image-20240507132255978

然后将图片另存为bmp图像,256色图,在代码里的图像数据开始位置是由这个参数决定的。

image-20240507132509631

五、直方图均衡效果

均衡前

picture_copy

均衡后

picture_histogram_equalization

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值