Java + openCV更换证件照背景色

news2024/11/29 6:26:20

最近在小红书上看到很多更换证件照背景色的需求,联想到以前自己也更换过证件照背景色而且还是付费的,碰巧最近在看一本书《Java+OpenCV高效入门》,于是查找资料,找到了通过技术解决这个需求的办法。
先看效果图(图片来自网络,如有侵权,请联系我删除):
在这里插入图片描述

在这里插入图片描述下面直接上代码,只需要一个函数就可以了:

/**
     * 算法实现步骤:
     * 1、加载原图像
     * 2、制作kmeans输入参数所需要的数据(kmeans的输入数据类型是CV_32F,所以不能直接使用原始图像的数据,因为原始图像的数据类型为CV_8UC1)
     * 3、使用kmeans算法实现图像分类,并得到分类标签
     * 4、创建遮罩:通过分类标签,将背景部分的颜色标记位0,将前景(人物)像素值标记位255
     * 5、先对mask执行形态学操作去除干扰的白点,在使用高斯模糊平滑前景和背景之前的过度
     * 6、创建一个3通道的目标输出结果Mat,然后将目标背景填充到背景区域,将前景部分填充到前景区域。
     * 7、输出图像
     * ps:算法的核心步骤其实就是找到mask,当mask找到之后就可以使用分类标签将背景和前景替换成为自己想要的像素。
     * 参考:https://www.cnblogs.com/tony-yang-flutter/p/16153446.html
     * @param photoPath  本地图片路径
     * @param rgbEnd  目标背景色
     * @return
     */
    @Override
    public Mat changePhotoBackgroundColor(String photoPath, double[] rgbBeg, double[] rgbEnd) {
        Mat src = Imgcodecs.imread(photoPath);
        //制作kmeans需要的数据
        int width = src.cols();
        int height = src.rows();
        int dims = src.channels();
        int sampleCount = width*height;//总共的像素点
        Mat points = new Mat(sampleCount, dims, CvType.CV_32F,new Scalar(10));

        int index = 0;
        for(int row = 0;row<height;row++){
            for(int col = 0;col<width;col++){
                index = row * width + col;
                double[] bgr = src.get(row, col);
                points.put(index,0, bgr[0]);
                points.put(index,1, bgr[1]);
                points.put(index,2, bgr[2]);
            }
        }

        int numCluster = 4;//多少个分类
        Mat labels = new Mat();//分类标签
        Mat centers = new Mat();//中心点
        TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 10, 0.1);
        kmeans(points,numCluster,labels,criteria,3,KMEANS_PP_CENTERS,centers);

        //创建遮罩
        Mat mask = Mat.zeros(src.size(),CvType.CV_8UC1);
        //找到背景像素的像素点位置
        index = src.rows() * 2 + 2;

        //找到像素点位置在labels中所对应的标签,找到这个标签以后就可以根据这个标签来判断前景和背景
        double[] cIndex = labels.get(index,0);
        for(int row=0;row<height;row++){
            for(int col=0;col<width;col++){
                index = row*width+col;
                double[] label = labels.get(index, 0);
                if(label[0] == cIndex[0]){//背景
                    mask.put(row, col, 0);
                }else{//前景
                    mask.put(row, col, 255);
                }
            }
        }

        //使用形态学腐蚀操作取出遮罩中的可能干扰正常结果的白点
        Mat kernel = getStructuringElement(MORPH_RECT,new Size(3,3),new Point(-1,-1));
        erode(mask,mask,kernel);
        //使用高斯模糊平滑边缘像素
        GaussianBlur(mask,mask,new Size(3,3),0,0);

        //执行图像像素融合,执行最终的背景替换,定义背景颜色
        double[] bgColor = new double[3];

        bgColor[0] = rgbEnd[2];
        bgColor[1] = rgbEnd[1];
        bgColor[2] = rgbEnd[0];

        //定义一个空的彩色图片
        Mat result = Mat.zeros(src.size(),CvType.CV_8UC3);

        //下面是背景融合的代码
        double w = 0.0;
        double b = 0, g = 0, r = 0;
        double b1 = 0, g1 = 0, r1 = 0;
        double b2 = 0, g2 = 0, r2 = 0;
        for(int row = 0;row<height;row++){
            for(int col=0;col<width;col++){
                double[] pix = mask.get(row,col);//获取像素值
                if(pix[0] == 255){//前景
                    result.put(row, col, src.get(row,col));
                }else if(pix[0] == 0){//背景
                    result.put(row, col, bgColor);
                }else{//需要像素融合的部分
                    w = pix[0] / 255.0;//权重
                    b1 = src.get(row,col)[0];
                    g1 = src.get(row,col)[1];
                    r1 = src.get(row,col)[2];

                    b2 = bgColor[0];
                    g2 = bgColor[1];
                    r2 = bgColor[2];

                    b = b1*w+b2*(1.0-w);
                    g = g1*w+g2*(1.0-w);
                    r = r1*w+r2*(1.0-w);
                    
                    result.put(row, col, b, g, r);
                }
            }
        }

        return result;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1256404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV入门11——图像的分割与修复

文章目录 图像分割的基本概念实战-分水岭法(一)实战-分水岭法(二)GrabCut基本原理实战-GrabCut主体程序的实现实战-GrabCut鼠标事件的处理实战-调用GrabCut实现图像分割meanshift图像分割视频前后景分离其它对视频前后影分离的方法图像修复 图像分割是计算机视觉中的一个重要领…

window环境搭建StarRocksFE节点

StarRocks部署–源码编译 前言 ​ 注意:本文借用了一些其他文章的一些截图&#xff0c;同时自己做了具体的编译步骤&#xff0c;添加了一些新的内容 ​ 目标&#xff1a; 编译StarRocks2.5.13版本FE节点代码&#xff0c;在本地window环境运行&#xff0c;可以访问到8030界面…

频剪辑软件Corel VideoStudio 会声会影2024最新7大新全新功能解析

我很喜欢视频剪辑软件Corel VideoStudio 会声会影2024&#xff0c;因为它使用起来很有趣。它很容易使用&#xff0c;但仍然给你很多功能和力量。视频剪辑软件Corel VideoStudio 会声会影2023让我与世界分享我的想法&#xff01;“这个产品的功能非常多&#xff0c;我几乎没有触…

【数据中台】开源项目(2)-Wormhole流式处理平台

Wormhole 是一个一站式流式处理云平台解决方案&#xff08;SPaaS - Stream Processing as a Service&#xff09;。 Wormhole 面向大数据流式处理项目的开发管理运维人员&#xff0c;致力于提供统一抽象的概念体系&#xff0c;直观可视化的操作界面&#xff0c;简单流畅的配置管…

《已解决: ImportError: Keras requires TensorFlow 2.2 or higher 问题》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

vscode 使用git提交前端代码

1、项目初始化git 如果是从其他地方拉的代码&#xff0c;把.git文件删除&#xff0c;再重新初始化。 2、提交代码 2.1、提交本地库 2.2、提交远程仓库 2.2.1、创建远程仓库 2.2.2、提交远程仓库–master分支 在本地添加远程仓库&#xff0c;设置别名为origin git remote…

首届教师案例教学竞赛一等奖作品上线至和鲸社区,快来学习!

细心的朋友可能已经发现&#xff0c;近期和鲸社区的频道页上线了一个新专区——“优秀参赛作品专区”。 图.和鲸社区频道页 迄今为止&#xff0c;和鲸参与/支持了 500 多场专业数据科学竞赛&#xff0c;包括面向气象、金融、医学、海洋等不同领域的&#xff0c;面向从业者、科学…

为你的项目加上微信登录(个人开发)

当我们开发个人项目的时候&#xff0c;为了用户登录的便捷性&#xff0c;经常会给我们的项目加上一些除了注册之外的方式&#xff0c;其中最常见的就是微信登录&#xff0c;但作为个人开发者&#xff0c;是无法使用微信的授权登录的&#xff0c;但是通过微信公众号可以获得同样…

【nginx】 实现限流

这里写自定义目录标题 前言正文nginx实现限流并发限制限制单IP并发数量限制单主机服务并发数量 速率限制限流效果 注意疑问参考链接 小结 前言 好久不见&#xff0c;还算为时不晚。最近一个月经历了工作的调整&#xff0c;技术栈从Java转向了Go和Python, 工作显得更忙了些&…

JavaScript基础—for语句、循环嵌套、数组、冒泡排序、综合案例—根据数据生成柱形图

版本说明 当前版本号[20231126]。 版本修改说明20231126初版 目录 文章目录 版本说明目录JavaScript 基础第三天笔记for 语句for语句的基本使用循环嵌套倒三角九九乘法表 数组数组是什么&#xff1f;数组的基本使用定义数组和数组单元访问数组和数组索引数据单元值类型数组长…

测试工程师必学看系列之Jmeter_性能测试:性能测试的流程和术语

性能测试的流程 一、准备工作 1、系统基础功能验证 一般情况下&#xff0c;只有在系统基础功能测试验证完成、系统趋于稳定的情况下&#xff0c;才会进行性能测试&#xff0c;否则性能测试是无意义的。2、测试团队组建 根据该项目的具体情况&#xff0c;组建一个几人的性能测试…

Linux面试题(三)

目录 34、du 和 df 的定义&#xff0c;以及区别&#xff1f; 35、awk 详解。 36、当你需要给命令绑定一个宏或者按键的时候&#xff0c;应该怎么做呢&#xff1f; 37、如果一个 linux 新手想要知道当前系统支持的所有命令的列表&#xff0c;他需要怎么做&#xff1f; 38、…

23年几个能打的UE4游戏技术选型

近期发现很多的精力放在游戏的整体技术选型以及产生的结果上面&#xff0c;所以回顾下几个游戏的选型和结果&#xff1b; 这里一个是自己玩游戏的画面流畅度的直接感受&#xff0c;以及一直非常喜爱的评测“数毛社”&#xff0c;digital foundry&#xff1b; 23年目前来看&…

【NeRF】3、MobileR2L | 移动端实时的神经光场(CVPR2023)

论文&#xff1a;Real-Time Neural Light Field on Mobile Devices 代码&#xff1a;https://github.com/snap-research/MobileR2L 出处&#xff1a;CVPR2023 贡献&#xff1a; 设计了一套移动端实时的 R2L 网络结构 MobileR2L&#xff0c;在 iphone13 上渲染一张 1008x756…

前端学习--React(4)路由

一、认识ReactRouter 一个路径path对应一个组件component&#xff0c;当我们在浏览器中访问一个path&#xff0c;对应的组件会在页面进行渲染 创建路由项目 // 创建项目 npx create router-demo// 安装路由依赖包 npm i react-router-dom// 启动项目 npm run start 简单的路…

【JavaEE】多线程 (2) --线程安全

目录 1. 观察线程不安全 2. 线程安全的概念 3. 线程不安全的原因 4. 解决之前的线程不安全问题 5. synchronized 关键字 - 监视器锁 monitor lock 5.1 synchronized 的特性 5.2 synchronized 使⽤⽰例 1. 观察线程不安全 package thread; public class ThreadDemo19 {p…

LeetCode中链表类题目十条血泪经验总结-全程干货

文章目录 前言干货经验汇总第一梯队第二梯队 力扣代表性链表题目推荐 前言 链表是以节点&#xff08;node&#xff09;存储的链式存储结构&#xff0c;一个node包含一个data域&#xff08;存放数据&#xff09;和一个next域&#xff08;存放下一个node的指针&#xff09;&…

Co-DETR:DETRs与协同混合分配训练论文学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2211.12860.pdf 代码地址&#xff1a; GitHub - Sense-X/Co-DETR: [ICCV 2023] DETRs with Collaborative Hybrid Assignments Training 摘要 作者提出了一种新的协同混合任务训练方案&#xff0c;即Co-DETR&#xff0c;以从多种标…

Web框架与Django简介

Web框架与Django简介 一、Web应用的组成 我们为了开发一款Web软件首先要了解什么才是Web应用软件呢&#xff1f; 对于传统的应用软件来说&#xff0c;基本都是部署单机使用&#xff0c;而Web应用软件就不一样&#xff0c;Web应用软件是基于B/S架构的&#xff0c;B和S都在不同…

Vue常见的实现tab切换的两种方法

目录 方法一&#xff1a;事件绑定属性绑定 效果图 完整代码 方法二&#xff1a;属性绑定 动态组件 component标签 效果图 完整代码 方法一&#xff1a;事件绑定属性绑定 效果图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta c…