1

I am trying to extract small objects from this image. I have applied Watershed algorithm for segmentation. How do I separate objects based on their color? I am trying to follow this guide but I am getting stuck with an exception error.

Image that needs extraction:

enter image description here

Code screenshot:

enter image description here

HansHirse
  • 18,010
  • 10
  • 38
  • 67
mohit guru
  • 35
  • 7
  • 1
    https://www.pyimagesearch.com/2014/08/04/opencv-python-color-detection/ – mohit guru Jan 10 '20 at 06:04
  • 1
    You need to convert your image to hsv and just filter the colors there is a lot of example just search hsv color filter. – ikibir Jan 10 '20 at 06:22
  • There is no need of using `argparse` in your `iPython` notebook. `argparse` is a utility for parsing command line arguments. In your case since you are not running this from command line you can reduce your code to: `import cv2` `cv2.imread("path.jpg")` – ZdaR Jan 10 '20 at 06:44
  • 1
    Please do not post an image of your code. Post your code as text so others can copy and test it. Is this your input image or one after segmentation. Please provide clearer details. Best if you post your code that shows your input image being processed to do the segmentation. Then others can try to add on the needed details. Please read this forum's help section on how to ask a good questions and providing minimally complete, verifiable and reproducible code. – fmw42 Jan 10 '20 at 06:48
  • @ikibir How to find the image format if its in BGR or any other format ? – mohit guru Jan 10 '20 at 07:18
  • this might be a bit helpful https://stackoverflow.com/questions/53977777/how-can-i-only-keep-text-with-specific-color-from-image-via-opencv-and-python – Andy_101 Jan 10 '20 at 07:20
  • @mohitguru if you just read image like img = imread('my_image.jpg') then it is BGR. – ikibir Jan 10 '20 at 07:20
  • The result of [`cv2.watershed`](https://docs.opencv.org/4.2.0/d7/d1b/group__imgproc__misc.html#ga3267243e4d3f95165d55a618c65ac6e1) stores all your markers as plain numbers like `0, 1, 2, 3...`. So, just use something like `currentMarker = [markers == i]` in some loop running from 0 to total number of markers. Absolutely no need for color detection here! Colors in the picture also just come from Matplotlib's colorbar, I'd assume. The result of `cv2.watershed` is a single channel! – HansHirse Jan 10 '20 at 07:24
  • @HansHirse I do want to extract each image of different color and store it in a file – mohit guru Jan 10 '20 at 07:33

1 Answers1

3

There's no need for real color detection here, since processing is done on the output of cv2.watershed. That's an "image" markers of type np.int32 with values 0, 1, 2, 3, ... for the single markers detected by cv2.watershed, whereas 0 is the background. So, there's nothing more to do than iterating all marker values (let's say i), mask the portion where markers == i, and find the corresponding coordinates of the bounding rectangle, copy that part to a new image and save this to some file.

Here's some code, where the cv2.watershed was mimicked by using cv2.findContours and cv2.drawContours accordingly (the interesting part regarding the question is solely the second for loop):

import cv2
import numpy as np
from matplotlib import pyplot as plt
from skimage import io          # Only needed for web grabbing images

# Load some image with circles from web
image = io.imread('https://www.teachertoolsinc.com/images/detailed/26/TCR77379.png')
plt.figure(1), plt.imshow(image), plt.title('original image'), plt.tight_layout()

# Mimic watershed result using findContours and drawContours
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
gray = cv2.threshold(gray, 16, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
markers = np.zeros_like(gray).astype(np.int32)
for i, cnt in enumerate(cnts):
    markers = cv2.drawContours(markers, [cnt], -1, i+1, cv2.FILLED)
plt.figure(2), plt.imshow(markers), plt.title('markers'), plt.colorbar(), plt.tight_layout()
plt.show()

# Assuming we only have markers now; iterate all values and crop image part
for i in np.arange(1, np.max(markers[:, :])+1):
    pixels = np.array(np.where(markers == i)).astype(np.int32)
    x1 = np.min(pixels[1, :])
    x2 = np.max(pixels[1, :])
    y1 = np.min(pixels[0, :])
    y2 = np.max(pixels[0, :])
    cv2.imwrite(str(i) + '.png', image[y1:y2, x1:x2, :])

That's the input image:

Input

That's the mimicked markers "image" (cf. to provided image in question):

Markers

And, here are two of the cut circles:

Example 1

Example 2

Hope that helps!

-----------------------
System information
-----------------------
Python:      3.8.1
Matplotlib:  3.2.0rc1
NumPy:       1.18.1
OpenCV:      4.1.2
-----------------------
HansHirse
  • 18,010
  • 10
  • 38
  • 67