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;
    }
相关推荐
苹果醋33 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花8 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端11 分钟前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan18 分钟前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
可峰科技20 分钟前
斗破QT编程入门系列之二:认识Qt:编写一个HelloWorld程序(四星斗师)
开发语言·qt
customer0822 分钟前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈24 分钟前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫
WaaTong26 分钟前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
面试鸭28 分钟前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展