8

Here is an example of the kinds of images I'll be dealing with:

Balls
(source: csverma at pages.cs.wisc.edu)

There is one bright spot on each ball. I want to locate the coordinates of the centre of the bright spot. How can I do it in Python or Matlab? The problem I'm having right now is that more than one points on the spot has the same (or roughly the same) white colour, but what I need is to find the centre of this 'cluster' of white points.

Also, for the leftmost and rightmost images, how can I find the centre of the whole circular object?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Physicist
  • 2,848
  • 8
  • 33
  • 62
  • Convolve with a 2D circular mask (for example Gaussian) and find the mask coordinates that give maximum correlation? – Luis Mendo May 06 '15 at 16:18
  • 2
    Why not simply take the average coordinates of the pixels that are in the 95-percentile of the picture's luminosity? This won't work when there are multiple bright spots, but for a single spot this should give you the simplest solution. – knedlsepp May 06 '15 at 17:09
  • You can also convert to grayscale, threshold and find the average coordinates of the thresholded result. No need to filter. – rayryeng May 06 '15 at 18:42
  • can try this one: http://www.mathworks.com/matlabcentral/fileexchange/37388-fast-2d-peak-finder – bla May 06 '15 at 20:47
  • @bla - Oh yes. Beautiful code. – rayryeng May 06 '15 at 22:19
  • @AGS - Can you explain why this post was closed due to "recommending" a tool or software library? The question nor answer reflects this. I find the question to be interesting, and not only did I answer the question adequately, but there is a well defined solution and provided in **both** languages requested by the OP. – rayryeng May 14 '15 at 22:24
  • @JoeKennedy - Can you explain why this post was closed due to "recommending" a tool or software library? The question nor answer reflects this. I find the question to be interesting, and not only did I answer the question adequately, but there is a well defined solution and provided in **both** languages requested by the OP. – rayryeng May 14 '15 at 22:24
  • @ShankarDamodaran - Can you explain why this post was closed due to "recommending" a tool or software library? The question nor answer reflects this. I find the question to be interesting, and not only did I answer the question adequately, but there is a well defined solution and provided in **both** languages requested by the OP. – rayryeng May 14 '15 at 22:24
  • @BenC - Can you explain why this post was closed due to "recommending" a tool or software library? The question nor answer reflects this. I find the question to be interesting, and not only did I answer the question adequately, but there is a well defined solution and provided in **both** languages requested by the OP. – rayryeng May 14 '15 at 22:24
  • @SonerGönül - Can you explain why this post was closed due to "recommending" a tool or software library? The question nor answer reflects this. I find the question to be interesting, and not only did I answer the question adequately, but there is a well defined solution and provided in **both** languages requested by the OP. – rayryeng May 14 '15 at 22:24
  • While i suppose this question seems valid because you're not requesting a tool or off site resource per sé, there isn't any code example that indicates a problem either. not entirely sure what to do here... – Timothy Groote Jun 03 '15 at 09:05

2 Answers2

9

You can simply threshold the image and find the average coordinates of what is remaining. This handles the case when there are multiple values that have the same intensity. When you threshold the image, there will obviously be more than one bright white pixel, so if you want to bring it all together, find the centroid or the average coordinates to determine the centre of all of these white bright pixels. There isn't a need to filter in this particular case. Here's something to go with in MATLAB.

I've read in that image directly, converted to grayscale and cleared off the white border that surrounds each of the images. Next, I split up the image into 5 chunks, threshold the image, find the average coordinates that remain and place a dot on where each centre would be:

im = imread('http://pages.cs.wisc.edu/~csverma/CS766_09/Stereo/callight.jpg');
im = rgb2gray(im);
im = imclearborder(im);

%// Split up images and place into individual cells
split_point = floor(size(im,2) / 5);
images = mat2cell(im, size(im,1), split_point*ones(5,1));

%// Show image to place dots
imshow(im);
hold on;

%// For each image...
for idx = 1 : 5
    %// Get image
    img = images{idx}; 

    %// Threshold
    thresh = img > 200;

    %// Find coordinates of thresholded image
    [y,x] = find(thresh);

    %// Find average
    xmean = mean(x);
    ymean = mean(y);

    %// Place dot at centre
    %// Make sure you offset by the right number of columns
    plot(xmean + (idx-1)*split_point, ymean, 'r.', 'MarkerSize', 18);
end        

I get this:

enter image description here


If you want a Python solution, I recommend using scikit-image combined with numpy and matplotlib for plotting. Here's the above code transcribed in Python. Note that I saved the image referenced by the link manually on disk and named it balls.jpg:

import skimage.io
import skimage.segmentation
import numpy as np
import matplotlib.pyplot as plt

# Read in the image
# Note - intensities are floating point from [0,1]
im = skimage.io.imread('balls.jpg', True)

# Threshold the image first then clear the border
im_clear = skimage.segmentation.clear_border(im > (200.0/255.0))

# Determine where to split up the image
split_point = int(im.shape[1]/5)

# Show image in figure and hold to place dots in
plt.figure()
plt.imshow(np.dstack([im,im,im]))

# For each image...
for idx in range(5):

  # Extract sub image
  img = im_clear[:,idx*split_point:(idx+1)*split_point]

  # Find coordinates of thresholded image
  y,x = np.nonzero(img)

  # Find average
  xmean = x.mean()
  ymean = y.mean()

  # Plot on figure
  plt.plot(xmean + idx*split_point, ymean, 'r.', markersize=14)

# Show image and make sure axis is removed
plt.axis('off')
plt.show()

We get this figure:

enter image description here

Small sidenote

I could have totally skipped the above code and used regionprops (MATLAB link, scikit-image link). You could simply threshold the image, then apply regionprops to find the centroids of each cluster of white pixels, but I figured I'd show you a more manual way so you can appreciate the algorithm and understand it for yourself.


Hope this helps!

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Good complete answer. Since finding the right threshold value (here 200/255) may be tricky, I suggest applying a non-linear concave function (such as `exp` as I explained in my answer, or a _gamma correction_ operation ) is important because it intensifies the brightest points even more. It makes the detection more robust. Also finding the right threshold in an automatic way in any case can be done using a histogram; such as by finding the value of the 1% (in terms of percentile) brightest pixel among all pixels. – Sohail Si May 08 '15 at 13:53
  • @SohailSi Something like Otsu could work that evaluates the histogram as you suggested. Applying a non linear filter is also a good idea but I'll leave that to the OP. I think I've done enough for a post! – rayryeng May 08 '15 at 16:24
1

Use a 2D convolution and then find the point with the highest intensity. You can apply a concave non-linear function (such as exp) on intensity values before applying the 2d convolution, to intensify the bright spots relative to the dimmer parts of the image. Something like conv2(exp(img),ker)

Sohail Si
  • 2,750
  • 2
  • 22
  • 36