Filling a single colored background in OpenCV

I am writing a program in OpenCV that takes an image of a parasite egg and tries to identify at least most of it. My problem is that the original image that I get with the best results has a large background. I tried to both fill the background and crop it, but when I do, I get the worst choice of eggs.

My currently thought out solution is to use an image with a background and then fill it out. It seemed like it would be easy, because I just want to fill something outside this circle with black, but I'm not sure how to actually complete the action. If someone can point out a way to use it or any suggestions that would be good.

Here is a link to what the image looks like:

enter image description here

Thanks!

+1
source share
2 answers

It seems that you need the appearance of the image to be filled with black, because this makes it easier to identify the eggs, as they will be isolated in white.

But what if parasitic eggs magically looked blue? I will explain this in a second, but this approach will free you from the burden of clicking on the image every time you need a new sample for analysis.

I wrote an answer in C ++, but if you follow what the code does, I'm sure you can quickly translate it into Python.

#include <iostream> #include <vector> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> int main(int argc, char* argv[]) { // Load input image (3-channel) cv::Mat input = cv::imread(argv[1]); if (input.empty()) { std::cout << "!!! failed imread()" << std::endl; return -1; } // Convert the input to grayscale (1-channel) cv::Mat grayscale = input.clone(); cv::cvtColor(input, grayscale, cv::COLOR_BGR2GRAY); 

What shades of gray are as follows:

iXTjU.png

  // Locate the black circular shape in the grayscale image std::vector<std::vector<cv::Point> > contours; cv::findContours(grayscale, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); // Fill the interior of the largest circular shape found with BLUE cv::Mat circular_shape = input.clone(); for (size_t i = 0; i < contours.size(); i++) { std::vector<cv::Point> cnt = contours[i]; double area = cv::contourArea(cv::Mat(cnt)); if (area > 500000 && area < 1000000) // magic numbers to detect the right circular shape { std::cout << "* Area: " << area << std::endl; cv::drawContours(circular_shape, contours, i, cv::Scalar(255, 0, 0), cv::FILLED, 8, std::vector<cv::Vec4i>(), 0, cv::Point() ); } } 

What circle_shape looks like :

24kNw.png

  // Create the output image with the same attributes of the original, ie dimensions & 3-channel, so we have a colored result at the end cv::Mat output = cv::Mat::zeros(input.size(), input.type()); // copyTo() uses circular_shape as a mask and copies that exact portion of the input to the output input.copyTo(output, circular_shape); cv::namedWindow("Eggs", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); cv::imshow("Eggs", output); cv::resizeWindow("Eggs", 800, 600); cv::waitKey(0); return 0; } 

output displayed in the window:

8a6MW.png

The advantage of this solution is that the user does not need to interact with the application to facilitate the detection of eggs, as they are already painted in blue.

After that, other operations can be performed using output , for example cv::inRange() to isolate colored objects from the rest of the image.

So, for the sake of completion, I will add a few more lines of text / code to demonstrate what you could do from now on to completely isolate the eggs from the rest of the image:

 // Isolate blue pixels on the output image cv::Mat blue_pixels_only; cv::inRange(output, cv::Scalar(255, 0, 0), cv::Scalar(255, 0, 0), blue_pixels_only); 

At this point, blue_pixels_only looks like :

tc0t8.png

 // Get rid of pixels on the edges of the shape int erosion_type = cv::MORPH_RECT; // MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE int erosion_size = 3; cv::Mat element = cv::getStructuringElement(erosion_type, cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1), cv::Point(erosion_size, erosion_size)); cv::erode(blue_pixels_only, blue_pixels_only, element); cv::dilate(blue_pixels_only, blue_pixels_only, element); cv::imshow("Eggs", blue_pixels_only); cv::imwrite("blue_pixels_only.png", blue_pixels_only); 

At this point, blue_pixels_only looks like :

gV940.png

+1
source

Developed a fix for my problem, I created a mouse event callback that fills everything I click with black. Below is the code I used in the callback:

 def paint(event, x, y, flags, param): global opening if event == cv2.EVENT_LBUTTONDOWN: h, w = opening.shape[:2] mask = np.zeros((h+2, w+2), np.uint8) cv2.floodFill(opening, mask, (x,y), (0, 0, 0)) cv2.imshow("open", opening) 
+1
source

Source: https://habr.com/ru/post/976510/


All Articles