Make the background of the image transparent using a mask

asked 2020-07-14 04:47:44 -0500

Greetings. How can I make the background of the image transparent using a mask, not a threshold. The essence of the program is as follows: I get the image and try to cut the object from the background. I have already implemented this. Here I found an implementation with threshold: stackoverflow). Here is what I have:

struct comparator{
bool operator() (std::tuple<std::vector<cv::Point>, bool, double> t1,
                 std::tuple<std::vector<cv::Point>, bool, double> t2) {
    return std::get<2>(t1) > std::get<2>(t2);
}

} comparator;

cv::Mat image = cv::imread("C:\\Users\\Sky\\Downloads\\12.png");
cv::Mat grayImg;

// convert to greyscale
cv::cvtColor(image, grayImg, COLOR_BGRA2GRAY);

// finding threshes
cv::Mat thresh;
cv::threshold(grayImg,thresh, 127, 255, THRESH_BINARY_INV | THRESH_OTSU);


// finding contours
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;

findContours( thresh, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );


// finding max contour
std::vector<std::tuple<std::vector<cv::Point>, bool, double>> vec;


for(size_t i = 0; i < contours.size(); ++i){

    vec.push_back(std::make_tuple(contours.at(i), cv::isContourConvex(contours.at(i)),cv::contourArea(contours.at(i))));
}

std::sort(vec.begin(), vec.end(), comparator);

std::tuple<std::vector<cv::Point>, bool, double> maxContour;
maxContour = vec.at(0);



// create mask
cv::Mat mask = Mat::zeros(thresh.size(), CV_8S);

for(size_t i = 0; i < contours.size(); ++i){
    cv::fillConvexPoly(mask, std::get<0>(vec.at(i)), Scalar(255,0,0),8,0);
}


// bitwise
cv::Mat res;
cv::bitwise_and(image, image, res, mask);

// show process
imshow("result", res);
imshow("mask", mask);
imshow("canny", thresh);
imshow("source", image);


// create transparent background
Mat dst;

Mat rgb[3];
split(res,rgb);

Mat rgba[4]={rgb[0],rgb[1],rgb[2]};
merge(rgba,4,dst);


// save to file transparent and cropped images
imwrite("C:/Documents/1.png", res);
imwrite("C:/Documents/dst.png",dst);


while (true) {
    if (waitKey() == 27) { //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
        std::cout << "esc key is pressed by user";
        break;
    }
}

return 0;

As we can see, most of the image is lost if threshold is used.

1)Source. 2) Mask. 3) Thresholds. 4) Result without a transparent background. 5) Result with a mask. As you can see, the result with a transparent background has lost color. I would be grateful for any information.

image description

image description

image description

image description

image description

edit retag flag offensive close merge delete

Comments

somehow, your code does not match the images

Mat rgba[4]={rgb[0],rgb[1],rgb[2]};
merge(rgba,4,dst);

it does not use the mask here (which should be the 4th item in the rgba array to merge)

berak gravatar imageberak ( 2020-07-14 05:08:40 -0500 )edit

please explain, why you are using RETR_TREE for the contours, and the purpose of that weird sorting

berak gravatar imageberak ( 2020-07-14 05:11:39 -0500 )edit

Thanks for the answer. I can not write like this:

Mat rgba[4]={rgb[0],rgb[1],rgb[2], mask};
merge(rgba,4,dst);

it breaks with an error: OpenCV(4.3.0) Error: Assertion failed (mv[i].size == mv[0].size && mv[i].depth() == depth) in merge, file C:\OpenCV\opencv\sources\modules\core\src\merge.dispatch.cpp, line 129

I use RETR_THREE because the following was written in the documentation. And this is the final guy, Mr.Perfect. It retrieves all the contours and creates a full family hierarchy list. It even tells, who is the grandpa, father, son, grandson and even beyond... :). This flag worked fine, so I used it. Sorting was used by me to find the maximum contour. In the future, I did not use it, because I decided to use all the contours for drawing a mask.

Painter123 gravatar imagePainter123 ( 2020-07-14 05:39:49 -0500 )edit

Unfortunately, I did not have enough time to study computer vision, since the problem appeared by itself and it is urgent to solve it. I apologize for this.

Painter123 gravatar imagePainter123 ( 2020-07-14 05:42:06 -0500 )edit

I think the RETR_THREE flag is associated with a Hierarchy. I do not use Hierarchy in the application, is it worth it to worry about?

Painter123 gravatar imagePainter123 ( 2020-07-14 05:51:32 -0500 )edit

Assertion failed (mv[i].size == mv[0].size && mv[i].depth() == depth)

your mask should be CV_8U too (like the B G R channels are)

cv::Mat mask = Mat::zeros(thresh.size(), CV_8U);

RETR_TREE

RETR_EXTERNAL would only retrieve outer contours (no idea, whtich way you want it)

berak gravatar imageberak ( 2020-07-14 08:01:35 -0500 )edit

Thank you very much, I worked on this for a very long time and only now I saw that I had a signed mask. Today, before dinner, I will pray for you :)

Painter123 gravatar imagePainter123 ( 2020-07-14 08:06:15 -0500 )edit

How to mark your answer as a solution to the problem?

Painter123 gravatar imagePainter123 ( 2020-07-14 08:07:20 -0500 )edit