Java + openCV更换证件照背景色

最近在小红书上看到很多更换证件照背景色的需求,联想到以前自己也更换过证件照背景色而且还是付费的,碰巧最近在看一本书《Java+OpenCV高效入门》,于是查找资料,找到了通过技术解决这个需求的办法。

先看效果图(图片来自网络,如有侵权,请联系我删除):

下面直接上代码,只需要一个函数就可以了:

java 复制代码
/**
     * 算法实现步骤:
     * 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;
    }
相关推荐
小徐Chao努力2 分钟前
Spring AI Alibaba A2A 使用指南
java·人工智能·spring boot·spring·spring cloud·agent·a2a
梅孔立9 分钟前
【实用教程】python 批量解析 EML 邮件文件 存成txt ,可以利用 AI 辅助快速生成年终总结
开发语言·python
rannn_11111 分钟前
【Git教程】概述、常用命令、Git-IDEA集成
java·git·后端·intellij-idea
我家领养了个白胖胖11 分钟前
向量化和向量数据库redisstack使用
java·后端·ai编程
骄傲的心别枯萎14 分钟前
RV1126 NO.57:ROCKX+RV1126人脸识别推流项目之读取人脸图片并把特征值保存到sqlite3数据库
数据库·opencv·计算机视觉·sqlite·音视频·rv1126
c#上位机20 分钟前
C#异步编程之async、await
开发语言·c#
苹果醋322 分钟前
Java设计模式实战:从面向对象原则到架构设计的最佳实践
java·运维·spring boot·mysql·nginx
郑州光合科技余经理32 分钟前
实战分享:如何构建东南亚高并发跑腿配送系统
java·开发语言·javascript·spring cloud·uni-app·c#·php
爱装代码的小瓶子33 分钟前
【c++进阶】C++11新特性:一切皆可{}初始化
开发语言·c++·visual studio
yaoxin52112337 分钟前
273. Java Stream API - Stream 中的中间操作:Mapping 操作详解
java·开发语言·python