Process image to find external contour

后端 未结 3 632
闹比i
闹比i 2020-12-18 07:50

I have hundreds of PNG images for which I have to generate corresponding B&W images that show the outer outline of the object. The source PNG image has alpha channel, so

3条回答
  •  时光说笑
    2020-12-18 08:35

    I agree with amgaera. OpenCV in Python is one of the best tools you can use if you want to find contours. As with his/her post, use the findContours method and use the RETR_EXTERNAL flag to get the outer most contour of the shape. Here's some reproducible code for you to illustrate this point. You first need to install OpenCV and NumPy to get this going.

    I'm not sure what platform you're using, but:

    • If you're using Linux, simply do an apt-get on libopencv-dev and python-numpy (i.e. sudo apt-get install libopencv-dev python-numpy).
    • If you're using Mac OS, install Homebrew, then install via brew install opencv then brew install numpy.
    • If you're using Windows, the best way to get this to work is through Christoph Gohlke's unofficial Python packages for Windows: http://www.lfd.uci.edu/~gohlke/pythonlibs/ - Check the OpenCV package and install all of the dependencies it is asking for, including NumPy which you can find on this page.

    In any case, I took your donut image, and I extracted just the image with the donut. In other words, I created this image:

    enter image description here

    As for your images being PNG and having an alpha channel, that actually doesn't matter. So long as you have only a single object contained in this image, we actually don't need tho access the alpha channel at all. Once you download this image, save it as donut.png, then go ahead and run this code:

    import cv2 # Import OpenCV
    import numpy as np # Import NumPy
    
    # Read in the image as grayscale - Note the 0 flag
    im = cv2.imread('donut.png', 0)
    
    # Run findContours - Note the RETR_EXTERNAL flag
    # Also, we want to find the best contour possible with CHAIN_APPROX_NONE
    contours, hierarchy = cv2.findContours(im.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    
    # Create an output of all zeroes that has the same shape as the input
    # image
    out = np.zeros_like(im)
    
    # On this output, draw all of the contours that we have detected
    # in white, and set the thickness to be 3 pixels
    cv2.drawContours(out, contours, -1, 255, 3)
    
    # Spawn new windows that shows us the donut
    # (in grayscale) and the detected contour
    cv2.imshow('Donut', im) 
    cv2.imshow('Output Contour', out)
    
    # Wait indefinitely until you push a key.  Once you do, close the windows
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    Let's go through the code slowly. First we import the OpenCV and NumPy packages. I imported NumPy as np, and if you look at numpy docs and tutorials everywhere, they do this to minimize typing. OpenCV and NumPy work with each other, which is why you need to install both packages. We then read in the image using imread. I set the flag to be 0 to make the image grayscale to make things simple. Once I load in the image, I then run findContours, and the output of this function outputs a tuple of two things:

    • contours - This is an array structure that gives you the (x,y) co-ordinates of each contour detected in your image.
    • hierarchy - This contains additional information about the contours you've detected, like the topology, but let's skip this for the sake of this post.

    Take note that I specified RETR_EXTERNAL to detect the outer most contour of the object. I also specify the CHAIN_APPROX_NONE flag to ensure we get the full contour without any approximations. Once we detect the contours, we create a new output image that is entirely black. This will contain our detected outer contour of the donut. Once we create this image, we run the drawContours method. You specify the image you want to show the contours in, the contours structure that was created from earlier, and the -1 flag says to draw all of the contours in the image. If it all works out, you should only have one contour detected. You then specify what colour you want the contour to look like. In our case, we want this to be white. After, you specify how thick you want the contour to be drawn. I chose a thickness of 3 pixels.

    Last thing we want to do is show what the results look like. I call imshow to show what the original donut image looks like (grayscale) and what the output contour looks like. imshow isn't the end of the story. You won't see any output until you invoke cv2.waitKey(0). What this is saying now is you can display the images indefinitely until you push a key. Once you press on a key, the cv2.destroyAllWindows() call closes all of the windows that were spawned.

    This is what I get (once you rearrange the windows so that they're side-by-side):

    enter image description here


    As an additional bonus, if you want to save the image, you just run imwrite to save the image. You specify the name of the image you want to write, and the variable you are accessing. As such, you would do something like:

    cv2.imwrite('contour.png', out)
    

    You'll then save this contour image to file which is named contour.png.


    This should be enough to get you started.

    Good luck!

提交回复
热议问题