1

私は OpenCV と C++ の初心者ですが、この問題の解決策を見つける必要があります。青い背景の人の画像があり、画像から背景を差し引いて別の画像に置き換える必要があります。この問題を解決するには 2 つの方法があると思いますが、どちらが優れているかはわかりません。

解決策 1 :

  1. 画像を白黒に変換
  2. 背景を差し引くためのマスクとして使用します。

解決策 2 :

  1. クトゥールを使って背景を見つけ、
  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;
4

2 に答える 2

0

背景が青色であることがわかっている場合は、画像を白黒に変換することで貴重な情報を失っています。

人が青を着ていない場合 (少なくとも背景色に非常に近い青ではない場合)、輪郭線を使用する必要はありません。青いピクセルを他の画像のピクセルに置き換えるだけです。これを実現するために、cvGet2D および cvSet2D 関数で cvScalar データ型を使用できます。

編集: あなたのコードは、あなたが述べた元の問題よりもはるかに複雑に見えます。青色の背景 (「ブルー スクリーン」や「クロマ キー」とも呼ばれる) は、テレビ チャンネルでニュース リーダーの背景を変更するために使用される一般的な方法です。青を選択した理由は、人間の皮膚は青成分の優位性が低いためです。

その人が青い服を着ていないと仮定すると、次のコードが機能するはずです。別のものが必要な場合はお知らせください。

//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;
于 2013-05-28T04:36:52.313 に答える