Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding number of colored shapes from picture using Python

My problem has to do with recognising colours from pictures. Doing microbiology I need to count the number of cell nuclei present on a picture taken with a microscope camera. I've used GIMP to tag the nuclei with dots of red colour. Now I'd need to make a script in python, which, given an image, would tell me how many red dots are present. There is no red in the picture except in the dots.

I've thought of a rather complicated solution which is probably not the best one: Take a picture and start iterating through pixels checking each one's colour. If that is red, check all 8 nearest pixels, recursively check each red one's neighbours again until no more neighbouring red pixels are found. Then increment nuclei count by one and mark traversed pixels so they won't be iterated through again. Then continue iteration from where it stopped. Seems kind of heavy so I thought I'd ask, maybe someone has already dealt with a similar problem more elegantly.

Regards, Sander

like image 508
Sander Avatar asked Mar 14 '11 13:03

Sander


People also ask

Is the Python library to detect shapes from image?

OpenCV is an open source library used mainly for processing images and videos to identify shapes, objects, text etc. It is mostly used with python.

How do we find items of a specific color in image processing?

To detect colors in images, the first thing you need to do is define the upper and lower limits for your pixel values. Once you have defined your upper and lower limits, you then make a call to the cv2. inRange method which returns a mask, specifying which pixels fall into your specified upper and lower range.


3 Answers

Count nuclei

The code adapted from Python Image Tutorial. Input image with nuclei from the tutorial:

nuclei

#!/usr/bin/env python
import scipy
from scipy import ndimage

# read image into numpy array
# $ wget http://pythonvision.org/media/files/images/dna.jpeg
dna = scipy.misc.imread('dna.jpeg') # gray-scale image


# smooth the image (to remove small objects); set the threshold
dnaf = ndimage.gaussian_filter(dna, 16)
T = 25 # set threshold by hand to avoid installing `mahotas` or
       # `scipy.stsci.image` dependencies that have threshold() functions

# find connected components
labeled, nr_objects = ndimage.label(dnaf > T) # `dna[:,:,0]>T` for red-dot case
print "Number of objects is %d " % nr_objects

# show labeled image
####scipy.misc.imsave('labeled_dna.png', labeled)
####scipy.misc.imshow(labeled) # black&white image
import matplotlib.pyplot as plt
plt.imsave('labeled_dna.png', labeled)
plt.imshow(labeled)

plt.show()

Output

Number of objects is 17 

labeled nuclei

like image 54
jfs Avatar answered Nov 01 '22 18:11

jfs


I would do it like this:

  • use OpenCV (python bindings),
  • take only the R component of RGB image,
  • binary threshold the R component, so that it leaves only the reddest pixels,
  • use some object/feature detection to detect the dots, fe. ExtractSURF

Comments: it will not be fastest, it will not be always accurate. But it will be fun to do - as CV is always fun - and ready in 10 lines of code. Just a loose thought.

As for the more production-ready suggestions:

  • actually I think that your idea is very good, and it can be parallelized if given some thought;
  • use blob detection in OpenCV (cvBlobsLib).

But the most elegant solution would just be to count the tagged nuclei in GIMP, as Ocaso Protal has suggested above. Accurate and fastest. Everything else will be prone to mistakes and much much slower, hence mine are just loose ideas, more fun than anything.

like image 41
Tomek Kopczuk Avatar answered Nov 01 '22 18:11

Tomek Kopczuk


A simple Numpy / Scipy solution would be something like:

import numpy, scipy
a = scipy.misc.imread("rgb.jpg") # Imports RGB to numpy array where a[0] is red, a[1] is blue, a[2] is green...
num_red = numpy.sum((a[:,:,0] == 255) * (a[:,:,1] == 0) * (a[:,:,2] == 0)) # Counts the number of pure red pixels

You could also use PIL to read the image.

EDIT: In light of comment, scipy.ndimage.measurements.label would be useful, and also returns a value num_features which gives you the count:

import numpy, scipy
from scipy import ndimage
a = scipy.misc.imread("rgb.jpg")
b = ((a[:,:,0] == 255) * (a[:,:,1] == 0) * (a[:,:,2] == 0))*1
labeled_array, num_features = scipy.ndimage.measurements.label(b.astype('Int8'))
like image 36
Benjamin Avatar answered Nov 01 '22 20:11

Benjamin