解析BMP类型的文件&被变相的“马赛克化”之……

  接下,为了对输入输出流有更多的了解,我们练习了对BMP格式文件的解析。

BMP格式的文件解析跟其他一样,也是按照字节的顺序一个一个字节的读取,在bmp格式的文件中,写有关于此类文件解析的协议,这也是我们初识“协议”这个东西,这在我们之后学过的通信中也有用到。

  BMP文件要用我们自己写的东西解析出来,就要了解它的字节写入的方法即“协议”,这样才能顺利读出来。

因此,我们查到了BMP文件的格式:

BMP4个组成部分:

 

位图文件头(bitmap-file header)

BITMAPFILEHEADER

bmfh

位图信息头(bitmap-information header)

BITMAPINFOHEADER

bmih

彩色表(color table)

RGBQUAD

aColors[]

图象数据阵列字节

BYTE

aBitmapBits[]

然后按照字节的排列,他的每个字节所代表的含义如下:

<!--EndFragment-->

1.位图文件头
0000-0001:文件标识,为字母ASCII“BM”
0002-0005:文件大小。
0006-0009:保留,每字节以“00”填写。
000A-000D:记录图像数据区的起始位置。各字节的信息依次含义为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。

2.图像描述信息块
000E-0011:图像描述信息块的大小,常为28H
0012-0015:图像宽度。
0016-0019:图像高度。
001A-001B:图像的plane总数(恒为1)。
001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。
001E-0021:数据压缩方式(数值位0:不压缩;18位压缩;24位压缩
0022-0025:图像区数据的大小。
0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。
002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。

 

由于我们使用的是24位的图像,因此之后两个表示颜色的都只用RGB来表示。

所以,根据协议的顺序,就可以一步步的解析BMP文件把它的数据给读出来了,而有一些固定不变的信息就不需要我们读取,我们只需要读取就像颜色长度宽度这类的重要信息。

 

 

package lyw.summer0628V3;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.JFileChooser;
/**
 * 按钮的监听器类
 * @author liuyuwen
 */
public class ButtonListener implements ActionListener {
	int picture_width;//图片宽度
	int picture_height;//图片高度
	int flen=14;//头文件信息长度
	int bilen=40;//位图信息头长度
	int skip_width;//因是补齐而要跳过的宽度
	Color c;
	int[][]	pictureR,pictureG,pictureB;//分别存储R,G,B值的int二维数组
	Graphics g;
	public ButtonListener(Graphics g){
		this.g=g;
	}
	public void actionPerformed(ActionEvent e) {
		String str=e.getActionCommand();
		if("打开文件".equals(str)){
			//文件选择
			JFileChooser jfc=new JFileChooser();
			//显示对话框
			jfc.showOpenDialog(null);
			//返回选中的文件
			File file=jfc.getSelectedFile();
			
			try {
				//文件输入流
				java.io.FileInputStream fis=new java.io.FileInputStream(file);
				//数据输入流
				java.io.DataInputStream dis=new java.io.DataInputStream(fis);
				//读取头文件信息长度数组
				byte[] fb=new byte[flen];
				dis.read(fb);
				//读取位图信息头长度数组
				byte[] bib=new byte[bilen];
				dis.read(bib);
				//调用change方法得到图片的宽度和高度
				picture_width=change(bib,7);
				picture_height=change(bib,11);
				//读取位图的RGB颜色数据
				readRGB(dis);
				//关闭文件
				dis.close();
			} catch (Exception e1) {
				e1.printStackTrace();
				System.out.println("文件读取出错啦~~~~");
			}
			paint(g);
		}
	}
	/**
	 *将byte数组的byte信息并转化为int型返回的方法
	 * @param b:byte数组
	 * @param n:数组中的位置
	 * @return:所得的int型数值
	 */
	public int change(byte[] b,int n){
		int t=(((int)b[n]&0xff)<<24)|
				(((int)b[n-1]&0xff)<<16)|
				(((int)b[n-2]&0xff)<<8)|
				((int)b[n-3]&0xff);
		return t;
	}
	/**
	 * 读取RGB数据的方法
	 * @param dis:数据输入流
	 */
	public void readRGB(java.io.DataInputStream dis){
		//判断每行是否后面有补0 的情况(是否为4的倍数)
		 if(!(picture_width*3%4==0)){//图片的宽度不为0  
	            skip_width=4-picture_width*3%4;  
	     }
		//实例化存储R,G,B数据的数组,行数和列数分别与图片的宽度和高度对应
		pictureR=new int[picture_height][picture_width];
		pictureG=new int[picture_height][picture_width];
		pictureB=new int[picture_height][picture_width];
		//按行读取,从左下开始倒着读
		for(int h=picture_height-1;h>=0;h--){
			for(int w=0;w<picture_width;w++){				
				try {
					//读取R,G,B三原色的数据,注意原是按蓝绿红的顺序
				    int blue=dis.read();
	                int green=dis.read(); 
	                int red=dis.read();
	                //将数据存放进R,G,B数组
	                pictureR[h][w]=red;
	                pictureG[h][w]=green;
	                pictureB[h][w]=blue;
	              //跳过补0项  
	                if(w==0)
	                	dis.skip(skip_width);
				} catch (IOException e) {
					e.printStackTrace();
				}  
			}
		}
	}
	/**
	 * 
	 * 绘制图片的方法
	 * @param g:画布
	 */
	public void paint(Graphics g){
		for(int h=picture_height-1;h>=0;h--){
			for(int w=0;w<picture_width;w++){
				g.setColor(new Color(pictureR[h][w],pictureG[h][w],pictureB[h][w]));
				g.drawLine(w+200, h+100, w+200, h+100);
			}
		}
	}
}

 

 

以上步骤就是对Bmp文件的一个读取过程,其中读取RGB颜色是一个困扰点,有好几次颜色读取跟原本图像不一致,只好好多个小组也遇到了同样的问题,最后发现是RGB三个色素的读取的顺序不一致导致的。在读取完数据后,我们调用paint方法,再重新调用刚才读取到的数据,用自己的方式画出之前一样的图形。 

我们重绘出之前图形的原理就是,从某一个边开始一个像素点一个像素点的画点,每个点有自己的一个RGB属性,构成了它的颜色,这样一个点一个点拼凑出来就成了之前一样的图像BMP格式的文件了。

 

package lyw.summer0628V3;

import java.awt.FlowLayout;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
/**
 * BMP图片的读取与显示
 * @author liuyuwen
 */
public class BMPPicture {
	/**
	 * 图片显示面板的方法
	 */
	public void showUI(){
		JFrame jf=new JFrame();
		jf.setTitle("BMP文件读取");	
		jf.setSize(800, 650);
		jf.setDefaultCloseOperation(3);
		jf.setVisible(true);
		FlowLayout fl=new FlowLayout();		//设置布局为流布局
		jf.setLayout(fl);
		//通过按钮打开文件
		JButton jb=new JButton("打开文件");
		jb.setSize(30, 30);
		jf.add(jb);
		Graphics g=jf.getGraphics();
		ButtonListener bl=new ButtonListener(g);
		jb.addActionListener(bl);
	}
	/**
	 * 主函数@param args
	 */
	public static void main(String[] args) {
		BMPPicture bp=new BMPPicture();
		bp.showUI();
	}
}

 BLABLA~~~这些是用自己写的打开的图片,没有失真。。吐舌头




 
 

 

<!--EndFragment-->在之后听到关于图片马赛克的事,然后就考虑了一下,发现其实跟图片解析没什么大的关系,只是重绘时画图的问题,就试着写了一下代码^O^....本来的想法是,把图片的每个像素点放大,然后就会出现马赛克的效果。。。。哭结果忘记考虑像素点放大图片也会放大,所以这个马赛克的效果只是把小的图片弄成”马赛克风“。。然后图片也变大了。。。+_+...

其实只是改了一下paint里面的方法。。。

public void paint(Graphics g){
		for(int h=picture_height-1;h>=0;h--){
			for(int w=0;w<picture_width;w++){
				g.setColor(new Color(pictureR[h][w],pictureG[h][w],pictureB[h][w]));
				System.out.println("颜色="+ pictureG[h][w]+" "+ pictureB[h][w]+" "+ pictureR[h][w]);
				g.fillRect(w+100+9*w,h+100+9*h,10,10);
				System.out.println("x="+w+"y="+h);
				System.out.println("我执行啦");
			}
		}
	}

 但是。。本来打开是这样的图片……



   。

   。

  

变成了……



 放大版。。叫喊

所以只能把图片全部缩小……



 

 

 成这个样子。。。

才能出来一般大小的马赛克。。。



 

 

 

 所以马赛克效果的制作还没我想象的那么简单,他需要不改变图像的大小,在某一范围内或者某个N*N的像素点的范围内,把那个范围内的不同颜色的像素点变成同一个颜色值。。这样应该才能算是真正的马赛克效果吧哭(不然像“可以给我脸打上马赛克吗”之类的。。。那么脸该会被放成多大啊。。。=_=|||)

 

 

 

 

 

 

 

<!--EndFragment-->

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值