JAVA抠图证件照,大头照更换背景或透明

本文是基础版,使用算法实现请查看: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();

你可能感兴趣的:(开发工具包,java,spring)