【问题标题】:Subtract blue background from image by OpenCV C++通过 OpenCV C++ 从图像中减去蓝色背景
【发布时间】:2013-05-28 04:25:40
【问题描述】:

我是 OpenCV 和 C++ 的初学者,但现在我必须找到解决这个问题的方法: 我有一个蓝色背景的人的图像,现在我必须从图像中减去背景,然后用另一个图像替换它。 现在我认为有两种方法可以解决这个问题,但我不知道哪个更好:

解决方案 1

  1. 将图像转换为黑白
  2. 将其用作遮罩以减去背景。

解决方案 2

  1. 使用 coutour 查找背景,
  2. 然后减去它。

我已经实现了解决方案 1,但结果并不如我所愿。 您是否知道还有另一种更好的解决方案,或者有人已经将其作为源代码实现了? 感谢您的帮助。

我在这里更新我的源代码,请给我一些意见

    //Get the image with person
cv::Mat imgRBG = imread("test.jpg");
//Convert this image to grayscale
cv::Mat imgGray = imread("test.jpg",CV_LOAD_IMAGE_GRAYSCALE);
//Get the background from image
cv::Mat background = imread("paris.jpg");

cv::Mat imgB, imgW;
//Image with black background but inside have some area black
threshold(imgGray, imgB, 200, 255, CV_THRESH_BINARY_INV);

cv::Mat imgTemp;
cv::Mat maskB, maskW;
cv::Mat imgDisplayB, imgDisplayW;
cv::Mat imgDisplay1, imgDisplay2, imgResult;    

//Copy image with black background, overide the original image
//Now imgTemp has black background wrap the human image, and inside the person, if there're some white area, they will be replace by black area
imgRBG.copyTo(imgTemp, imgB);

//Now replace the black background with white color
cv::floodFill(imgTemp, cv::Point(imgTemp.cols -10 ,10), cv::Scalar(255.0, 255.0, 255.0));
cv::floodFill(imgTemp, cv::Point(10,10), cv::Scalar(255.0, 255.0, 255.0));
cv::floodFill(imgTemp, cv::Point(10,imgTemp.rows -10), cv::Scalar(255.0, 255.0, 255.0));
cv::floodFill(imgTemp, cv::Point(imgTemp.cols -10,imgTemp.rows -10), cv::Scalar(255.0, 255.0, 255.0));

//Convert to grayscale
cvtColor(imgTemp,imgGray,CV_RGB2GRAY);

//Convert to B&W image, now background is black, other is white
threshold(imgGray, maskB, 200, 255, CV_THRESH_BINARY_INV);

//Convert to B&W image, now background is white, other is black
threshold(imgGray, maskW, 200, 255, CV_THRESH_BINARY);

//Replace background of image by the black mask
imgRBG.copyTo(imgDisplayB, maskB);

//Clone the background image
cv::Mat overlay = background.clone();

//Create ROI
cv::Mat overlayROI = overlay(cv::Rect(0,0,imgDisplayB.cols,imgDisplayB.rows));

//Replace the area which will be human image by white color
overlayROI.copyTo(imgResult, maskW);

//Add the person image 
cv::addWeighted(imgResult,1,imgDisplayB,1,0.0,imgResult);

imshow("Image Result", imgResult);


waitKey();

return 0;

【问题讨论】:

  • 尝试分段。将 cv::grabCut 与非常接近边界的前景蒙版一起使用(例如,将图像的 4 个角像素设置为前景像素)。
  • 亲爱的威廉。你能描述得更详细些吗?在这种情况下如何使用 grabCut?谢谢
  • 阅读this 并尝试this

标签: opencv background subtraction


【解决方案1】:

检查这个项目

https://sourceforge.net/projects/cvchromakey

void chromakey(const Mat under, const Mat over, Mat *dst, const Scalar& color) {

// Create the destination matrix
*dst = Mat(under.rows,under.cols,CV_8UC3);

for(int y=0; y<under.rows; y++) {
    for(int x=0; x<under.cols; x++) {

     if (over.at<Vec3b>(y,x)[0]  >= red_l && over.at<Vec3b>(y,x)[0]  <= red_h && over.at<Vec3b>(y,x)[1]  >= green_l && over.at<Vec3b>(y,x)[1]  <= green_h && over.at<Vec3b>(y,x)[2]  >= blue_l && over.at<Vec3b>(y,x)[2]  <= blue_h) 
         {
                 dst->at<Vec3b>(y,x)[0]= under.at<Vec3b>(y,x)[0]; 
                 dst->at<Vec3b>(y,x)[1]= under.at<Vec3b>(y,x)[1]; 
                 dst->at<Vec3b>(y,x)[2]= under.at<Vec3b>(y,x)[2];}
             else{
                 dst->at<Vec3b>(y,x)[0]= over.at<Vec3b>(y,x)[0];
                 dst->at<Vec3b>(y,x)[1]= over.at<Vec3b>(y,x)[1];
                 dst->at<Vec3b>(y,x)[2]= over.at<Vec3b>(y,x)[2];}

    }
}

}

【讨论】:

    【解决方案2】:

    如果您知道背景是蓝色的,则将图像转换为黑白会丢失有价值的信息。

    如果此人没有穿蓝色(至少不是非常接近背景颜色的),则不必使用轮廓。只需将蓝色像素替换为其他图像中的像素即可。您可以使用 cvScalar 数据类型和 cvGet2D 和 cvSet2D 函数来实现这一点。

    编辑: 您的代码看起来比您所说的原始问题复杂得多。拥有蓝色背景(也称为“蓝屏”和“色度键”)是电视频道用来更改新闻阅读器背景的常用方法。选择蓝色的原因是人体皮肤在蓝色成分中的优势较小。

    假设这个人没有穿蓝色衣服,下面的代码应该可以工作。如果您需要不同的东西,请告诉我。

    //Read the image with person
    IplImage* imgPerson = cvLoadImage("person.jpg");
    //Read the image with background
    IplImage* imgBackground = cvLoadImage("paris.jpg");
    
    // assume that the blue background is quite even
    // here is a possible range of pixel values
    // note that I did not use all of them :-)
    unsigned char backgroundRedMin = 0;
    unsigned char backgroundRedMax = 10;
    unsigned char backgroundGreenMin = 0;
    unsigned char backgroundGreenMax = 10;
    unsigned char backgroundBlueMin = 245;
    unsigned char backgroundBlueMax = 255;
    
    // for simplicity, I assume that both images are of the same resolution
    // run a loop to replace pixels
    for (int i=0; i<imgPerson->width; i++) 
    {
        for (int j=0; j< imgPerson->height; j++) 
        {
            CvScalar currentPixel = cvGet2D(imgPerson, j, i);
            // compare the RGB values of the pixel, with the range
            if (curEdgePixel.val[0] > backgroundBlueMin && curEdgePixel.val[1] < 
                backgroundGreenMax && curEdgePixel.val[2] < backgroundRedMax)
                {
                 // copy the corresponding pixel from background
                 CvScalar currentBackgroundPixel = cvGet2D(imgBackground, j, i);
                 cvSet2D(imgPerson, j, i, currentBackgroundPixel);
                }
            }
        }
    
    
    imshow("Image Result", imgPerson);
    
    waitKey();
    
    return 0;
    

    【讨论】:

    • 谢谢你的帮助,我已经更新了我的源代码,你能给我一些cmets吗?
    • 完成。如果我没有回答您的问题,请告诉我。
    • 如果您的背景不是蓝色或不均匀,GrabCut 是最佳选择。
    • 亲爱的龙猫。谢谢你的帮助。你对我真好。可能是我让它变得更复杂了。蓝色背景是我需要关注的关键。我会尝试抓取并使用其他颜色的背景扩展我的工作。
    猜你喜欢
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2016-02-27
    • 1970-01-01
    • 2016-06-08
    • 2018-08-20
    相关资源
    最近更新 更多