5.图片转SVG使用示例

一灰灰blogQuickMediaimage-plugin约 1149 字大约 4 分钟

本文为 image-pluginopen in new window 中图转SVG的使用参考示例

1. 基本原理

我们知道图片是由一个一个的像素点组成的,将图片转换为svg的一个思路就是将图片中的每个像素点直接转换为svg中的矩形颜色块,这样就可以直接生成一个对应的svg文本了

一个基本的转换实现可以参见 SvgParseTest.javaopen in new window

public static String SVG_START = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
            "<svg xmlns=\"http://www.w3.org/2000/svg\"\n" +
            "        viewBox=\"0 0 {width} {height}\"\n" +
            "        style=\"width: 100%; height: 100%; overflow: auto; fill: {BG_COLOR}\">\n";
public static String SVG_END = "\n</svg>";


public String toRect(String color, int w, int h, int x, int y) {
    return "<rect" + " fill=\"" + color + "\" height=\"" + h + "\" width=\"" + w + "\" y=\"" + y + "\" x=\"" + x + "\"/>";
}
@Test
public void basicParse() throws Exception {
    String img = "https://spring.hhui.top/spring-blog/css/images/avatar.jpg";

    BufferedImage bufferedImage = ImageLoadUtil.getImageByPath(img);
    int w = bufferedImage.getWidth(), h = bufferedImage.getHeight();

    StringBuilder builder = new StringBuilder();
    String s = StrUtil.replace(SVG_START, "{width}", String.valueOf(w), "{height}", String.valueOf(h), "{BG_COLOR}", "white", "{FONT_COLOR}", "black");
    builder.append(s).append("\n");

    for (int x = 0; x < w; x++) {
        for (int y = 0; y < h; y++) {
            int color = bufferedImage.getRGB(x, y);
            if (color == Color.WHITE.getRGB()) {
                // 背景直接不要
                continue;
            }

            String htmlColor = ColorUtil.int2htmlColor(color);
            builder.append(toRect(htmlColor, 1, 1, x, y));
        }
    }
    builder.append(SVG_END);
    FileWriteUtil.saveContent(new File("/tmp/parseSvg-out1.svg"), builder.toString());
}
输出图
输出图

上面实现就是基本的像素块的替换了,可以看到输出的svg文本较大;接下来我们可以再优化一下,首先利用ImgPixelWrapper将图片转换为像素块(降低精度),然后再将像素块的图片转换为svg

因此可以对上面的实现进行一个微调

@Test
public void test2svg() {
    int size = 8;
    BufferedImage pixelImg = ImgPixelWrapper.build()
            .setSourceImg("https://spring.hhui.top/spring-blog/css/images/avatar.jpg")
            .setBlockSize(size)
            .setPixelType(PixelStyleEnum.PIXEL_COLOR_AVG)
            .build().asBufferedImg();

    int w = pixelImg.getWidth(), h = pixelImg.getHeight();
    StringBuilder builder = new StringBuilder();
    String s = StrUtil.replace(SVG_START, "{width}", String.valueOf(w), "{height}", String.valueOf(h), "{BG_COLOR}", "white", "{FONT_COLOR}", "black");
    builder.append(s).append("\n");
    for (int i = 0; i < w; i += size) {
        for (int j = 0; j < h; j += size) {
            int color = pixelImg.getRGB(i, j);
            Color c = new Color(color, true);
            if (c.getRed() >= 250 && c.getGreen() >= 250 && c.getBlue() >= 250) {
                continue;
            }
            String htmlColor = ColorUtil.int2htmlColor(color);
            builder.append(toRect(htmlColor, size, size, i, j)).append("\n");
        }
    }
    builder.append(SVG_END);
    System.out.println(builder);
    System.out.println("over");
    FileWriteUtil.saveContent(new File("/tmp/parseSvg-out2.svg"), builder.toString());
}

比如上面设置size = 8,相当于将原图精度损失8倍,对应生成的svg如下

输出图
输出图

2. 使用示例

2.1 参数说明

核心参数四个,其中source传图为必填

参数名类型说明是否必填
sourceBufferedImage原始图
blockSizeint对于转字符图时,它控制字符大小;对于灰度/像素处理时,这个表示像素化的处理操作非必填,默认 4
scaleRateDouble缩放比例,1 表示输出的图不缩放; > 1,表示生成的图,按倍数扩大非必填,默认 1
bgPredicatePredicate<Color>背景色判断方式,传参为 intColor,如果返回true,表示认定为背景色;否则不是非必填,默认纯白色为背景

2.2 使用示例

使用姿势形如

SvgParserWrapper
      .of(png) // 制定图片
      .setBgPredicate(c -> { // 设置背景判断方式
          if (c.getRed() >= 50 && c.getRed() <= 60
                  && c.getGreen() >= 55 && c.getGreen() <= 62
                  && c.getBlue() >= 60 && c.getBlue() <= 65) {
              return true;
          }
          if (c.getRed() >= 0xd7 && c.getGreen() >= 0x5f && c.getBlue() >= 0xf0) {
              return true;
          }
          return false;
      })
      .build()
      // 将svg输出到指定文件中,若目录不存在会自动创建缺省的目录
      .asFile("...");

下面是一个简单的使用示例

@Test
public void png2svg() throws IOException {
    // 图片转svg
    String png = "https://ci.xiaohongshu.com/d5137769-1836-cc20-c1eb-20af1109dc7a?imageView2/2/w/1080/format/jpg";
    SvgParserWrapper
            .of(png)
            .setScaleRate(0.3f)
            .setBlockSize(1)
            .setBgPredicate(c -> {
                if (c.getRed() >= 50 && c.getRed() <= 60
                        && c.getGreen() >= 55 && c.getGreen() <= 62
                        && c.getBlue() >= 60 && c.getBlue() <= 65) {
                    return true;
                }
                if (c.getRed() >= 0xd7 && c.getGreen() >= 0x5f && c.getBlue() >= 0xf0) {
                    return true;
                }
                return false;
            })
            .build()
            .asFile("/tmp/dlam.svg");
    System.out.println("----over----");
}

原图svg图

接下来再看一个真实人像的转换过程,如下

@Test
public void parsePhoto() throws Exception {
    String photo = "/tmp/quick-media/lyf.png";
    SvgParserWrapper.of(photo)
            .setScaleRate(0.4)
            .setBlockSize(1)
            .setBgPredicate(c -> {
                return c.getAlpha() < 10;
            })
            .build().asFile("/tmp/quick-media/lyf.svg");
    System.out.println("---over---");
}


// 用文字进行替换颜色单元格
public void parsePhoto() throws Exception {
    String txt = "刘亦菲";
    AtomicInteger ato = new AtomicInteger(1);
    String photo = "d:/quick-media/lyf.png";
    SvgParserWrapper.of(photo)
            .setScaleRate(4)
            .setBlockSize(12)
            .setBgPredicate(c -> {
                return c.getAlpha() < 10;
            })
            .setSvgCellParse((color, x, y, size) -> {
                // 使用文字来填充像素单元格
                return ImgPixelHelper.getSvgTxtCell(txt.charAt(ato.getAndAdd(1) % 3) + "", color, x, y, size);
            })
            .build().asFile("d:/quick-media/lyf2.svg");
    System.out.println("---over---");
}

说明:原图来自网上 https://www.5youqu.com/bizhi/124906.htmlopen in new window,做了背景剔除

对比结果如下

原图svg图
Loading...