偶然逛知乎看到一个代码,实现了用文字堆砌图片,效果如下:
(图侵删@xlzd)
源码是py写的,无奈没学过python,大概看懂了一些思路,假设字体是10px,那么就用一个字代替10*10的像素,颜色是这100个像素的RGB色平均值,自己尝试用java实现了一下,发现有一些问题,思考很久,没发现问题所在,有没有大佬指点下啊.
以下是源码:
#!/usr/bin/env python
# encoding=utf-8
from __future__ import print_function, unicode_literals
from collections import namedtuple
from itertools import cycle
import jinja2
from PIL import Image
Point = namedtuple('Point', ['x', 'y'])
Pixel = namedtuple('Pixel', ['r', 'g', 'b'])
RenderItem = namedtuple('RenderItem', ['color', 'char'])
RenderGroup = list
HTMLImage = list
TEMPLATE = '''
{{ title }}body {
margin: 0px; padding: 0px; line-height:100%; letter-spacing:0px; text-align: center;
min-width: {{width}}px;
width: auto !important;
font-size: {{size}}px;
background-color: #{{background}};
font-family: {{font_family}};
}
{% for group in html_image %}
{% for item in group %}{{ item.char }}{% endfor %}
{% endfor %}
'''
_c = cycle(r'/-\|')
def _progress_callback(percent):
if percent == 100:
print('\rDone! ')
else:
import sys, time
lca = getattr(_progress_callback, '_last_call_at', 0)
if time.time() - lca > 0.1:
_progress_callback._last_call_at = time.time()
sys.stdout.write('\r{} progress: {:.2f}%'.format(_c.next(), percent))
sys.stdout.flush()
class Img2HTMLConverter(object):
def __init__(self,
font_size=10,
char='䦗',
background='#000000',
title='img2html by xlzd',
font_family='monospace',
progress_callback=None):
self.font_size = font_size
self.background = background
self.title = title
self.font_family = font_family
if isinstance(char, str):
char = char.decode('utf-8')
self.char = cycle(char)
self._prg_cb = progress_callback or _progress_callback
def convert(self, source):
image = Image.open(source)
width, height = image.size
row_blocks = int(round(float(width) / self.font_size))
col_blocks = int(round(float(height) / self.font_size))
html_image = HTMLImage()
progress = 0.0
step = 1. / (col_blocks * row_blocks)
for col in xrange(col_blocks):
render_group = RenderGroup()
for row in xrange(row_blocks):
pixels = []
for y in xrange(self.font_size):
for x in xrange(self.font_size):
point = Point(row * self.font_size + x, col * self.font_size + y)
if point.x >= width or point.y >= height:
continue
pixels.append(Pixel(*image.getpixel(point)[:3]))
average = self.get_average(pixels=pixels)
color = self.rgb2hex(average)
render_item = RenderItem(color=color, char=self.char.next())
render_group.append(render_item)
progress += step
self._prg_cb(progress * 100)
html_image.append(render_group)
self._prg_cb(100)
return self.render(html_image)
def render(self, html_image):
template = jinja2.Template(TEMPLATE)
return template.render(
html_image=html_image,
size=self.font_size,
background=self.background,
title=self.title,
font_family=self.font_family,
width=self.font_size * len(html_image[0]) * 2
)
@staticmethod
def rgb2hex(pixel):
return '{:02x}{:02x}{:02x}'.format(*pixel)
@staticmethod
def get_average(pixels):
r, g, b = 0, 0, 0
for pixel in pixels:
r += pixel.r
g += pixel.g
b += pixel.b
base = float(len(pixels))
return Pixel(
r=int(round(r / base)),
g=int(round(g / base)),
b=int(round(b / base)),
)
我的效果如下:
可以看到高度只有差不多一半,第一行错位,左边莫名其妙多了很多黑色的字,可能跟图片大小有关,但是换大一点的图片后,上述问题依然存在
下面是代码:
public class Img2Html {
public static void convert(File imgPath, File resultPath, int fontSize,String chars) throws Exception{
BufferedImage bufferedImage = null;
BufferedWriter bufferedWriter = null;
try {
bufferedImage = ImageIO.read(imgPath);
bufferedWriter = new BufferedWriter(new FileWriter(resultPath));
//宽度
int width = bufferedImage.getWidth();
//高度
int height = bufferedImage.getHeight();
//
int[] rgb = new int[3];
//X轴字的个数
int rowBlockNum = Math.round((float)height/fontSize);
//Y轴字的个数
int colBlockNum = Math.round((float)width/fontSize);
//背景颜色
bufferedWriter.write("\n");
bufferedWriter.write("
\n");bufferedWriter.write("
bufferedWriter.write("div{\n");
bufferedWriter.write("background-color:#272822;\n");
bufferedWriter.write("font-size:"+ fontSize +"px;\n");
bufferedWriter.write("font-family:monospaced");
bufferedWriter.write("}\n");
bufferedWriter.write("");
bufferedWriter.write("
for (int i = 0; i < colBlockNum; i++) {
for (int j = 0; j < rowBlockNum; j++) {
for (int y = 0; y < fontSize; y++) {
for (int x = 0; x < fontSize; x++) {
//越界无视
if(x+j*fontSize >= width || +i*fontSize >= height){
continue;
}
int pixel = bufferedImage.getRGB(x+j*fontSize, y+i*fontSize);
//获取像素点RGB值
rgb[0] += (pixel & 0xff0000) >> 16;
rgb[1] += (pixel & 0xff00) >> 8;
rgb[2] += (pixel & 0xff);
}
}
//求平均值
int[] rgbAvg = getAvg(rgb,fontSize);
//转换成16进制
String rgbHex = rgb2hex(rgbAvg);
System.out.println(rgbAvg[0]+","+rgbAvg[1]+","+rgbAvg[2] + " hex:" + rgbHex);
bufferedWriter.write(""+ chars +"");
if (j == colBlockNum-1)
bufferedWriter.write("
\n");
//重置
rgb[0] = 0;
rgb[1] = 0;
rgb[2] = 0;
}
}
bufferedWriter.write("\n
bufferedWriter.write("\n");
bufferedWriter.write("\n");
}catch (Exception e){
e.printStackTrace();
}finally {
bufferedWriter.close();
}
}
public static String toHex(int num){
String hex = Integer.toHexString(num);
if(hex.length() == 2){
return hex;
}else {
return "0"+hex;
}
}
public static int[] getAvg(int[] rgb,int fontSize) {
int[] avg = new int[3];
avg[0] = rgb[0] / (fontSize * fontSize);
avg[1] = rgb[1] / (fontSize * fontSize);
avg[2] = rgb[2] / (fontSize * fontSize);
return avg;
}
public static String rgb2hex(int[] rgbAvg){
String rgbHex = "#" + toHex(rgbAvg[0]) + toHex(rgbAvg[1]) + toHex(rgbAvg[2]);
return rgbHex;
}
}