Java + openCV更换证件照背景色

最近在小红书上看到很多更换证件照背景色的需求,联想到以前自己也更换过证件照背景色而且还是付费的,碰巧最近在看一本书《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;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日一题2023.11.26——个位数统计【PTA】

题目要求&#xff1a; 输入格式&#xff1a; 每个输入包含 1 个测试用例&#xff0c;即一个不超过 1000 位的正整数 N。 输出格式&#xff1a; 对 N 中每一种不同的个位数字&#xff0c;以 D:M 的格式在一行中输出该位数字 D 及其在 N 中出现的次数 M。要求按 D 的升序输出。…

设备树是什么?

设备树&#xff1a; 设备树DTS(Device Tree Source) 描述设备信息的独立的文件。 为什么要引入设备树&#xff1f; 随着芯片的发展&#xff0c;Linux内核中就包含着越来越多这些描述设备的代码&#xff0c;导致Linux内核代码会很臃肿。因此引入了设备树文件&#xff0c;从…

Runloop解析

RunLoop 前言 ​ 本文介绍RunLoop的概念&#xff0c;并使用swift和Objective-C来描述RunLoop机制。 简介 ​ RunLoop——运行循环&#xff08;死循环&#xff09;&#xff0c;它提供了一个事件循环机制在程序运行过程中处理各种事件&#xff0c;例如用户交互、网络请求、定…

前端review

关于实时预览vs code中的颜色代码需要安装的插件&#xff0c;包括html文件格式中的颜色代码安装Flutter Color插件 VSCode 前端常用插件集合 1.Auto Close Tag自动闭合HTML/XML标签 2.Auto Rename Tag自动完成另一侧标签的同步修改 3.Beautify格式化代码&#xff0c;值得注…

Python武器库开发-前端篇之html概述(二十八)

前端篇之html概述(二十八) html概述 HTML5是构建Web内容的一种语言描述方式。HTML5是互联网的下一代标准&#xff0c;是构建以及呈现互联网内容的一种语言方式&#xff0e;被认为是互联网的核心技术之一。HTML产生于1990年&#xff0c;1997年HTML4成为互联网标准&#xff0c;…

斐讯K2结合Padavan实现锐捷认证破解方法

前言 众所周知&#xff0c;校园网在传统模式下是不能直接插路由使用的&#xff0c;但苦于校园网只能连接一台设备的烦恼&#xff0c;不得不“另辟蹊径”来寻求新的解决路径&#xff0c;这不&#xff0c;它来了&#xff0c;它来了&#xff0c;它带着希望走来了。 本文基于斐讯…

基于Haclon的图形镜像案例

项目要求&#xff1a; 图为HALCON的例图“green-dot”&#xff0c;请将其中的圆形图案按水平和垂直两个方向分别进行镜像。 项目知识&#xff1a; 首先要用BLOB分析的方法&#xff0c;得到圆形图案的目标区域&#xff0c;再对其进行镜像。 在HALCON中与镜像相关的算子为mirr…

Android平台GB28181设备接入模块开发填坑指南

技术背景 为什么要开发Android平台GB28181设备接入模块&#xff1f;这个问题不再赘述&#xff0c;在做Android平台GB28181客户端的时候&#xff0c;媒体数据这块&#xff0c;我们已经有了很好的积累&#xff0c;因为在此之前&#xff0c;我们就开发了非常成熟的RTMP推送、轻量…

Deepin使用记录-deepin系统下安装RabbitMq

目录 0、引言 1、由于RabbitMq是erlang语言开发的&#xff0c;所有需要先安装erlang 2、更新源并安装RabbitMq 3、安装完成之后&#xff0c;服务是启动的&#xff0c;可以通过以下语句查看状态 4、这样安装完成之后&#xff0c;是看不到web页面的&#xff0c;需要再安装一…

PTA NeuDs_数据库题目

二.单选题 1.数据库应用程序的编写是基于数据库三级模式中的。 A.模式 B.外模式 C.内模式 D.逻辑模式 用户应用程序根据外模式进行数据操作&#xff0c;通过外模式一模式映射&#xff0c;定义和建立某个外模式与模式间的对应关系 2.对创建数据库模式一类的数据库对象的授权…

【开源项目】C#.NET 扩展库 -- Com.Gitusme.Net.Extensiones.Core

目录 1、项目介绍 2、集成方式 方法一&#xff1a;项目中通过Nuget包管理器安装导入 方法二&#xff1a;手动从Nuget官网下载&#xff0c;下载地址&#xff1a; 3、代码中导入命名空间 4、版本变更说明 1.0.7 版本 1.0.6 版本 1.0.5 版本 1.0.4 版本 5、演示示例 示…

Unity SRP 管线【第三讲:URP 光照】

3.2.3 以前属于Shader部分&#xff0c;Shader部分不进行讲解。 这里只涉及Unity内部管线的设置问题。 文章目录 3.2.3 向GPU发送灯光数据设置光源数据设置主光源设置额外点光源 Shader中的数据 3.2.3 向GPU发送灯光数据 在UniversalRenderPipeline.cs > RenderSingleCamera…

叠加原理(superposition principle)

叠加原理&#xff08;superposition principle&#xff09;指对线性系统而言&#xff0c;两个或多个输入产生的输出&#xff0c;等于这几个输入单独引起的输出的和&#xff0c;即输入的叠加等于各输入单独引起的输出的叠加。 例如&#xff0c;如果输入产生的输出是&#xff0c;…

如何使用nginx部署静态资源

Nginx可以作为静态web服务器来部署静态资源&#xff0c;这个静态资源是指在服务端真实存在&#xff0c;并且能够直接展示的一些文件数据&#xff0c;比如常见的静态资源有html页面、css文件、js文件、图片、视频、音频等资源相对于Tomcat服务器来说&#xff0c;Nginx处理静态资…

大数据面试大厂真题【附答案详细解析】

1.Java基础篇&#xff08;阿里、蚂蚁、字节、携程、快手、杭州银行等&#xff09; 问题&#xff1a;HashMap的底层实现原理 答案&#xff1a; 在jdk1.8之前&#xff0c;hashmap由 数组-链表数据结构组成&#xff0c;在jdk1.8之后hashmap由 数组-链表-红黑树数据结构组成&…

②⑩② 【读写分离】Sharding - JDBC 实现 MySQL读写分离[SpringBoot框架]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Sharding-JDBC Sharding-JDBC介绍使用 Shardin…

DistilBERT模型训练实战

LLM似乎正在接管世界&#xff0c;但许多人仍然不真正理解他们是如何运作的。 我从事机器学习工作已有几年&#xff0c;并且对自然语言处理和最近的进展非常着迷。 尽管我阅读了大部分随附的论文&#xff0c;但训练这些模型对我来说仍然是一个谜&#xff0c;这就是为什么我决定…

Java(七)(Lambda表达式,正则表达式,集合(Collection,Collection的遍历方式))

目录 Lambda表达式 省略写法(要看懂) 正则表达式 语法 案例 正则表达式的搜索替换和分割内容 集合进阶 集合体系结构 Collection Collection的遍历方式 迭代器 增强for循环 Lambda表达式遍历Collection List集合 ArrayList LinkedList 哈希值 HashSet底层原理 …

中东客户亲临广东育菁装备参观桌面型数控机床生产

近日&#xff0c;中东地区的一位重要客户在广东育菁装备有限公司的热情接待下&#xff0c;深入了解了该公司生产的桌面型数控机床。这次会面不仅加强了双方在业务领域的交流&#xff0c;也为中国与中东地区的经济合作描绘出更美好的前景。 在育菁装备公司各部门主要负责人及工作…

赋值,浅拷贝,深拷贝

1.前置知识 数据分为基本类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol)和引用类型(Object)基本类型&#xff1a;直接存储在栈内存中的数据引用类型&#xff1a;指向改数据的指针变量存储在栈内存中&#xff0c;真实的数据存储在堆内存中引用类型在栈内存…