OpenCV之图像分割(六) 绿幕背景视频抠图

视频中GMM或KMeans算法,实时性不行。

算法设计步骤:
这里写图片描述

HSV 颜色空间:
这里写图片描述

代码

    #include "../common/common.hpp"

    static Mat background_01, background_02;
    static Mat replace_and_blend(Mat &frame, Mat &mask);

    void main(int argc, char** argv) 
    {
        int arr[2][2] = { {1, 4}, {8, 12} };
        int * parr = arr[0];
        int num = *parr++; // 等同于 *(parr++), ++ 优先于 * 运算符,但是右++的特性是先赋值给表达式,然后再++
        cout << num << "," << *parr << endl; // 1,4
        int (*ppa)[2] = &arr[0]; // 二维数组的指针
        int ab = **(ppa + 1); // 行指针,指针步长是一行。指针步长可以理解为将变量的数据类型去掉一个* ,然后留下的数据类型的sizeof()就是步长
        cout << ab << endl; // 8

        background_01 = imread(getCVImagesPath("images/bg_01.jpg"));
        background_02 = imread(getCVImagesPath("images/bg_02.jpg")); // 替换成的背景
        VideoCapture capture;
        capture.open(getCVImagesPath("videos/01.mp4")); // 720P 的视频
        if (!capture.isOpened()) printf("could not find the video file...\n");

        Mat frame, hsv, mask;
        bool is = true; // 打印信息
        while (capture.read(frame)) 
        {
            cvtColor(frame, hsv, COLOR_BGR2HSV); // 转HSV
            /*
                void inRange( // 可实现二值化功能(类似threshold()函数)
                    InputArray src, // 输入要处理的图像,可以为单通道或多通道。
                    InputArray lowerb, // 包含下边界的数组或标量。
                    InputArray upperb, // 包含上边界数组或标量。
                    OutputArray dst // 输出的二值图像,与输入图像src 尺寸相同且为CV_8U 单通道
                            // dst每个像素位置的值为:若src对应位置像素的每个通道的颜色数据大于等于lowerb小于等于upperb对应通道的颜色数据时,dst为255,否则为0
                );
            */
            inRange(hsv, Scalar(35, 43, 46), Scalar(155, 255, 255), mask); // 在HSV中,Scalar的通道顺序为 H S V ?
            if (is) {
                is = false;
                // frame depth=0, type=16, channels=3    CV_8UC3
                cout << "frame depth=" << frame.depth() << ", type=" << frame.type() << ", channels=" << frame.channels() << endl;
                // hsv depth = 0, type = 16, channels = 3    CV_8UC3
                cout << "hsv depth=" << hsv.depth() << ", type=" << hsv.type() << ", channels=" << hsv.channels() << endl;
                // mask depth=0, type=0, channels=1
                cout << "mask depth=" << mask.depth() << ", type=" << mask.type() << ", channels=" << mask.channels() << endl;
            }
            imshow("inRange", mask); // mask 中元素的值,只有 0 或 255

            // 形态学操作,降低边缘的干扰
            Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
            morphologyEx(mask, mask, MORPH_CLOSE, k); // 去掉黑色前景色上的白点
            erode(mask, mask, k);
            GaussianBlur(mask, mask, Size(3, 3), 0, 0); // 腐蚀用的size是3,模糊的时候也要是3,效果才好

            Mat result = replace_and_blend(frame, mask);
            if (waitKey(1) == 27) break; // 帧间隔不用设置为帧率,因为上面的算法会耗时的
            imshow("matting5-15", result); // 时效性还是有点不好。。。
            imshow("sc5-15", frame);
        }
    }

    static bool go = true; // 打印信息,此变量放到函数内部速度会变慢一点。。
    Mat replace_and_blend(Mat &frame, Mat &mask) 
    {
        Mat result = Mat::zeros(frame.size(), frame.type());
        int h = frame.rows;
        int w = frame.cols;
        int dims = frame.channels();

        // replace and blend
        int m = 0;
        double wt = 0;

        int r = 0, g = 0, b = 0;
        int r1 = 0, g1 = 0, b1 = 0;
        int r2 = 0, g2 = 0, b2 = 0;

        double time = getTickCount();
        for (int row = 0; row < h; row++) 
        {
            uchar* current = frame.ptr<uchar>(row);
            uchar* bgrow = background_02.ptr<uchar>(row);
            uchar* maskrow = mask.ptr<uchar>(row);
            uchar* targetrow = result.ptr<uchar>(row);
            for (int col = 0; col < w; col++) 
            {
                m = *maskrow++;
                if (m == 255) { // 背景
                    *targetrow++ = *bgrow++;
                    *targetrow++ = *bgrow++;
                    *targetrow++ = *bgrow++;
                    current += 3;

                }
                else if (m == 0) {// 前景
                    *targetrow++ = *current++;
                    *targetrow++ = *current++;
                    *targetrow++ = *current++;
                    bgrow += 3;
                }
                else {
                    b1 = *bgrow++; // 用指针的方式,速度快一些
                    g1 = *bgrow++;
                    r1 = *bgrow++;

                    b2 = *current++;
                    g2 = *current++;
                    r2 = *current++;

                    // 权重
                    wt = m / 255.0;

                    // 混合
                    b = b1*wt + b2*(1.0 - wt);
                    g = g1*wt + g2*(1.0 - wt);
                    r = r1*wt + r2*(1.0 - wt);

                    *targetrow++ = b;
                    *targetrow++ = g;
                    *targetrow++ = r;
                }
            }
        }
        if (go) {
            go = false;
            cout << (getTickCount() - time) / getTickFrequency() << endl; // 0.00792733 ,指针速度比 mat.at<> 速度要快一个数量级!
        }

        return result;
    }

效果图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huanghuangjin/article/details/81461040