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;
    }
相关推荐
鸽芷咕1 分钟前
【Python报错已解决】xlrd.biffh.XLRDError: Excel xlsx file; not supported
开发语言·python·机器学习·bug·excel
铁匠匠匠24 分钟前
【C总集篇】第八章 数组和指针
c语言·开发语言·数据结构·经验分享·笔记·学习·算法
猿饵块27 分钟前
cmake--get_filename_component
java·前端·c++
编程小白煎堆28 分钟前
C语言:枚举类型
java·开发语言
秋邱33 分钟前
C++: 类和对象(上)
开发语言·c++
王哈哈嘻嘻噜噜35 分钟前
c语言中“函数指针”
java·c语言·数据结构
好多吃的啊37 分钟前
背景图鼠标放上去切换图片过渡效果
开发语言·javascript·ecmascript
神奇夜光杯42 分钟前
Python酷库之旅-第三方库Pandas(123)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
qq_3391911443 分钟前
spring boot admin集成,springboot2.x集成监控
java·前端·spring boot
Bill661 小时前
OpenCV GUI常用函数详解
人工智能·opencv·计算机视觉