Verilog、C++、Matlab实现BMP图片翻转

近期学习Verilog课程的时候,由于老师布置了一个大作业,让我们利用Verilog语言来实现BMP图片的翻转,由于还比较有趣,所以笔者在此向大家分享一下我的代码和整体实现的流程,若大家能够提出更加简洁的实现方法,也欢迎在文章底部留言,与我交流。此外由于笔者学历、知识有限,出现错误在所难免,若大家发现错误可及时向我反馈!

首先我们需要准备一张256*256*24的BMP位图文件,笔者本次采用的文件如下:

verilog1.jpg

然后我们将其重命名为 DSS.bmp,并放置于电脑D盘根目录下:

verilog2.jpg

然后我们打开一个C++编译软件并且新建一个工程,将如下代码输入(笔者由于重装系统导致Visual Studio 2017被删除了,目前还未重新安装这个软件,所以此处采用CodeBlocks软件示范):

#include<iostream>
#include<fstream>
using namespace std;
int main()
{   int num=256*256;  //一共256*256个像素点
    short int buffer[27];  //用于取出头部信息,将文件指针指向第一个像素点信息
	bool rgb[3];  //储存一个RGB像素点信息
	ifstream bmp("D:/DSS.bmp",ios::binary);  //打开DSS.bmp位图文件,准备对其进行读取操作
	ofstream bmptxt("D:/DSS.txt",ios::binary);  //打开DSS.txt文件,将位图像素点信息存入其中供Verilog程序调用
	bmp.read((char *)buffer,sizeof(buffer));  //保护位图头部信息,防止位图头部信息被记入txt像素文件中
	for (int i=0;i<num;i++)  //开始读取bmp位图像素点信息
	{  bmp.read((char *)rgb,sizeof(rgb));  //读取一个像素点信息
		for(int j=0;j<3;j++)
		{   int intj=int(rgb[j]);  //将一个像素点信息分为R、G、B三部分
			int b[8];
			for(int m=0;m<8;m++)
			{   b[m]=intj%2;
				intj=intj/2;
			}
			for(int n=0;n<8;n++)
                bmptxt<<b[7-n];  //将像素点信息逐个输入txt文件中储存
		}
		if(i<num-1)bmptxt<<' ';  //每读完一个像素点信息插入一个空格
	}
	bmp.close();  //关闭位图文件
	bmptxt.close();  //关闭txt文件
}
verilog3.jpg

然后进行编译,再运行,运行完成之后,储存有DSS.bmp位图像素信息的DSS.txt文件就出现在D盘的根目录下了

verilog4.jpg

然后我们打开Modelsim软件,新建一个工程,里面包含两个文件,一个是翻转模块的文件,一个是测试模块的文件,将以下代码分别输入到对应的Verilog文件中

//翻转模块代码
module DSS(imain,imaout,start,res,clk);  //定义模块DSS
input clk,start,res;  //定义输入变量clk时钟、start启动信号、res复位信号
input [0:23] imain;  //定义24字节的输入信号,用于接受一个像素点信息
output [0:23] imaout;  //定义24字节的输出信号,用于输出翻转之后的像素信息
wire clk,res;  //定义clk时钟和res复位信号为线网类型
wire [0:23] imain;   //定义输入信号为线网类型
reg [0:23] imaout;  //定义输出信号为寄存器类型
reg [0:23] store [0:65535];  //定义一片寄存器储存区,用于存储BMP位图所有的像素点信息
reg [0:7] ctrow,ctcol;  //定义行、列变量,用于确定当前像素点在原BMP位图中的位置
reg [0:15] num,t,n;  //定义计数变量,其中num为翻转之后的像素点在store储存区的地址,t用于控制输出像素点信息为256*256个,n用于记录输入像素点信息至store时当前像素点的地址
reg [0:7] trow,tcol;  //定义翻转之后的像素点的行、列变量
reg finish;  //定义输入完成标志,当finish为1时表明所有像素点已经输入store,可以开始进行翻转输出操作

always @(posedge clk)  //输入计数块
begin
 if(res)  //判断是否复位
  begin
   finish<=0;  //finish置零
   n<=0;  //n置零
  end
 else
  begin
   if(start)  //判断是否开始
    if(n==65535)  //当n=65535时代表输入完毕
      begin
      n<=0;  //此时n置零,为翻转输出做好准备
      finish<=1;  //finish置一,表示输入已经完成
      t<=65535;  //t置65535,控制输出的像素点信息为65536个
      end
    else
      n=n+1;  //如果n<65535,则继续输入,地址为n+1
  end
end

always@(n or finish)  //计算当前输入store的像素点在原BMP中的行列位置
if(n==0)  //第一个像素点人为规定行列为0,0
 begin
  ctcol=0;
  ctrow=0;
 end
else
begin
  ctcol<=n%256;  //列等于地址除以256的余数
  ctrow<=(n-ctcol)/256;  //行等于地址除以256的整数
end

always@(ctrow or ctcol)  //计算翻转输出时像素点的行、列位置
begin
  trow<=ctcol;  //翻转之后的像素点的行等于翻转前的像素点的列
  tcol<=255-ctrow;  //翻转之后像素点的列为翻转前的像素点的行被255减
end

always@(trow or tcol)  //计算翻转后的像素点在store中的地址
begin
  num<=256*trow+tcol;  //地址等于翻转后的行乘以256再加列
end

always@(posedge clk)  //输入和输出
begin
  if(start&&(~res))  //判断是否开始且是否复位
    begin
      if(finish&&(t>=0))  //判断是否输入完成和输出个数是否大于65536
       begin
        imaout<=store[num];  //按照计算后像素点在store中储存的地址将其输出
        t=t-1;  //记录剩余输出的像素点个数
      end
      else
        begin
        imaout<=24'bzzzz_zzzz_zzzz_zzzz_zzzz_zzzz;  //如果没有输入完成或者已经输出完所有像素点信息,则输出高阻
        store[n]<=imain;  //并且继续将像素点信息按顺序输入store
        end
      end
   else
       imaout<=24'bzzzz_zzzz_zzzz_zzzz_zzzz_zzzz;  //没有开始信号则输出高阻
end
endmodule

//测试模块代码
`timescale 100 ns /1 ns
module rot_test( );
//变量的定义
reg reset,clk,start;
reg [23:0] imin;  
wire [23:0] imout;//存储24位像素值
reg [23:0] memory [0:65535];
integer i,odata,m;
DSS  Q1 (.imain(imin),.imaout(imout),.start(start),.res(reset),.clk(clk));//调用旋转模块

initial 
begin
 clk=1'b0;
 start=1'b0;
 reset=1'b0;
 m=0;
 i=0;
 $readmemb("D:/DSS.txt",memory); //存储器读取TXT文件
 odata=$fopen("D:/DSS_turn.txt");   //生成输出的TXT文件
 #10 reset=1; 
 #20 reset=0;//设置复位信号
 #5 start=1;//传递开始信号
end 

always
 #5 clk=~clk; //以clk作为时钟信号

always @(negedge clk)
begin 
 if(start)         //#40判定成功
  begin 
   if(i<65536)     //i作为输入部分的计数变量
     begin
      imin=memory[i]; //给旋转模块提供输入
      i=i+1;  
     end
    m=m+1;
  end
end  

always @(negedge clk)
begin
  if(m>65536*2)    //m作为整个过程的计数变量
      $finish; 
end

always @(posedge clk)
begin 
  if((start)&&(m>65536))    //m变化的后半段是输出部分
    begin
    #1 $fwrite(odata, imout[23:16]," ");
      $fwrite(odata, imout[15:8]," ");
       $fwrite(odata, imout[7:0]," ");
    end  
end  
endmodule
verilog5.jpg
verilog6.jpg

然后我们对这两个文件进行保存,之后进行编译,然后返回到工程界面右键点击work下的DSS_test,在出现的选择栏中选择Simulate

verilog7.jpg

然后右键点击DSS_test,在出现的选择栏中选择Add Wave

verilog8.jpg

之后在顶部工具栏点击Simulate,再选择Run,再选择Run-All

verilog9.jpg

等待仿真运行结束之后,会弹出是否需要完成的对话框,此时如果选择是,将会结束仿真并且关闭modelsim,如果选择否则会暂停仿真,停在$finish处,我在这里选择否

verilog10.jpg

之后选择End Simulation停止仿真

verilog11.jpg

然后在D盘根目录下会多出一个储存有翻转过后的BMP位图像素信息的 DSS_turn.txt 文件

verilog12.jpg

之后我们再次打开C++编译软件,新建一个工程然后输入如下代码:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    int num=256*256;  //bmp文件像素点总个数
    short int buffer[27];  //用于存储bmp文件头部信息
	int rgb[3];  //用于存储一个像素点信息
	char str;
	ifstream turn("D:/DSS_turn.txt",ios::binary);  //打开经过Verilog转换之后的像素点txt文件
	ifstream bmp("D:/DSS.bmp",ios::binary);  //打开原bmp文件用于复制头部信息
	ofstream bmp_turn("D:/DSS_turn1.bmp",ios::binary);  //新建一个bmp文件用于显示转换后的图片
	bmp.read((char *)buffer,sizeof(buffer));  //将bmp头部信息存入buffer
	bmp_turn.write((char *)buffer,sizeof(buffer));  //将头部信息从buffer中存入新的bmp文件
	int i=0;
	while(i<num)  //控制输出的像素点的个数
	{
	    int j=0;
		while(j<3)  //按照RGB顺序输出一个像素点的信息
		{
            turn>>rgb[j];  //将DSS_turn.txt文件中的像素点信息存入rgb中
			str=rgb[j];  //将像素点信息由二进制转换成字符型
			bmp_turn<<str;  //将字符型的像素点信息存入新的bmp位图文件中
			j=j+1;
		}
		i=i+1;
	}
	turn.close();  //关闭转换后的txt文件
	bmp.close();  //关闭原bmp文件
	bmp_turn.close();  //关闭新建的bmp文件
}
verilog13.jpg

将代码进行编译,并且运行,运行完成之后在D盘根目录将会多出一个DSS_turn1.bmp文件,这就是翻转完成之后的文件了

verilog14.jpg

此外还可以利用Matlab来实现翻转后的BMP位图生成,而且Matlab的语句不到C++语句的一半,代码如下:

DSS_t=load('D:/DSS_turn.txt');  %将DSS_turn.txt文件中的像素点信息存入DSS_t矩阵中,该矩阵的行列为1*(256*256*3)
m=1;  %将DSS_t矩阵中储存的像素点按照顺序转换到三维矩阵DSS_bmp矩阵中
for x=256:-1:1  %三维矩阵的行从256到1
    for y=1:1:256  %三维矩阵的列从1到256
        for z=3:-1:1  %三维矩阵的深度从3到1
            DSS_bmp(x,y,z)=DSS_t(m);  %进行赋值
            m=m+1;
        end
    end
end
imwrite(uint8(DSS_bmp),'D:/DSS_turn.bmp','bmp')  %将三维矩阵中的double类型的像素点信息转换成uint8类型再生成BMP位图
verilog14.jpg

保存并且运行之后,在D盘根目录将会出现一个DSS_turn.bmp的位图文件,这也是翻转过后的图片

verilog16.jpg

至此,BMP位图的翻转就完成了!

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注

//Microd //Microd