Java实现图片渲染((拖动)马赛克黑白照油画风格等)

Posted GuochaoHN

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现图片渲染((拖动)马赛克黑白照油画风格等)相关的知识,希望对你有一定的参考价值。

方法改进

紧接上一篇实现图片渲染链接: 图片渲染(图像编码原理).

可以发现:只前在绘画图片的时候速度非常的慢。那是因为每次只要一获得像素就立即将其绘画出来。

g.setColor(color);
g.fillRect(i, j,1, 1)

在内存中执行代码后,会向GPU请求刷新屏幕,一张图片有成千上万个像素,那么屏幕就会连续刷新很多次,速度非常慢。例如,如果电脑屏幕的刷新率是60赫兹的话,意味着一秒钟屏幕会刷新60次,放到这里来讲的话,一秒钟最多显示60个像素,对于分辨率极高的图片,要加载出来,可想而知等的时间也就会越久。

所以,就要用一种方法一次性将所有的像素画出来。BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区我们可以很方便地操作这个图片)

在请求刷新屏幕之前,现将所有的像素设置完毕,然后一次性将缓冲区的像素全部画出来(刷新一次屏幕)。有两种方法来设置缓冲区。
第一:直接设置缓冲区相应位置像素的RGB值。
实现如下:

//绘画原图改进方法
	public BufferedImage paint1(Graphics g,int[][] arrpixel)
	{	    
		BufferedImage buffimage=new BufferedImage(arrpixel.length, arrpixel[0].length,BufferedImage.TYPE_INT_RGB);
		for(int i=0;i<arrpixel.length;i++)
		{
			for(int j=0;j<arrpixel[0].length;j++)
			{
				int value=arrpixel[i][j];				 
				buffimage.setRGB(i, j, value);//一次性将所有像素存储完成
			}
		}				
		  g.drawImage(buffimage, 0, 0, null);//一次性画完				  		
		  return buffimage;
		  
	}

第二:通过缓冲区获得画笔,将画笔赋相应的RGB值,然后通过fillRect方法来填充矩形小像素。这种方法还可以方便我们实现渲染(如后面的马赛克等)
实现如下:

//原图改进方法
public BufferedImage paint1(Graphics g,int[][] arrpixel)
	{	    
		BufferedImage buffimage=new BufferedImage(arrpixel.length, arrpixel[0].length,BufferedImage.TYPE_INT_RGB);
	    Graphics ng=buffimage.getGraphics();
		for(int i=0;i<arrpixel.length;i++)
		{
			for(int j=0;j<arrpixel[0].length;j++)
			{
				int value=arrpixel[i][j];
				 if(i==0&&j==0)
				  { System.out.println("arrpixel:"+value);}
				 ng.setColor(new Color(value));
				 ng.fillRect(i, j, 1, 1);				
			}
		}				
		  g.drawImage(buffimage, 0, 0, null);		  		  
	}

之后就可以把其他的渲染方法逐个改进。以下是图片渲染程序的源代码。

ImageUI

public class ImageUI extends JPanel {
	 String []bt= {"原图","方形马赛克","灰度","二值化","轮廓检测","油画绘制","拖动原图马赛克","拖动彩色马赛克","截图","放大","缩小","撤回上一步","清空图片"};
	 ImageListener listener=new ImageListener();//什么时候static final
	 JFrame nj=new JFrame();//截图确认窗口
	public void initUI()
	{
		JFrame jf=new JFrame();
		jf.setSize(1500,780);
		jf.setTitle("图片处理");
		jf.setDefaultCloseOperation(3);
		jf.setLocationRelativeTo(null);
		
		
		//按钮功能区面板
		JPanel jp1=new JPanel();
		jp1.setBackground(new Color(0xafeeee));
		Dimension dm=new Dimension(200,780);
		jp1.setPreferredSize(dm);
		Dimension btdm=new Dimension(190,40);		
		for(int i=0;i<bt.length;i++)
		{
			JButton jb =new JButton(bt[i]);
			jb.setPreferredSize(btdm);
			jb.setBackground(Color.white);
			Font font= new Font("黑体",Font.BOLD,20);
			jb.setFont(font);
			jb.setForeground(new Color(0x000080));
			jb.setBackground(new Color(0xf0fff0));
			jb.addActionListener(listener);
			jp1.add(jb);
		}
		
	    //绘图区jframe默认边框布局				
		this.setBackground(new Color(0xffffe0));
		jf.add(jp1,BorderLayout.EAST); 
		jf.add(this,BorderLayout.CENTER);

		this.addMouseListener(listener);
		this.addMouseMotionListener(listener);
		this.addKeyListener(listener);
		jf.setVisible(true);

		//从jp2 获得画笔
		Graphics g=jf.getRootPane().getGraphics();
		listener.setgraphics(g);
		listener.setjpanel(this);
					
		nj.setTitle("提示");
		nj.setSize(300, 150);
		//nj.setDefaultCloseOperation(3);
		//这里弹出窗口点叉不能设置关闭程序,否则主界面也会跟着关闭
		nj.setLocationRelativeTo(jf);
		nj.setResizable(false);
		
		JLabel txt =new JLabel("是否确认截图",JLabel.CENTER);
		Font fnt= new Font("黑体",Font.BOLD,30);
		txt.setFont(fnt);
		nj.setLayout(new FlowLayout());
		nj.add(txt);
		
		Font fnt2=new Font("黑体", Font.CENTER_BASELINE, 15);
		JButton bt =new JButton("确认");
		bt.addActionListener(listener);
		bt.setPreferredSize(new Dimension(100, 30));
		bt.setFont(fnt2);
		nj.add(bt);
		
		JButton bt2=new JButton("取消");
		bt2.addActionListener(listener);
		bt2.setPreferredSize(new Dimension(100, 30));
		bt2.setFont(fnt2);
		nj.add(bt2);
	    nj.addWindowListener(listener);
	}
	
	
	@Override
	public void paint(Graphics g) {
		// TODO Auto-generated method stub
		 super.paint(g);		  		
		 		 System.out.println("fresh"+!listener.freshbuff.isEmpty());
		    if( !listener.freshbuff.isEmpty())
		       {
		        BufferedImage top=( BufferedImage)listener.freshbuff.peek();
			     g.drawImage(top,0,0,top.getWidth(),top.getHeight() ,null);		
			     System.out.println("调用paint");
	           }
	}
	
	public void clear(Graphics g)
	{
		super.paint(g);
	}
	
	public static void main(String[] args) {
		new ImageUI().initUI();
		
	}
	
}

ImageListener

#前提说明:
如果一个类需要的接口太多,且接口中的方法又有很
多,且大部分无用,而这个类又不需要继承,那么就可以考虑做一个工具类——实现所有的接口。 然后用所需的类继承这个工具类,选择性的重写方法。
这里监听器类为ImageListener,继承监听器工具类ListenerUtils ,选择性重写接口方法:

  public class ImageListener extends ListenerUtils
{
  //选择性重写
  ... ...
}

public class ListenerUtils implements ActionListener,MouseListener,MouseMotionListener
,KeyListener,ChangeListener,WindowListener{

	@Override
	public void stateChanged(ChangeEvent e) {
		// TODO Auto-generated method stub
		
	}
	... ... ... ... ..

为了更好得实现返回上一步功能,定义了两个BufferedImage栈,考虑到,窗口改变执行的peek()操作,返回上一步执行的是pop()操作,所以用一个栈不容易将功能正确实现。(读者也可以自己定义不同的数据类型存储BufferedImage,可能会更加方便)
刷新栈:
窗口改变大小实现图片重绘。

public Stack<BufferedImage> freshbuff=new Stack<BufferedImage>();

操作栈:
记录每一次的执行结果,以实现返回上一步功能。

public Stack<BufferedImage> buffstack =new Stack<BufferedImage>();

具体代码如下:

public class ImageListener extends ListenerUtils {
	
	int[][] imgarr;
	String btn="";
	String path="E:\\\\java\\\\eclipse\\\\eclipse javaee\\\\workspace\\\\Yu java\\\\66.jpg";
	private Graphics g;
	ImageUtils imageff;
    ImageUI jp;
	public Stack<BufferedImage> buffstack =new Stack<BufferedImage>();
	public Stack<BufferedImage> freshbuff=new Stack<BufferedImage>();
	int index;
    int p_x,p_y;
    int r_x,r_y;
    int d_x,d_y;
    		
	public void setgraphics(Graphics g)
	{
		this.g=g;
	}	
	public void setjpanel(ImageUI jp)
	{
		this.jp=jp;		
	}
	
	//初始化监听器
	ImageListener()
	{		
		imageff=new ImageUtils();
		imgarr=imageff.getImagePixel(path);
		System.out.println("初始化成功"+imgarr.length);
	}
		
	@Override
	public void windowClosing(WindowEvent e) {
		// TODO Auto-generated method stub
		super.windowClosed(e);
     jp.paint(g);         
	}
		
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		System.out.println("当前"+btn);
		//System.out.println("调用actionperformed");
	    btn=e.getActionCommand();
	    if(btn.equals("原图"))
	    {	
	     jp.clear(g);    		    
	     BufferedImage img=	imageff.paint1(g,imgarr);
	     buffstack.push(img);
	     freshbuff.push(img);
	     System.out.println("栈顶"+buffstack.peek().getWidth());
	     System.out.println("栈内元素数"+buffstack.size());	    
	    }
	    	   	    
	    else if(btn.equals("方形马赛克")) 
	    {  
	    	jp.clear(g); 
	    	imageff.paint2(g, freshbuff,buffstack);		         	    
	    }
	    
	    else if(btn.equals("灰度")) 
	    {	
	    	jp.clear(g);   
	        imageff.paint3(g, freshbuff,buffstack);		    	     
	    }
	    
	    else if(btn.equals("二值化")) 
	    {    
	    	jp.clear(g);
	    	imageff.paint4(g, freshbuff,buffstack);		     	    	 	    	
	    }
	    
	    else if(btn.equals("轮廓检测")) 
	    {   
	    	jp.clear(g);
	    	imageff.paint5(g, freshbuff,buffstack);		    	    	
	    }
	    
	    else if(btn.equals("油画绘制")) 
	    {  
	    	jp.clear(g);
	    	imageff.paint6(g, freshbuff,buffstack);		     		     
	    }
	    
	    else if(btn.equals("放大"))
	    {   
	    	jp.clear(g);
	        imageff.piant7(g, freshbuff,buffstack);	    
	        jp.paint(g);   
	    }
	    
	    else if(btn.equals("缩小"))
	    {  
	    	jp.clear(g);
	    	imageff.paint8(g,freshbuff,buffstack );	    	
	    	jp.paint(g);
	    }
	    
	    else if(btn.equals("清空图片"))
	    {
	      jp.clear(g);
	      imageff.withdraw(g,jp,buffstack,freshbuff);
	    }
	    
	    else if (btn.equals("撤回上一步"))
	    {
	      jp.clear(g);
	      freshbuff.push(imageff.backward(buffstack,g));	   	   
	    }	    
	    
	    else if(btn.equals("确认"))
	    {
	        jp.clear(g);
	    	imageff.paint12(g, freshbuff, buffstack,p_x,p_y,r_x,r_y);
	    	System.out.println("截图完成");
	    	jp.nj.dispose();
	    }
	    
	    else if(btn.equals("取消"))
	    {
	    	jp.nj.dispose();
	    	jp.paint(g);
	    }	    
	}
	
	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		super.mousePressed(e);
		p_x=e.getX();
		p_y=e.getY();
		d_x=e.getX();
		d_y=e.getY();
	}
	
	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
		super.mouseReleased(e);
		r_x=e.getX();
		r_y=e.getY();
		if(btn.equals("截图"以上是关于Java实现图片渲染((拖动)马赛克黑白照油画风格等)的主要内容,如果未能解决你的问题,请参考以下文章

js进行数字图像处理:亮度对比度马赛克画笔放大缩小镜像贴纸旋转颜色值显示

图片特效

黑白照转彩照

ProcessingJoy —— 多种风格的字符马赛克JAVA

用文字描述给黑白照上色,这个免费网站火了!网友:比其他同类都好用

忆回70年代 用Python修复父母那辈精彩回忆,黑白照变彩照