【图片、字符画互转】字符画实现(JAVA)

一、实现方法

字符画实际上是将图片的每一个像素点,按一定的标准(如灰度值等)替换为字符(如图所示,图示为黑色背景,白色字体)。
示例

二、图片转字符画 实现过程

①读取图片(这里使用java.jwt自带的BufferedImage对象读取)

	 /** 获取最原生的BufferedImage对象 */
     public static BufferedImage getBufferedImage(final String src) throws FileNotFoundException, IOException {
     		return ImageIO.read(new FileInputStream(new File(src.trim())));
     }

②对图片进行处理(如压缩、灰度化、二值化等),一般来说,先二值/灰度化后再压缩,效果比较好,当然具体情况具体分析
灰度化示例
下面贴上代码

	/**
 * 按设置的宽度高度比例压缩图片文件,如果width和height都大于0,则以此为基准进行压缩(可能造成图片变型)
 * 
 * @param oldFile
 *            要进行压缩的文件全路径
 * @param newFile
 *            新文件
 * @param width
 *            宽度
 * @param height
 *            高度
 * @return 返回压缩后的文件的全路径
 */
public static BufferedImage zipImageFileWithRate(BufferedImage image, int width, int height) {
	if (image == null) {
		return null;
	}
	if(width<=0&&height<=0){
		return image;
	}
	/* 对服务器上的临时文件进行处理 */
	/* 按比例压缩 */
	int w = image.getWidth(null);
	int h = image.getHeight(null);
	double bili;
	if (width > 0&&height<=0) {
		bili = width / (double) w;
		height = (int) (h * bili);
	} else if (height > 0&&width<=0) {
			bili = height / (double) h;
			width = (int) (w * bili);
	}
	
	BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
	Graphics2D graphics = buffImg.createGraphics();
	graphics.getFontRenderContext();
	graphics.setBackground(new Color(255, 255, 255));
	graphics.setColor(new Color(255, 255, 255));
	graphics.fillRect(0, 0, width, height);
	graphics.drawImage(image.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
	StringOutputter.print("压缩图片 (宽:?,高:?)==>成功", width,height);
	return buffImg;
}

/**
 * 根据路径获取灰度化后的图片
 */
public static BufferedImage getGrayBufferedImage(final BufferedImage image)
		throws FileNotFoundException, IOException {
	int width = image.getWidth();
	int height = image.getHeight();
	BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
	for (int i = 0; i < width; i++) {
		for (int j = 0; j < height; j++) {
			int rgb = image.getRGB(i, j);
			grayImage.setRGB(i, j, rgb);
		}
	}
	return grayImage;
}
/**
 * 获取二值化图片
 */
public static BufferedImage getBinaryBufferedImage(final BufferedImage image) {
	if (image == null) {
		return null;
	}
	int width = image.getWidth();
	int height = image.getHeight();
	BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
	for (int i = 0; i < width; i++) {
		for (int j = 0; j < height; j++) {
			int rgb = image.getRGB(i, j);
			binaryImage.setRGB(i, j, rgb);
		}
	}
	return binaryImage;
}

③遍历图片,根据灰度值,从已知的字符字典里获取字符,并写入txt文件

		/**
    	 * 获取图片在x,y轴的rgb值
    	 */
    	public static RgbPixel getScreenPixel(BufferedImage image, int x, int y) {
		if (isLegal(image, x, y)) {
			RgbPixel rgb = new RgbPixel();
			int pix = image.getRGB(x, y);
			rgb.setRed((pix & 0xff0000) >> 16);// 最高8位为Red
			rgb.setGreen((pix & 0x00ff00) >> 8);// 中间8位为Green
			rgb.setBlue((pix & 0x0000ff));// 最低8位为Blue
			return rgb;
		}
		return null;
	}
	/* 主要实现函数,遍历整张图片 */
    public static void write(BufferedImage image,FileOutputStream fop) throws IOException{
    		/* 遍历整个图像,xy像素点填充字符 */
    		for (int j = image.getMinY(); j < image.getHeight(); j++) {
    			for (int i = image.getMinX(); i < image.getWidth(); i++) {
    				RgbPixel pixel = PictureReader.getScreenPixel(image, i, j);
    				int index = RgbNumberWritter.getChar(pixel);
    				fop.write(DICTORY.charAt(index));// 插入字符
    				fop.write(32);// 加个空格,防止图片会看起来扁了一半
    			}
    			/* 填充换行符 */
    			fop.write(10);
    		}
    }

这里为了方便,自己写了一个RgbPixel类(getter和setter没有贴出)

    public class RgbPixel {
    	private int red;
    	private int green;
    	private int blue;
    	public double getGray(){
    		/*
    		 * 灰度值公式:Gray=R×0.299+G×0.587+B×0.114
    		 */
    		double gray=red*0.299+green*0.587+blue*0.114;
    		return gray;
    }

从字符字典中获取对应字符(根据灰度值):DICTORY是字典,下标越大,代表其灰度值越大

	private final static String DICTORY=" .,·-'`:!1+*abcdefghijklmnopqrstuvwxyz<>()\\/{}[]?23456789AJKLICFDBEGHMNPQRSTUVWXYZ%&#$0O@";
	private final static int DICTORY_LEN=DICTORY.length();
	/** 获取该灰度值对应下标 */
	public static int getChar(RgbPixel rgb){
		double key =DICTORY_LEN- Math.round(rgb.getGray()/STEP);
		if(key>=1){
			key-=1;
		}
		return (int)key;
	}

通过上面几个步骤,可以很快的生成对应的txt文本:
txt图片

三、字符画(txt)转图片

(一)、方法

字符画转图片,只需要读取txt文本里的文本信息,然后直接画在图片上即可,
Graphics提供了drawString方法,可以很方便的生成,需要注意的是,为了生成的图片能尽可能的还原图片,所以建议使用等宽字体,并且调整好图片的大小(字体的单位是pt,像素单位是px,具体请参考:https://blog.csdn.net/gogl/article/details/48584191https://blog.csdn.net/flyeek/article/details/43934945
为什么还要多此一举把txt生成图片呢?因为我电脑上的文本阅读器功能比较少,不能将文字缩放成特别小的尺寸,所以生成的字符画看不到全局,而图片可以。

这里挖个坑,剩下的以后写,部分代码如下
(二)、代码
public static boolean writePictureFromFile(File file,String output,
			 int IMAGE_WIDTH , int IMAGE_HEIGHT,final int fontSize,boolean whiteBackGround){
		boolean flag=false;
		if(file!=null&&file.exists()&&!file.isDirectory()){
			BufferedReader reader = null;
	        try {
	            reader = new BufferedReader(new FileReader(file));
	            //像素点和字体大小转化
//	        	IMAGE_WIDTH*=ptToPx(fontSize);
//	        	IMAGE_HEIGHT*=(ptToPx(fontSize)+2);
		        IMAGE_WIDTH*=((fontSize&1)==0? fontSize:fontSize+1); 
		        IMAGE_HEIGHT*=(fontSize+2);
//	        	StringOutputter .print("produce a image with Width:? ,Height:?",IMAGE_WIDTH,IMAGE_HEIGHT);
		        BufferedImage bimage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,BufferedImage.TYPE_INT_RGB);
		        //获取图像上下文
		        Graphics g = createGraphics(bimage,IMAGE_WIDTH,IMAGE_HEIGHT,fontSize,whiteBackGround);
		        String line;
		        //图片中文本行高
		        FontMetrics metrics = g.getFontMetrics();
//		        final int Y_LINEHEIGHT =  (ptToPx(fontSize)+2);//一般是像素大小+2
		        final int Y_LINEHEIGHT = metrics.getHeight(); //相当于fontsize+2;
//		        StringOutputter.print("@ :height:? ,Y_LINEHEIGHT: ?", height,Y_LINEHEIGHT);
		        int lineNum = 1;
		        try {
		            while((line = reader.readLine()) != null){
//		            	StringOutputter.print("Get Line[?]:?",lineNum,line);
		                g.drawString(line, 0, lineNum * Y_LINEHEIGHT);
//		                StringOutputter.print("lineNum * Y_LINEHEIGHT: ? ,width:?", lineNum * Y_LINEHEIGHT,metrics.stringWidth(line));
		                lineNum++;
		            }
		            g.dispose();
		            //保存为jpg图片
		            FileOutputStream out = new FileOutputStream(output);
	                ImageIO.write(bimage, "JPEG", out);
	                out.close();
	                StringOutputter.print("写文本图片(像素:(?,?))==>成功 (输出路径:?)",IMAGE_WIDTH,IMAGE_HEIGHT, output);
		        } catch (IOException e) {
		            e.printStackTrace();
		            return false;
		        }
		        return true;
	        } catch (FileNotFoundException e) {
	            e.printStackTrace();
	            return false;
	        }finally{
	        	if(reader!=null){
	        		try {
						reader.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
	        	}
	        }
	       
		}
		return flag;
	}
/**
 * 获取图片的上下文,设置样式
 * @param image 处理的图片对象
 * @param IMAGE_WIDTH 宽
 * @param IMAGE_HEIGHT 高
 * @param fontSize 字体大小
 * @param whiteBackGround 是否为白底黑字
 * @return
 */
private static Graphics createGraphics(final BufferedImage image,
		final int IMAGE_WIDTH,final  int IMAGE_HEIGHT,final int fontSize,final boolean whiteBackGround){
    if(IMAGE_WIDTH>0&&IMAGE_HEIGHT>0){
    	//创建图片
    	Graphics g = image.createGraphics();
    	//设置背景色
    	if(whiteBackGround){
    		g.setColor(null); 
    	}else{
    		g.setColor(Color.BLACK);
    	}
        g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);//绘制背景
        //设置前景色
        if(whiteBackGround){
        	 g.setColor(Color.BLACK); 
        }else{
        	 g.setColor(Color.WHITE); 
        }
        g.setFont(new Font(FONT_NAME, Font.PLAIN, fontSize)); //设置字体
        return g;
    }
	return null;
}

四、代码共享

挖个坑以后填…

大小姐(字符画图片)

这真的是字符串!

  • 9
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值