1

I am trying to analyze the red wells with different intensities from the attached image.
I want to analyze how the intensity of each red colored well differs from the others.
Does anyone have a solution without using the following code:

import numpy as np
import argparse
import cv2

# Construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help="path to the image")
args = vars(ap.parse_args())

# Load the image
image = cv2.imread(args["image"])

# Define the list of boundaries
boundaries = [
    ([17, 15, 100], [50, 56, 200]),
    ([86, 31, 4], [220, 88, 50]),
    ([25, 146, 190], [62, 174, 250]),
    ([103, 86, 65], [145, 133, 128])
    ]

# Loop over the boundaries
for (lower, upper) in boundaries:
    # Create NumPy arrays from the boundaries
    lower = np.array(lower, dtype="uint8")
    upper = np.array(upper, dtype="uint8")
    
    # Find the colors within the specified boundaries and apply the mask
    mask = cv2.inRange(image, lower, upper)
    output = cv2.bitwise_and(image, image, mask=mask)

Image I'm trying to process:
enter image description here

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Andale
  • 11
  • 2

1 Answers1

2

Assume we want to automatically find the red wells.
Assume we know that the wells are circular, and that the color is red (reddish).

We also know that the wells are not tiny, but we don't want to overfit the solution too much...

We may use the following stages:

  • Use cv2.bilateralFilter for reducing noise (while keeping edges fairly sharp).
  • Find red pixels as described in the following post.
    Convert from BGR to HSV, and find the pixels in range that is considered red.
    According to the post there are "lower_red" and "upper_red" ranges (we are keeping the ranges without tuning).
  • Find contours.
    Iterate the contours.
    Skip small contours (assumed to be noise).
  • Use cv2.minEnclosingCircle as described here.
    Draw a filled circle for each contour, to form a mask, and use cv2.bitwise_and as used in your question.

Code sample:

import numpy as np
import argparse
import cv2

# Construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help="path to the image")
args = vars(ap.parse_args())

# Load the image
image = cv2.imread(args["image"])

img = cv2.bilateralFilter(image, 11, 75, 75)

# https://stackoverflow.com/questions/30331944/finding-red-color-in-image-using-python-opencv
# Convert from BGR to HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Lower mask (0-10)
lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])
mask0 = cv2.inRange(img_hsv, lower_red, upper_red)

# Upper mask (170-180)
lower_red = np.array([170, 50, 50])
upper_red = np.array([180, 255, 255])
mask1 = cv2.inRange(img_hsv, lower_red, upper_red)

# Join the masks
raw_mask = mask0 | mask1

ctns = cv2.findContours(raw_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]  # Find contours

mask = np.zeros_like(raw_mask)  # Fill mask with zeros

idx = 0
# Iterate contours
for c in ctns:
    area = cv2.contourArea(c)  # Find the area of each contours

    if (area > 50):  # Ignore small contours (assume noise).
        cv2.drawContours(mask, [c], 0, 255, -1)

        # https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html
        (x, y), radius = cv2.minEnclosingCircle(c)
        center = (int(x), int(y))
        radius = int(radius)
        cv2.circle(mask, center, radius, 255, -1)

        tmp_mask = np.zeros_like(mask)
        cv2.circle(tmp_mask, center, radius, 255, -1)
        output = cv2.bitwise_and(image, image, mask=tmp_mask)
        cv2.imshow(f'output{idx}', output)  # Show output images for testing
        cv2.imwrite(f'output{idx}.png', output)  # Save output images for testing
        idx += 1

cv2.imshow('image', image)
cv2.imshow('img', img)
cv2.imshow('raw_mask', raw_mask)
cv2.imshow('mask', mask)
cv2.waitKey()
cv2.destroyAllWindows()

Results:

Image after bilateral filter:
enter image description here

raw_mask:
enter image description here

mask:
enter image description here

output0.png:
enter image description here

output1.png:
enter image description here

output2.png:
enter image description here

output3.png:
enter image description here

output4.png:
enter image description here


In case the circles are too large, we can reduce the radius, and get only the center of each well.

Rotem
  • 30,366
  • 4
  • 32
  • 65