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

2023-10-26

本文是基础版,使用算法实现请查看:http://t.csdn.cn/U8Y4C

使用hutool包,maven引入

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.20</version>
</dependency>
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<Integer> r = new ArrayList<>();
        ArrayList<Integer> g = new ArrayList<>();
        ArrayList<Integer> 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<Integer> 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();

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA抠图证件照,大头照更换背景或透明 的相关文章

  • 按钮和窗口之间的空间

    我这里有这段代码 其想法是在主窗口中在文本区域旁边有两个按钮 但我尚未添加 在尝试使用 GridBagLayout 并在此过程中扯掉我的头发后 我决定不使用布局并在不可调整大小的窗口内手动放置按钮 import java awt impor
  • 当 SwingWorker 在后台工作时,对话框没有响应

    基本上我得到了一个以 main 方法作为入口点的 JFrame 在这个主要方法中 程序必须下载一些图像 为了通知用户程序将随时启动 我想显示一个简单的对话框 如果我将对话框设置为模式 我必须在启动程序后关闭它才能触发下载 如果我将其设置为非
  • 将 Mockito 模拟注入 Spring bean

    我想将 Mockito 模拟对象注入到 Spring 3 bean 中 以便使用 JUnit 进行单元测试 我的 bean 依赖项当前是通过使用注入的 Autowired私有成员字段上的注释 我考虑过使用ReflectionTestUtil
  • System.out 什么时候不会出现在 Java 控制台中?

    我明白任何要标准化的东西 System out 将出现在 Java 控制台窗口中 启用时 我在某个地方发现 在某些情况下可能无法尝试这样做 例如 在 Swing 应用程序中 是这样吗 基本上 什么情况或设置我不希望在控制台中看到标准输出 例
  • 让线程休眠的更好方法

    我一直在编写有关 Java 8 中 2D 图形的教程 当时 NetBeans 给了我一个提示 Thread Sleep会影响性能 然而 尽管我已经找到了几种更好的方法 但我还没有找到一种方法来包含它们而不弄乱代码 package platf
  • “赠送”应用内购买 Android

    有没有办法将 Google Billing 中的应用内购买 赠送 给特定帐户 我把这个问题放在这里是因为如果有一种方法可以以编程方式完成它 那很好 但不是必须的 在官方文档中找不到任何相关内容 我想要这个的原因是因为我的一个应用程序目前处于
  • 删除最低位

    给定一个二进制数 删除最低位的最快方法是什么 01001001010 gt 01001001000 它将在代码中用于迭代变量的位 伪代码如下 while bits 0 index getIndexOfLowestOrderBit bits
  • 如何将 JTextField 添加到 JFrame 的菜单栏?

    我一直在尝试重载 JMenu 并放入一些自定义代码来支持 JTextField 但进展并不顺利 我在这里的主要目的是在菜单项的右侧添加一个搜索字段 因此 我在左侧有 文件 编辑 帮助 之类的东西 然后在右侧是搜索栏 几乎就像某些浏览器中的谷
  • 将两个表视图绑定在一起,以便它们同步滚动

    我想将两个表视图绑定在一起 以便它们同步滚动 我怎么做 我无法找到如何访问表格视图的滚动条 我做了一个CSS hack来将Tableview与外部滚动条绑定 一个滚动条控制两个表格视图 我的想法的概述 创建两个表视图 制作一个垂直滚动条 在
  • 如何更改鼠标进入/鼠标退出时按钮的图标图像?

    我想更改鼠标输入和鼠标退出时按钮的图标图像 private void jButton1MouseEntered java awt event MouseEvent evt this jButton1 setBackground Color
  • 如何通过 RestTemplate 发布字节数组

    目标 使用 RestTemplate 发布图像 目前正在使用这个的变体 MultiValueMap
  • 根据条件更改 JSlider 的最小值和最大值

    我正在 Netbeans 中创建 Swing GUI 此 GUI 的目的是打开一个 缓冲 图像 在 JLabel 中作为图标 并对其应用仿射变换 现在我正在做 4 个转换 如下所示 现在 每个变换都需要两个滑块来更改 X 和 Y 值 但旋转
  • Spring方法获取给定类型的所有bean

    我试图从一个相同类型的豆子中获取所有豆子FileSystemXmlApplicationContext 我正在使用factory getBeansOfType SomeType class 但我注意到它只返回顶级 bean 是否有任何其他方
  • Struts 2 - 使用 CKEditor 拦截上传的图像文件

    我有一个CKEditor在网站的不同页面上 所以我将上传内容设置为true以及让它工作的所有配置内容 并且图像上传工作得很好 Send it to the Server 标签 但从这里我想拦截或互动upload函数能够将图像上传到文件夹中
  • 在 Android 应用程序中读取 CSV 文件

    我正在开发一个概念验证应用程序 以便我可以在我正在制作的更大的应用程序中实现该功能 我对 Java 和 Android 开发有点陌生 但希望这个问题不会太简单或太复杂 基本上 我试图从 CSV 文件中读取字符串列表 并使其可用于在应用程序的
  • Java 接口合成方法生成,同时缩小返回类型

    我有 2 个接口和 2 个返回类型 interface interfaceA Publisher
  • JAXB 是否支持 xsd:restriction?

  • 使用 lambda 或 Stream API 合并流以生成交替序列

    我有一些按预期返回 Stream 的代码 但也许可以用某种类型的 lambda 或 stream 操作替换它 而不是耗尽 a 中的迭代器while loop 它只是一种交替流中元素的方法first and second当其中一个元素耗尽时停
  • 使用 Java Swing 平均成绩 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个家庭作业 我一直在编码 我以
  • Android 并获取 id 转换为字符串的视图

    在 Android 项目的 Java 代码中 如果您想要视图资源的引用 您可以执行以下操作 View addButton findViewById R id button 0 在上面的 R id button 0 不是一个字符串 是否可以通

随机推荐

  • 游戏开发日志17(保存技能树信息)

    最开始的想法是这样的 在GameManage中写一个public List
  • 剑指 Offer 50. 第一个只出现一次的字符(C++实现)

    剑指 Offer 50 第一个只出现一次的字符https leetcode cn problems di yi ge zhi chu xian yi ci de zi fu lcof 遍历两次数组 第一次遍历 通过哈希表记录每个字母出现的次
  • CMake中configure_file的使用

    CMake中的configure file命令用于将一个文件拷贝到另一个位置并修改其内容 其格式如下 configure file
  • 微信网页开发调用微信jssdk接口遇到的坑以及最终解决方法 (持续更新)

    1 微信网页开发调用jssdk时报permission denied 大致是两个原因 1 首先注册时未将你所调用的接口名字添加至jsApiList 2 第二个就是你的这个公众号没有权限使用这个api 例如在开发环境中的微信页面就无法调取这个
  • (六)使用Transformers进行情感分析

    文章目录 准备数据 搭建模型 训练模型 推断 完整代码 在本笔记本中我们将使用在本 论文中介绍的Transformer模型 具体来说 我们将使用来自这篇 论文中的BERT Bidirectional Encoder Representati
  • 《KyLin学习理解》-01-KyLin麒麟的简介及其思想

    1 美图 1 诞生背景 HIVE是数据仓库 是把存储在HDFS分布式文件系统的存储文件映射到类似于关系型数据库的东西 举个例子 假设有一个文件存储在本地 opt hzjs lcc work pro txt文件 文件的格式为 1 产品120
  • html 单选打勾效果,checkbox 实现单选效果(html)

    note 在html 标签类中的checkbox实现单选效果 在最近的开发项目中 客户要求使用小方格子实现 单选 功能 显然圆点的radio被out了 只能选择chckbox的方块样式 也在网上搜过 可能有点儿脑残 没有找到 废话不多说直接
  • c++网络编程3:UDP编程

    一 概念 UDP是传输层中面向无连接的协议 所以UDP丢包后是不会重传的 而且他在编程上服务端和客户端是没有区别的 有的只是 虚拟上 的服务端和客户端 他在编程的实现上也很简单 不像TCP那么复杂 二 UDP终端的编程 由于UDP在服务端和
  • 不同型号服务器esxi,如果管理多个esxi服务器

    如果管理多个esxi服务器 没有 装一个vcenter吧 可以集中管理 多台服务器之间还可以做双机热备等功能 很好用 至于注册序列号 你漫天都是 你可以把分数拉高一点 我送给你一个 哈哈 如何给esxi搭建ntp服务器 提在ESX ESXi
  • Windows系统的JDK安装与配置

    1 选择JDK版本 以在Windows 64位平台上安装JDK 8版本为例 JDK 8 Windows版官网下载地址 https www oracle com java technologies downloads java8 window
  • Redis的安装

    环境 用的Linux系统阿里云服务器 1 在redis官网下载最新稳定版的安装包 官网网址 https redis io 2 下载完的安装包通过Xftp上传到 opt目录下 3 连接上远程服务器60 205 189 176端口22 输入下面
  • 离线实时一体化数仓与湖仓一体—云原生大数据平台的持续演进

    林伟 阿里云智能研究员 阿里云智能通用计算平台MaxCompute 机器学习PAI平台技术负责人 本篇内容将从三个部分为读者讲述离线实时一体化数仓与湖仓一体 云原生大数据平台的持续演进 通过从数据湖到数仓的历史 反思为什么要做湖仓一体 以及
  • 向“全栈”进发,大型线上商城实战项目,Spring Boot + Vue 前后端分离版本的商城来了(文末有视频)

    新蜂商城 Vue 版本 它来了 文末有视频 如上图所示 新蜂商城 Vue 版本已经开发完成 这是新蜂商城开源项目的第一个大版本更新 根据大量的用户调研 最终决定将新蜂商城升级为 Spring Boot Vue 两个流行技术栈的前后端分离商城
  • Web开发-基础环境配置

    Web开发 基础环境配置 回炉再造 2021 Vue3 0 前端全家桶学习笔记 web前端职业发展路线 技术范围广 发展速度快 兼容浏览器众多 核心技术 html css JavaScript BOM DOM 新的技术 html5 css3
  • QT5使用PCAN读取CAN数据

    文章目录 QT5使用PCAN读取CAN数据 CAN Bus example 重点讲解 pro文件 创建CAN bus Device 参数设置 接收报文 PCAN支持的QCanBusDevice ConfigurationKey类型 QT5使
  • IDEA 插件代理设置

    一 ctrl alt s 打开设置
  • pytorch JIT浅解析

    概要 Torch Script中的核心数据结构是ScriptModule 它是Torch的nn Module的类似物 代表整个模型作为子模块树 与普通模块一样 ScriptModule中的每个单独模块都可以包含子模块 参数和方法 在nn M
  • Python告诉你:李子柒的螺蛳粉到底有多火?

    居家隔离的日子里 各类方便速食食品成了许多人的心头爱 特别是螺蛳粉 异军突起 火遍全网 几乎卖到脱销 有的螺蛳粉热销店铺的购买页面还显示 现在下单 预售40天后发货 这是种什么操作 万万没想到 这些日子发不出货的 除了口罩 还有螺蛳粉 今天
  • 单调队列优化的DP问题

    概述 单调队列就是通过排除求最值时候的冗余 从而是队列具有性质 可以方便求解问题 DP的两个阶段 朴素DP的基本原理 闫氏DP分析法 对朴素DP进行优化 闫氏DP分析法的拓展 在一个有限的集合中求最值 单调队列练习 135 最大子序和 输入
  • JAVA抠图证件照,大头照更换背景或透明

    本文是基础版 使用算法实现请查看 http t csdn cn U8Y4C 使用hutool包 maven引入