通过OpenCV C ++从图像中减去蓝色背景

时间:2013-05-28 04:25:41

标签: opencv background subtraction

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

解决方案1 ​​

  1. 将图片转换为B& W
  2. 将其用作蒙版以减去背景。
  3. 解决方案2

    1. 使用coutour查找背景,
    2. 然后减去它。
    3. 我已经实现了解决方案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;
      

2 个答案:

答案 0 :(得分:0)

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

如果此人没有穿蓝色(至少不是非常接近背景颜色的人),则不必使用轮廓。只需用其他图像中的像素替换蓝色像素即可。您可以使用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;

答案 1 :(得分:0)

检查此项目

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];}

    }
}

}