OpenCV背景减除

8

我有一张背景场景的图片和一张在前景有物体的同一场景的图片。现在我想通过背景减法来创建前景中物体的掩码。这两张图片都是RGB格式。

我已经创建了以下代码:

cv::Mat diff;
diff.create(orgImage.dims, orgImage.size, CV_8UC3);
diff = abs(orgImage-refImage);

cv::Mat mask(diff.rows, diff.cols, CV_8U, cv::Scalar(0,0,0));
//mask = (diff > 10);

for (int j=0; j<diff.rows; j++) {
    // get the address of row j
    //uchar* dataIn= diff.ptr<uchar>(j);
    //uchar* dataOut= mask.ptr<uchar>(j);
    for (int i=0; i<diff.cols; i++) {
        if(diff.at<cv::Vec3b>(j,i)[0] > 30 || diff.at<cv::Vec3b>(j,i)[1] > 30 || diff.at<cv::Vec3b>(j,i)[2] > 30)
            mask.at<uchar>(j,i) = 255;
    }
}

我不知道自己是否做对了这件事?

2个回答

9
请查看OpenCV中的inRange函数。这将允许您同时为3通道图像设置多个阈值。
因此,要创建您正在寻找的掩模,请执行以下操作:
inRange(diff, Scalar(30, 30, 30), Scalar(255, 255, 255), mask);

这样做比尝试自己访问每个像素要快。

编辑:如果您正在尝试进行皮肤检测,我建议先进行皮肤检测,然后再进行背景减除以去除背景。否则,您的皮肤检测器将不得不考虑由减法引起的强度变化。

查看我的其他答案,了解有关皮肤检测的良好技术。

编辑:

这样做会更快吗?

int main(int argc, char* argv[])
{
    Mat fg = imread("fg.jpg");
    Mat bg = imread("bg.jpg");

    cvtColor(fg, fg, CV_RGB2YCrCb);
    cvtColor(bg, bg, CV_RGB2YCrCb);

    Mat distance = Mat::zeros(fg.size(), CV_32F);

    vector<Mat> fgChannels;
    split(fg, fgChannels);

    vector<Mat> bgChannels;
    split(bg, bgChannels);

    for(size_t i = 0; i < fgChannels.size(); i++)
    {
        Mat temp = abs(fgChannels[i] - bgChannels[i]);
        temp.convertTo(temp, CV_32F);

        distance = distance + temp;
    }


    Mat mask;
    threshold(distance, mask, 35, 255, THRESH_BINARY);

    Mat kernel5x5 = getStructuringElement(MORPH_RECT, Size(5, 5));
    morphologyEx(mask, mask, MORPH_OPEN, kernel5x5);

    imshow("fg", fg);
    imshow("bg", bg);
    imshow("mask", mask);

    waitKey();

    return 0;
}

这段代码根据您输入的图像生成此掩膜:

enter image description here

最后,使用我的简单阈值方法得到的结果如下:

    Mat diff = fgYcc - bgYcc;
    vector<Mat> diffChannels;
    split(diff, diffChannels);

    // only operating on luminance for background subtraction...
    threshold(diffChannels[0], bgfgMask, 1, 255.0, THRESH_BINARY_INV);

    Mat kernel5x5 = getStructuringElement(MORPH_RECT, Size(5, 5));
    morphologyEx(bgfgMask, bgfgMask, MORPH_OPEN, kernel5x5);

这将产生以下掩码: 输入图像描述

谢谢你的评论!但是我这么做对吗?因为在差异图像中,有些地方的像素完全是黑色的,而输入图像和参考图像之间确实存在差异... - pietro
你能发布一下上述算法中所使用的图片吗? - mevatron
这是图片:http://dl.dropbox.com/u/5276376/image.jpg 参考图片:http://dl.dropbox.com/u/5276376/refImage.jpg 还有它们之间的差异:http://dl.dropbox.com/u/5276376/diff.jpg(是的,图像和参考图像之间有轻微的照明差异)。正如您所看到的,差异图像中的手臂包含许多黑色像素。因此,使用例如30的阈值将丢失所有这些像素。 - pietro
嗯,我认为问题在于我正在使用RGB颜色空间,这不是一个感知均匀的颜色空间。两种颜色可能看起来非常相似,而另外两种颜色之间的距离相同,但看起来却非常不同...所以现在我首先将图像转换为YCrCb颜色空间,但结果更糟。也许是因为我使用了错误的类型来保存每个像素,因此低于零的值将被剪切为零或其他原因?? 目前,我正在使用CV_32F来计算差异: diff.create(table.dims, table.size, CV_32F); diff = abs(table - refRoi); - pietro
我知道皮肤检测技术,但我也想得到其他部位的轮廓,例如衣服...因此需要使用背景减除技术。 - pietro

2
我认为这样做可以得到正确的结果:(在YCrCb颜色空间中),但访问每个像素很慢,所以我需要找到另一种算法。
    cv::Mat mask(image.rows, image.cols, CV_8U, cv::Scalar(0,0,0));

    cv::Mat_<cv::Vec3b>::const_iterator itImage= image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itRef= refRoi.begin<cv::Vec3b>();
    cv::Mat_<uchar>::iterator itMask= mask.begin<uchar>();

    for ( ; itImage!= itend; ++itImage, ++itRef, ++itMask) {
        int distance = abs((*itImage)[0]-(*itRef)[0])+
                        abs((*itImage)[1]-(*itRef)[1])+
                        abs((*itImage)[2]-(*itRef)[2]);

        if(distance < 30)
            *itMask = 0;
        else
            *itMask = 255;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接