feat: 使用爽的接口进行人像抠图

main
luochuan 6 days ago
parent 04ee849a95
commit 1f80d800fb

@ -0,0 +1,393 @@
package org.alis.excel.model;
import com.github.hui.quick.plugin.base.awt.ColorUtil;
import com.github.hui.quick.plugin.base.awt.GraphicUtil;
import com.github.hui.quick.plugin.base.constants.MediaType;
import com.github.hui.quick.plugin.base.file.FileReadUtil;
import com.github.hui.quick.plugin.base.file.FileWriteUtil;
import com.github.hui.quick.plugin.base.gif.GifDecoder;
import com.github.hui.quick.plugin.base.gif.GifHelper;
import com.github.hui.quick.plugin.base.io.IoUtil;
import com.github.hui.quick.plugin.image.helper.ImgPixelHelper;
import com.github.hui.quick.plugin.image.util.FontUtil;
import com.github.hui.quick.plugin.image.wrapper.pixel.ImgPixelOptions;
import com.github.hui.quick.plugin.image.wrapper.pixel.context.PixelContextHolder;
import com.github.hui.quick.plugin.image.wrapper.pixel.model.IPixelStyle;
import com.github.hui.quick.plugin.image.wrapper.pixel.model.PixelStyleEnum;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import static com.github.hui.quick.plugin.image.util.PredicateUtil.conditionGetOrElse;
/**
* @author lc
* @date 2024/11/9 17:20
**/
public class ImageWrapper {
private static final Logger LOG = LoggerFactory.getLogger(ImageWrapper.class);
private final ImgPixelOptions pixelOptions;
static {
ImageIO.scanForPlugins();
}
public static ImageWrapper.Builder build() {
return new ImageWrapper.Builder();
}
private ImageWrapper(ImgPixelOptions pixelOptions) {
this.pixelOptions = pixelOptions;
}
/**
*
*
* @return
*/
public BufferedImage asBufferedImg() {
if (pixelOptions.getSource() == null) {
throw new IllegalArgumentException("bufferedImage cannot be null.");
}
return parseImg(pixelOptions.getSource()).left;
}
/**
* gif
*
* @return
*/
public ByteArrayOutputStream asGif() {
GifDecoder decoder = pixelOptions.getGifSource();
if (decoder == null) {
throw new IllegalArgumentException("gif source img cannot be null.");
}
int gifCnt = decoder.getFrameCount();
// Pair.left 渲染的字符图, Pair.right gif图展示间隔时间
List<ImmutablePair<BufferedImage, Integer>> renderImgList = new ArrayList<>(gifCnt);
for (int index = 0; index < gifCnt; index++) {
ImmutablePair<BufferedImage, PixelContextHolder.ImgPixelChar> pair = parseImg(decoder.getFrame(index));
renderImgList.add(ImmutablePair.of(pair.left, decoder.getDelay(index)));
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GifHelper.saveGif(renderImgList, outputStream);
return outputStream;
}
/**
*
*
* @param fileName
* @return
* @throws Exception
*/
public boolean asFile(String fileName) throws Exception {
File file = new File(fileName);
FileWriteUtil.mkDir(file.getParentFile());
if (pixelOptions.getGifSource() != null) {
try (ByteArrayOutputStream output = asGif()) {
FileOutputStream out = new FileOutputStream(file);
out.write(output.toByteArray());
out.flush();
out.close();
}
return true;
}
BufferedImage bufferedImage = asBufferedImg();
if (!ImageIO.write(bufferedImage, pixelOptions.getPicType(), file)) {
throw new IOException("save pixel render image to: " + fileName + " error!");
}
return true;
}
/**
*
*
* @return
*/
public List<List<String>> asChars() {
// 静态图的转换
if (pixelOptions.getSource() != null) {
return PixelContextHolder.toPixelChars(parseImg(pixelOptions.getSource()).getRight());
}
// gif图的转换
GifDecoder decoder = pixelOptions.getGifSource();
int gifCnt = decoder.getFrameCount();
PixelContextHolder.ImgPixelChar imgPixelChar = null;
for (int index = 0; index < gifCnt; index++) {
ImmutablePair<BufferedImage, PixelContextHolder.ImgPixelChar> pair = parseImg(decoder.getFrame(index));
pair.right.setPre(imgPixelChar);
imgPixelChar = pair.right;
}
return PixelContextHolder.toPixelChars(imgPixelChar);
}
/**
* svg
*
* @return
*/
public List<String> asSvg() {
List<List<String>> chars = asChars();
List<String> result = new ArrayList<>(chars.size());
for (List<String> txt : chars) {
result.add(ImgPixelHelper.ascii2svg(txt, ColorUtil.color2htmlColor(pixelOptions.getBgColor()),
ColorUtil.color2htmlColor(pixelOptions.getFontColor())));
}
return result;
}
/**
* svg
*
* @param file
* @throws Exception
*/
public void asSvgFile(String file) throws Exception {
FileWriteUtil.FileInfo fileInfo = FileWriteUtil.saveFile(file, "svg");
List<String> context = asSvg();
if (context.size() == 1) {
FileWriteUtil.saveContent(fileInfo, context.get(0));
return;
}
String lastName = fileInfo.getFilename();
for (int i = 0; i < context.size(); i++) {
fileInfo.setFilename(lastName + i);
FileWriteUtil.saveFile(fileInfo, context.get(i));
}
}
private ImmutablePair<BufferedImage, PixelContextHolder.ImgPixelChar> parseImg(BufferedImage source) {
if (pixelOptions.getPixelType().scaleUp()) {
return parseAndScaleImg(source);
}
try {
PixelContextHolder.newPic();
if (pixelOptions.getRate() != 1) {
source = GraphicUtil.scaleImg((int) (source.getWidth() * pixelOptions.getRate()), (int) (source.getHeight() * pixelOptions.getRate()), source);
}
int picWidth = source.getWidth();
int picHeight = source.getHeight();
int blockSize = pixelOptions.getBlockSize();
IPixelStyle pixelType = pixelOptions.getPixelType();
BufferedImage pixelImg = new BufferedImage(picWidth / blockSize * blockSize, picHeight / blockSize * blockSize, source.getType());
Graphics2D g2d = GraphicUtil.getG2d(pixelImg);
g2d.setColor(null);
g2d.fillRect(0, 0, picWidth, picHeight);
for (int y = 0; y < picHeight; y += blockSize) {
for (int x = 0; x < picWidth; x += blockSize) {
pixelType.render(g2d, source, pixelOptions, x, y);
}
}
g2d.dispose();
return ImmutablePair.of(pixelImg, PixelContextHolder.getPixelChar());
} finally {
PixelContextHolder.clear();
}
}
private ImmutablePair<BufferedImage, PixelContextHolder.ImgPixelChar> parseAndScaleImg(BufferedImage source) {
try {
PixelContextHolder.newPic();
int blockSize = pixelOptions.getBlockSize();
int outWidth = source.getWidth() * blockSize;
int outHeight = source.getHeight() * blockSize;
IPixelStyle pixelType = pixelOptions.getPixelType();
BufferedImage pixelImg = new BufferedImage(outWidth, outHeight, source.getType());
Graphics2D g2d = GraphicUtil.getG2d(pixelImg);
g2d.setColor(null);
g2d.fillRect(0, 0, outWidth, outHeight);
for (int y = 0; y < source.getHeight(); y += 1) {
for (int x = 0; x < source.getWidth(); x += 1) {
pixelType.render(g2d, source, pixelOptions, x, y);
}
}
g2d.dispose();
return ImmutablePair.of(pixelImg, PixelContextHolder.getPixelChar());
} finally {
PixelContextHolder.clear();
}
}
public static class Builder {
private static final String DEFAULT_CHAR_SET = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\\\"^`'. ";
private static final double DEFAULT_RATE = 1D;
private static final int DEFAULT_BLOCK_SIZE = 1;
private final ImgPixelOptions pixelOptions;
private String fontName;
private int fontStyle;
private Builder() {
pixelOptions = new ImgPixelOptions();
}
/**
* gif/jpeg/png
*
* @param img
* @return
*/
public ImageWrapper.Builder setSourceImg(String img) {
try {
return setSourceImg(FileReadUtil.getStreamByFileName(img));
} catch (IOException e) {
throw new RuntimeException("failed to load " + img, e);
}
}
/**
* jpeg/png
*
* @param img
* @return
*/
public ImageWrapper.Builder setSourceImg(BufferedImage img) {
pixelOptions.setSource(img);
return this;
}
public ImageWrapper.Builder setSourceImg(InputStream inputStream) {
try (ByteArrayInputStream target = IoUtil.toByteArrayInputStream(inputStream)) {
MediaType media = MediaType.typeOfMagicNum(FileReadUtil.getMagicNum(target));
if (media != null) {
pixelOptions.setPicType(media.getExt());
}
if (media == MediaType.ImageGif) {
GifDecoder gifDecoder = new GifDecoder();
gifDecoder.read(target);
pixelOptions.setGifSource(gifDecoder);
} else {
pixelOptions.setSource(ImageIO.read(target));
}
} catch (IOException e) {
LOG.error("load backgroundImg error!", e);
throw new RuntimeException("load sourceImg error!", e);
}
return this;
}
public ImageWrapper.Builder setBlockSize(int size) {
pixelOptions.setBlockSize(size);
return this;
}
/**
* + +
*
* @param pixelType
* @return
*/
public ImageWrapper.Builder setPixelType(IPixelStyle pixelType) {
pixelOptions.setPixelType(pixelType);
return this;
}
/**
*
*
* @param chars
* @return
*/
public ImageWrapper.Builder setChars(String chars) {
pixelOptions.setChars(chars);
return this;
}
/**
*
*
* @param rate
* @return
*/
public ImageWrapper.Builder setRate(Double rate) {
pixelOptions.setRate(rate);
return this;
}
public ImageWrapper.Builder setFontName(String fontName) {
this.fontName = fontName;
return this;
}
public ImageWrapper.Builder setFontStyle(int fontStyle) {
this.fontStyle = fontStyle;
return this;
}
public ImageWrapper.Builder setPicType(String picType) {
pixelOptions.setPicType(picType);
return this;
}
public ImageWrapper.Builder setBgColor(int color) {
pixelOptions.setBgColor(ColorUtil.int2color(color));
return this;
}
public ImageWrapper.Builder setBgColor(Color color) {
pixelOptions.setBgColor(color);
return this;
}
public ImageWrapper.Builder setFontColor(int color) {
pixelOptions.setFontColor(ColorUtil.int2color(color));
return this;
}
public ImageWrapper.Builder setFontColor(Color color) {
pixelOptions.setFontColor(color);
return this;
}
public ImageWrapper.Builder setFontSize(int fontSize) {
pixelOptions.setFontSize(fontSize);
return this;
}
public ImageWrapper.Builder setBgPredicate(Predicate<Integer> predicate) {
pixelOptions.setBgPredicate(predicate);
return this;
}
public ImageWrapper build() {
pixelOptions.setBlockSize(conditionGetOrElse((s) -> s > 0, pixelOptions.getBlockSize(), DEFAULT_BLOCK_SIZE));
pixelOptions.setPixelType(conditionGetOrElse(Objects::nonNull, pixelOptions.getPixelType(), PixelStyleEnum.CHAR_COLOR));
pixelOptions.setChars(conditionGetOrElse(Objects::nonNull, pixelOptions.getChars(), DEFAULT_CHAR_SET));
pixelOptions.setRate(conditionGetOrElse(Objects::nonNull, pixelOptions.getRate(), DEFAULT_RATE));
pixelOptions.setBgColor(conditionGetOrElse(Objects::nonNull, pixelOptions.getBgColor(), Color.WHITE));
// 默认将全透明作为背景色
pixelOptions.setBgPredicate(conditionGetOrElse(Objects::nonNull, pixelOptions.getBgPredicate(), integer -> integer == 0));
pixelOptions.setFontColor(conditionGetOrElse(Objects::nonNull, pixelOptions.getFontColor(), Color.BLACK));
pixelOptions.setFontSize(conditionGetOrElse(s -> pixelOptions.getFontSize() > 0, Math.min(pixelOptions.getFontSize(), pixelOptions.getBlockSize()), pixelOptions.getBlockSize()));
fontStyle = conditionGetOrElse((s) -> s >= 0 && s <= 3, fontStyle, Font.ITALIC);
pixelOptions.setFont(FontUtil.getFontOrDefault(fontName, fontStyle, pixelOptions.getFontSize()));
pixelOptions.setPicType(conditionGetOrElse(Objects::nonNull, pixelOptions.getPicType(), MediaType.ImageJpg.getExt()));
return new ImageWrapper(pixelOptions);
}
}
}

@ -0,0 +1,64 @@
package org.alis.excel.model;
import com.github.hui.quick.plugin.base.awt.ColorUtil;
import com.github.hui.quick.plugin.image.wrapper.pixel.ImgPixelOptions;
import com.github.hui.quick.plugin.image.wrapper.pixel.context.PixelContextHolder;
import com.github.hui.quick.plugin.image.wrapper.pixel.model.IPixelStyle;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Objects;
/**
* @author lc
* @date 2024/11/9 17:02
**/
public enum StyleEnum implements IPixelStyle {
CHAR_SEQ_SCALE_UP {
public Color calculateColor(int red, int green, int blue, int size) {
return null;
}
@Override
public Color getAverage(int[] colors) {
int red = 0;
int green = 0;
int blue = 0;
for (int color : colors) {
red += ((color & 0xff0000) >> 16);
green += ((color & 0x00ff00) >> 8);
blue += (color & 0x0000ff);
}
int len = colors.length;
return calculateColor(red, green, blue, len);
}
public boolean scaleUp() {
return true;
}
public void draw(Graphics2D g2d, ImgPixelOptions options, int x, int y) {
int index = PixelContextHolder.getPixelChar().getAndUpdateSeqIndex(options.getChars().length());
char ch = options.getChars().charAt(index);
int offsetSize = (options.getBlockSize() - options.getFontSize()) / 2;
g2d.drawString(String.valueOf(ch), x * options.getBlockSize() + offsetSize, y * options.getBlockSize() + offsetSize);
}
public void render(Graphics2D g2d, BufferedImage source, ImgPixelOptions options, int x, int y) {
int color = source.getRGB(x, y);
if (!options.getBgPredicate().test(color)) {
if (g2d.getFont() == null || !Objects.equals(g2d.getFont().getName(), options.getFont().getName()) || g2d.getFont().getSize() != options.getFontSize()) {
g2d.setFont(options.getFont());
}
g2d.setColor(ColorUtil.int2color(color));
this.draw(g2d, options, x, y);
}
}
};
StyleEnum() {
}
}

@ -1,11 +1,24 @@
package org.alis.excel.service;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.hui.quick.plugin.base.awt.ColorUtil;
import com.github.hui.quick.plugin.image.wrapper.pixel.ImgPixelWrapper;
import com.github.hui.quick.plugin.image.wrapper.pixel.model.IPixelStyle;
import com.github.hui.quick.plugin.image.wrapper.pixel.model.PixelStyleEnum;
import org.alis.excel.model.ImageWrapper;
import org.alis.excel.model.StyleEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;
/**
* @author lc
@ -16,23 +29,45 @@ public class ImageService implements Runnable {
@Override
public void run() {
try {
ImgPixelWrapper.build()
// 这里采用爽提供的接口服务抠图获取base64编码的图片
// 接口地址 https://ai-zjz.cn/api/human_matting post
// String json = HttpUtil.createRequest(Method.POST, "https://ai-zjz.cn/api/human_matting")
// .form("input_image", new File("/Users/lily/Downloads/id.jpeg"))
// .execute().body();
// JSONObject jsonObj = JSONUtil.parseObj(json);
// String base64 = jsonObj.getStr("image_base64").replace("data:image/png;base64,","");
// 图片进行解码
// byte[] decode = Base64.getDecoder().decode(base64);
// 先存图片到本地
// File opFile = new File("/Users/lily/Downloads/url.jpg");
// if(!opFile.exists()){
// if(!opFile.createNewFile()){
// return;
// }
// }
// BufferedImage readImage = ImageIO.read(new ByteArrayInputStream(decode));
// ImageIO.write(readImage, "png", new File("/Users/lily/Downloads/url.png"));
ImageWrapper.build()
// .setSourceImg(readImage)
.setSourceImg("/Users/lily/Downloads/mylife-removebg-preview.png")
.setChars("李萍")
.setFontName("Tanugo-S")
.setBlockSize(24)
.setFontSize(22)
.setBlockSize(2)
.setFontSize(1)
.setFontStyle(Font.BOLD)
.setBgPredicate(color -> {
// 指定背景色,不渲染文本
// // 指定背景色,不渲染文本
if (color == 0) return true;
Color rc = ColorUtil.int2color(color);
// 将白色当作背景色
return rc.getRed() >= 245 && rc.getGreen() >= 245 && rc.getBlue() >= 245;
})
.setPixelType(PixelStyleEnum.CHAR_SEQ_SCALE_UP)
// .setPixelType(PixelStyleEnum.CHAR_SEQ_SCALE_UP)
.setPixelType(StyleEnum.CHAR_SEQ_SCALE_UP)
.build().asFile("/Users/lily/Downloads/transfer.png");
} catch (Exception e) {
e.printStackTrace();
}
}
}

Loading…
Cancel
Save