本文是基础版,使用算法实现请查看:http://t.csdn.cn/U8Y4C
使用hutool包,maven引入
cn.hutool
hutool-all
5.7.20
package cn.xxx.utils;
import cn.hutool.core.img.Img;
import cn.hutool.core.util.ObjectUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
/**
* 图片工具
* author xwt
* @date 2021/11/18 14:44
*/
public class ImgUtils {
private ImgUtils() {
}
/***
* 图片替换透明
* 主要用于证件照
*
* @param pressImg 文件
* @return Image对象
*/
public static Image imageReplaceColor(Image pressImg) {
int width = pressImg.getWidth(null);
int height = pressImg.getHeight(null);
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.drawImage(pressImg, 0, 0, width, height, null);
// 替换白色为透明旋转替换,上\左\右
ImgUtils.faceMatting(image, 0, new Color(255,255,255), 40, null);
image = (BufferedImage) new Img(image, "png").rotate(90).getImg();
ImgUtils.faceMatting(image, 0, new Color(255,255,255), 40,null);
image = (BufferedImage) new Img(image, "png").rotate(180).getImg();
ImgUtils.faceMatting(image, 0, new Color(255,255,255), 40,null);
image = (BufferedImage) new Img(image, "png").rotate(90).getImg();
return image;
}
/***
* 根据图片路径获取图片
*
* @param path 文件路径
* @return Image对象
*/
public static Image getImageByPath(String path){
try {
return ImageIO.read(new File(path));
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("图片获取异常", e);
}
}
/**
* 图片添加水印
*
* @param pressImg 水印文件
* @param targetImg 目标文件
* @param x 水印添加的坐标x
* @param y 水印添加的坐标y
* @param w 水印图片的宽度
* @param h 水印图片的高度
*/
public static BufferedImage pressImage(Image pressImg, Image targetImg, int x, int y, int w,int h) {
//目标文件
BufferedImage image = new BufferedImage(targetImg.getWidth(null), targetImg.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.drawImage(targetImg, 0, 0, targetImg.getWidth(null), targetImg.getHeight(null), null);
// 水印文件
g.drawImage(pressImg, x, y, w, h, null);
return image;
}
/****
* 更换图片中的颜色为指定颜色,单一颜色
* 自动计算调整区间值
*
* @param bi 图片
* @param alpha 阿尔法值,rgba 的a,代表透明的0~1程度
* @param coverColor 需要替换的颜色
* @param deviation 需要替换的颜色的偏差值,给区间值,该方法会自动计算调整区间值
* @param changeColor 更换的颜色rgb值, null代表透明
*/
public static void faceMatting(BufferedImage bi, int alpha, Color coverColor, int deviation, Integer changeColor){
// 状态,记录异常rgb
int state = -1;
// 填补的rgb颜色,null为透明
int rgb;
// 得到图片的长宽、x,y
int width = bi.getWidth();
int height = bi.getHeight();
int minx = bi.getMinX();
int miny = bi.getMinY();
/*
* 这里是遍历图片的像素,因为要处理图片的背色,所以要把指定像素上的颜色换成目标颜色
* 这里 是一个二层循环,遍历长和宽上的每个像素
*/
for (int i = minx; i < width; i++) {
for (int j = miny; j < height; j++) {
// 得到指定像素(i,j)上的RGB值,
int pixel = bi.getRGB(i, j);
// 分别进行位操作得到 r g b上的值
int r = (pixel & 0xff0000) >> 16;
int g = (pixel & 0xff00) >> 8;
int b = (pixel & 0xff);
// 进行判断是否使用替换默认的颜色
rgb = (ObjectUtil.isNull(changeColor) ? (((alpha + 1) << 24) | (pixel & 0x00ffffff)) : changeColor);
if(coverColor.getRed() != r || coverColor.getGreen() != g || coverColor.getBlue() != b){
// 自动计算
Color aveColor = getAveColor(bi, 70, j, i, height);
int rDeviation = getAuthDeviation(aveColor.getRed(), deviation);
int gDeviation = getAuthDeviation(aveColor.getGreen(), deviation);
int bDeviation = getAuthDeviation(aveColor.getBlue(), deviation);
// 进行换色操作,判断图片中rgb值是否在换色范围的像素
if (((coverColor.getRed() - r) < rDeviation) || ((coverColor.getGreen() - g) < gDeviation) || ((coverColor.getBlue() - b) < bDeviation)) {
bi.setRGB(i, j, rgb);
}else if(pixel == rgb){
state = j;
} else if(j - state > 4){
break;
}
}else{
// 等于要替换的值,直接替换
bi.setRGB(i, j, rgb);
}
}
// 恢复默认值
state = -1;
}
}
/***
* 计算偏差值
* @param aveColor 颜色平均值
* @param deviation 偏差值
* @return 偏差值
*/
private static int getAuthDeviation(int aveColor, int deviation){
BigDecimal divide = BigDecimal.valueOf(aveColor).divide(BigDecimal.valueOf(255), 2, BigDecimal.ROUND_HALF_UP);
// 1 - divide(0.98)
BigDecimal subtract = BigDecimal.valueOf(1).subtract(divide);
// 0.02 * 100
BigDecimal multiply = subtract.multiply(BigDecimal.valueOf(deviation));
return multiply.intValue();
}
/***
* 获取向前的颜色平均值
* @param proPx 向前数
* @param curY 高度
* @param maxHeight 最大高度
* @return 颜色平均值
*/
private static Color getAveColor(BufferedImage bi, int proPx, int curY, int curX, int maxHeight) {
int i1 = curY + proPx;
ArrayList r = new ArrayList<>();
ArrayList g = new ArrayList<>();
ArrayList b = new ArrayList<>();
proPx = Math.min(maxHeight, i1);
for (int i = curY; i < proPx; i++) {
int pixel = bi.getRGB(curX, i);
// 得到指定像素(i,j)上的RGB值,
// 分别进行位操作得到 r g b上的值
r.add((pixel & 0xff0000) >> 16);
g.add((pixel & 0xff00) >> 8);
b.add((pixel & 0xff));
}
return new Color(average(r),average(g),average(b));
}
/***
* 计算平均数
* @param arr 数据聚合
* @return 平均数
*/
public static int average(ArrayList arr) {
// 去掉一个最小值
Integer min = Collections.min(arr);
arr.remove(min);
// 去掉一个最大值
Integer max = Collections.max(arr);
if (!min.equals(max)){
arr.remove(max);
}
int sum = arr.stream().mapToInt(j -> j).sum();
BigDecimal divide = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(arr.size()), 2, BigDecimal.ROUND_HALF_UP);
return divide.intValue();
}
}
该工具证件大头照使用
实现方法faceMatting,替换纯色
增强方法imageReplaceColor
使用:
// 根据图片路径获取Image
Image image = ImgUtils.getImageByPath("d:");
// 1、抠图,去掉白色,该方法为封装过的
Image transparentImg = ImgUtils.imageReplaceColor(image);
// 2、抠图,自定义
ImgUtils.faceMatting(image, 0, new Color(255,255,255), 40, null);
//水印文件结束,imgPath:存入图片路径
FileOutputStream out = new FileOutputStream(imgPath);
ImageIO.write(bufferedImage, "png", out);
out.close();