Python ImageFilter for fun, profit and spinning candy

Having product images increases sales, but post-processing is a chore which involves clicking around in Photoshop/Gimp to isolate the product and changing levels such that the background becomes pure white. As I've been selling Japanese candy online, taking shots like these has become a repeating task.

You could pay someone to do that, but wouldn't it be nice if it could happen automatically? Today I challenged myself to see if I could manage it. After a few hours of hacking it works quite well, actually the above image is output from the script.

One side effect is that because touching up the product picture is now free, you could even do it hundreds of times just for fun. For example for no particular reason, you could take a hundred pictures at different angles and then isolate the product on each frame to get a nice animation. Not that anyone would be crazy enough to do such a thing.

Tech used is just Python with the Image and ImageFilter modules. To put together the gif animation I used ffmpeg. How to do the isolating part then? The solution actually turns out to be very straightforward, just finding adjacent pixels based on color. Read on if you care for the details.

Step 1:

Convert the image to black and white such that the background becomes solid color that surrounds the object. Now the thing we want to isolate is the blob in the middle. If we had an outline for that, then that can be used to pick out the object from the original photo.

Step 2:

Find one pixel which is known to belong to the object that we want to isolate. I pick the first darkish pixel I can find near the center of the image. Then starting from that pixel, recursively explore neighboring pixels. If you get unlucky the algorithm will fail here if it happens to start from an unconnected island, so in reality I had to use multiple start points. Below is what you get after counting all neighboring pixels to be part of the object. Note we have now isolated it from the background, but there's still gaps inside.

Step 3:

To cover the gaps, I thought the simplest thing would be just to expand the selection. I found in Python there is ImageFilter.MinFilter, which works by including any pixels in the mask that have nearby pixels which are also in the mask. Done repeatedly gets the desired result reasonably fast. Now we have a pretty good mask of the product, with some minor gaps which tend to be shiny areas anyway so they are not very noticeable in the end result.
Step 4:

Use the mask to cut out the product from the original image.
Final step:

Adjust levels so the edge becomes white. Image.convert is useful here. Done!