java实现文字堆砌图片?

偶然逛知乎看到一个代码,实现了用文字堆砌图片,效果如下:
(图侵删@xlzd)

clipboard.png

clipboard.png

源码是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 = '''
<html>
<head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
    <style type="text/css">
        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}};
        }
    </style>
</head>
<body>
<div>
{% for group in html_image %}
    {% for item in group %}<font color="#{{ item.color }}">{{ item.char }}</font>{% endfor %}
    <br>
{% endfor %}
</div>
</body>
</html>'''


_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)),
        )

我的效果如下:

clipboard.png

clipboard.png

可以看到高度只有差不多一半,第一行错位,左边莫名其妙多了很多黑色的字,可能跟图片大小有关,但是换大一点的图片后,上述问题依然存在

下面是代码:

    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("<html>\n");
            bufferedWriter.write("<body>\n");
            bufferedWriter.write("<style>\n");
            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("</style>");
            bufferedWriter.write("<div>\n");
            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("<font style=\"color:"+ rgbHex +"\">"+ chars +"</font>");
                    if (j == colBlockNum-1)
                        bufferedWriter.write("<br/>\n");
                    //重置
                    rgb[0] = 0;
                    rgb[1] = 0;
                    rgb[2] = 0;
                }
            }
            bufferedWriter.write("\n</div>");
            bufferedWriter.write("</body>\n");
            bufferedWriter.write("</html>\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;
    }
}
阅读 1.4k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题