# Adjusting the pixel values underlying a morphological component when computing an “IntensityCentroid”

When calculating an "IntensityCentroid" with ComponentMeasurements, i.e. for an image $I$ and set of components $C$, calculating:

ComponentMeasurements[{C, I}, "IntensityCentroid"][[All, 2]]

Is there an easy way for me to threshold or manipulate the underlying pixel values used to compute the intensity centroid? Specifically, I would like to subtract the minimum or mean pixel value in the region defined by the morphological component from all other pixel values in that region, or Floor (i.e. set to zero) pixel values below a certain threshold. Again, I would like to do this locally in the context of each morphological component rather than globally on the image $I$. Is this possible without a lot of work?

Motivation: Say you're attempting to determine the center of some object to sub-pixel accuracy. If you have a significant amount of "local" background noise (s.t. global thresholding works poorly), a calculated "IntensityCentroid" will be biased towards the "Centroid" of the morphological component, and this will become worse with the morphological component's size (relative to the size of the object of interest). As such, it would be fantastic to be able to manipulate and do local thresholding on pixels strictly in the region defined by a particular morphological component.

Here is an example image (a 450 x 449 pixel PNG file):

• If I understand correctly, you should provide a sample image with those componentes, which coordinates you are looking for? – Kuba Jul 19 '13 at 11:48
• "in the context of each morphological component rather than globally on the image I" - there is only one component in your example image, can you explain? – C. E. Jul 19 '13 at 11:53
• @Anon I think it is the background OP has mentioned. – Kuba Jul 19 '13 at 11:56

As I understand it, you want to modify areas defined by MorphologicalComponents separately.

First some code:

findAlphaMap[components_, index_] :=
Module[{c = components, i = index, alternatives},
alternatives =
Alternatives @@ DeleteDuplicates[Flatten@components] /.
0 -> Sequence[];
Image[components /. { i -> 1, alternatives -> 0}]
]

morphologicalAlphaPartition[image_] :=
Module[{i = image, c, indexes},
c = MorphologicalComponents[image];
indexes = DeleteDuplicates[Flatten@c] /. 0 -> Sequence[];
SetAlphaChannel[image, findAlphaMap[c, #]] & /@ indexes]

Now an example. First find an image, in this case we'll be working with this:

image = ImageCrop@Graphics[{
Black, Rectangle[{-1.1, -1.1}, {1.1, 1.1}],
Red, Rectangle[{-1.1, 0.1}],
Blue, Rectangle[{-1.1, -1.1}],
Green, Rectangle[{0.1, 0.1}],
Yellow, Rectangle[{0.1, -1.1}]
}]

It has four distinct morphological components:

(components = MorphologicalComponents[image]) // Colorize

And this is how I can manipulate each component individually:

Show[ColorNegate /@ morphologicalAlphaPartition[image],
Background -> Black]

I used ColorNegate to illustrate this. You could use whatever function you want. The point is that morphologicalAlphaPartition partitions the image according to the result of MorphologicalComponents and uses the alpha channel in such a way that the partitions can be superimposed to recreate the original image, with whatever alterations you may have had done to the parts.

• This is hitting very close to what I'm looking for... how might I apply Threshold with a "tspec" level specification to the pixel values in each component? Forgive me, I'm very new to Mathematica notation. – Intemediocre Jul 19 '13 at 12:28
• ColorNegate /@ morphologicalAlphaPartition[image] says do ColorNegate on each component separately. You could do the same with Threshold like this: Threshold[#,tspec]& /@ morphologicalAlphaPartition[image]. Replace tspec with your value, look up anonymous functions if you don't recognize the # and & syntax. – C. E. Jul 19 '13 at 12:31
• I think this constitutes a great answer. One more question if you have the time - how might I make tspec the mean or minimum of a particular morphological components pixels? In other words, how can I threshold the pixel values in each morphological component according to the local mean or min pixel value? This is my primary aim for the post. – Intemediocre Jul 19 '13 at 12:35
• @Intemediocre Try Threshold[#,FindThreshold[#,Method->"Mean"]]& - Look up FindThreshold in the documentation and read the section on the Method option. I'm glad you liked the answer :) – C. E. Jul 19 '13 at 12:40
• This is really compact and fantastic. – Intemediocre Jul 19 '13 at 12:47

You can use the "IntensityData" and "PixelList" component measurements to get a list of the pixel intensities and locations for each component.Then you can apply whatever processing you want to the intensity values before computing a centroid.

Here's an implementation:

Module[{centroid, a},
a = ImageDimensions[image][[2]];
centroid[{vals_, x_}] := Module[{v = func[vals]},
{#2 - 0.5, a - #1 + 0.5} & @@ (v.x/Total[v])];
MapAt[centroid, #, 2] & /@
ComponentMeasurements[{m, image}, {"IntensityData", "PixelList"}]]

Create some example data:

image = ImageAdjust[Blur[RandomImage[1, {100, 100}], 10]];
m = MorphologicalComponents[Binarize@image];

The standard intensity centroids are:

ComponentMeasurements[{m, image}, "IntensityCentroid"]

(* {1 -> {23.2785, 89.919}, 2 -> {54.4926, 99.1493},
3 -> {55.4731, 42.3182}, 4 -> {0.999261, 72.5},
5 -> {0.69655, 65.0988}, 6 -> {73.6453, 38.3212},
7 -> {2.4862, 1.55681}} *)

We can confirm that adjustedComponentCentroids gives the same result if there is no processing of the pixel values:

(* {1 -> {23.2785, 89.919}, 2 -> {54.4926, 99.1493},
3 -> {55.4731, 42.3182}, 4 -> {0.999261, 72.5},
5 -> {0.69655, 65.0988}, 6 -> {73.6453, 38.3212},
7 -> {2.4862, 1.55681}} *)

To manipulate the pixel values just give the appropriate pure function as the second argument. For example subtracting the minimum:

adjustedComponentCentroids[{m, image}, (# - Min[#]) &]

(* {1 -> {22.0809, 93.249}, 2 -> {54.2119, 99.3953},
3 -> {54.2235, 41.668}, 4 -> {0.5, 72.5}, 5 -> {0.513051, 65.0331},
6 -> {73.9267, 38.2359}, 7 -> {1.90598, 1.10256}} *)

An alternative way to approach this is to operate on a clipped version of the image.

img=Import["http://i.stack.imgur.com/ZGSz0.png"];
Image[Clip[ImageData[ColorConvert[img, "Grayscale"]], {0.3, 1}, {0, 1}]]

Of course you could choose the particular value for the clipping (here 0.3 is chosen arbitrarily) and then send this clipped image to ComponentMeasurements.