Make the background of the image transparent using a mask
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.
somehow, your code does not match the images
it does not use the mask here (which should be the 4th item in the rgba array to merge)
please explain, why you are using RETR_TREE for the contours, and the purpose of that weird sorting
Thanks for the answer. I can not write like this:
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.
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.
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?
your mask should be CV_8U too (like the B G R channels are)
RETR_EXTERNAL would only retrieve outer contours (no idea, whtich way you want it)
How to mark your answer as a solution to the problem?