Вычесть синий фон из изображения с помощью OpenCV С++

Я новичок в OpenCV и C++, но теперь мне нужно найти решение этой проблемы: у меня есть изображение человека с синим фоном, теперь мне нужно вычесть фон из изображения, а затем заменить его другим изображением. Теперь я думаю, что есть 2 способа решить эту проблему, но я не знаю, какой лучше:

Решение 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;

person user2411800    schedule 28.05.2013    source источник
comment
Попробуйте сегментацию. Используйте cv::grabCut с маской переднего плана очень близко к границам (например, установите 4 угловых пикселя изображения в качестве переднего плана).   -  person LovaBill    schedule 28.05.2013
comment
Дорогой Уильям. Можете ли вы описать более подробно? Как я могу использовать GrabCut в этой ситуации? Спасибо   -  person user2411800    schedule 28.05.2013
comment
Прочтите это и попробуйте это.   -  person LovaBill    schedule 28.05.2013


Ответы (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;
person Totoro    schedule 28.05.2013
comment
Спасибо за вашу помощь, я уже обновил свой исходный код, можете ли вы дать мне несколько комментариев? - person user2411800; 28.05.2013
comment
Сделанный. Дайте мне знать, если я не ответил на ваш вопрос. - person Totoro; 29.05.2013
comment
Если ваш фон не синий и не неровный, лучшим выбором будет GrabCut. - person Totoro; 30.05.2013
comment
Дорогой Тоторо. Спасибо за вашу помощь. Ты так добр со мной. Может быть, я сделал это более сложным. Синий фон — это ключ, на котором мне нужно сосредоточиться. Я попробую вырезать и расширить свою работу с другим цветом фона. - person user2411800; 31.05.2013

Проверить этот проект

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

    }
}

}

person Mobin    schedule 23.10.2014