3

I'm trying to help a colleague that uses a very old colorimetric technique to measure cellular death. To simplify the problem here is a schematic image:

enter image description here

This is known as a 96-well plate. I need to find all the wells and return the RGB value for each one. Pink means all cells are alive and blue means no cell is alive. They have a formula for this calculation. Now I have been working with this image and so far I can detect all wells with this code:

import cv2 
import numpy as np 

# Read image. 
img = cv2.imread('images/placaTeoricaCompleta_result.jpg') 

# Convert to grayscale. 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

# Blur using 3 * 3 kernel. 
gray_blurred = cv2.blur(gray, (3, 3))


# Apply Hough transform on the blurred image. 
detected_circles = cv2.HoughCircles(gray_blurred, 
                cv2.HOUGH_GRADIENT, 1.2, 20, param1 = 50, 
            param2 = 30, minRadius = 30, maxRadius = 50) 

# Draw circles that are detected. 
if detected_circles is not None: 

    # Convert the circle parameters a, b and r to integers. 
    detected_circles = np.uint16(np.around(detected_circles))

    for pt in detected_circles[0, :]: 
        a, b, r = pt[0], pt[1], pt[2] 

        # Draw the circumference of the circle. 
        cv2.circle(img, (a, b), r, (0, 255, 0), 2) 

        # Draw a small circle (of radius 1) to show the center. 
        cv2.circle(img, (a, b), 1, (0, 0, 255), 3) 
    
    cv2.imshow("Detected Circle", img) 
    cv2.waitKey(0) 

But I can't find a way to return the RGB value for each well.

A real image would look something like this:

enter image description here

How do I return the RGB value for each circle? This would preferably be in order from A to H and from 1 to 12, or otherwise write the RGB value in the circle.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
alexzaizar09
  • 490
  • 1
  • 4
  • 18
  • You have the centroid for each circle, you can average the RGB values at and nearby the centroid to get a good measurement of the color. – Cris Luengo Jul 17 '20 at 00:01
  • I would recommend a different color-space because the R, G, and B components of an object's color in a digital image are all correlated with the amount of light hitting the object, and therefore with each other, image descriptions in terms of those components make object discrimination difficult. Descriptions in terms of hue/lightness/chroma or hue/lightness/saturation are often more relevant. This is when you want to detect colors in digital images like the second image you posted – Rick M. Jul 17 '20 at 07:50
  • Have you looked at [How to find the average colour of an image in Python with OpenCV?](https://stackoverflow.com/questions/43111029/how-to-find-the-average-colour-of-an-image-in-python-with-opencv/58177484) – coffeewin Jul 17 '20 at 21:08

1 Answers1

3

You can generate a mask for each circle, then obtain the mean of the color channels. The following code is just for one circle, but you can just put it in a for loop:

x, y, r = detected_circles[0].astype(np.int32)
roi = image[y - r: y + r, x - r: x + r]

region of interest

# generate mask
width, height = roi.shape[:2]
mask = np.zeros((width, height, 3), roi.dtype)
cv2.circle(mask, (int(width / 2), int(height / 2)), r, (255, 255, 255), -1)

mask

dst = cv2.bitwise_and(roi, mask)

masked image

# filter black color and fetch color values
data = []
for i in range(3):
    channel = dst[:, :, i]
    indices = np.where(channel != 0)[0]
    color = np.mean(channel[indices])
    data.append(int(color))

# opencv images are in bgr format
blue, green, red = data # (110, 74, 49)

final image

The text in the image is in rgb format.

Sebastian Liendo
  • 781
  • 4
  • 12