# Get Coordinates in Image Processing

I'm new to Mathematica. I'm very impressed about it's abilities. It's very easy to achieve complex tasks. But I fail the simple ones.

How can I get coordinates in image processing?

I'd like to detect one of the following things

• absolute vertical center of the rectangle surrounding all white parts
• gravity center of all white parts

e.g.: for this picture

I want to find the green point (center of outbound box).

-
So the center of outbound box (the green point in your example) is generally NOT the gravity center of all white parts, then which is the one you really want? –  Silvia Apr 22 '13 at 11:42
The question is more general: "How to find and handle positions/coordinates in images". I do not care, which aspect of the precise question is going to be answered ;-) –  Josua Schmid Apr 22 '13 at 12:53
Then that will be a HUGE topic, with various methods for various problems. It's usually not the positions of points, but the special properties of those points, which distinguish them from points you want to drop, are essential for image processing. –  Silvia Apr 22 '13 at 14:35

ImageValuePositions, new in 9, can return a list of white pixel positions.

i = Import@"http://i.stack.imgur.com/opzqB.png";
p = ImageValuePositions[i, White] // Mean
(* {298.896, 21.3231} *)

HighlightImage[i, {p},
Method -> {"CrossMarkers", 5}]


Update: Surrounding rectangle

Get the min/max of white pixel coordinates.

whites = ImageValuePositions[i, White];
corners = Transpose[{
Through[{Min, Max}[First /@ whites]],
Through[{Min, Max}[Last /@ whites]]}]
(* {{218.5, 0.5}, {373.5, 50.5}} *)


Make a rectangle boundary and highlight that region on the image.

rect = corners /. {{x1_, y1_}, {x2_, y2_}} :>
With[{dx = 2}, Join[
Table[{x1, y1} + {0, y}, {y, 0, y2 - y1, dx}],
Table[{x1, y2} + {x, 0}, {x, 0, x2 - x1, dx}],
Table[{x2, y2} - {0, y}, {y, 0, y2 - y1, dx}],
Table[{x2, y1} - {x, 0}, {x, 0, x2 - x1, dx}]]];

HighlightImage[i, rect,
Method -> {"DiskMarkers", 1}]

-
@Mr.Wizard But he is looking for "gravity center of all white parts." –  BoLe Apr 22 '13 at 11:13
@belisarius Okay, I've been put in my place. :-o (+1 to BoLe) –  Mr.Wizard Apr 22 '13 at 11:16
+1. Didn't notice the "gravity center" phrase in OP's question. –  Silvia Apr 22 '13 at 11:43

For another approach, there's always the ComponentMeasurements way:

cm = ComponentMeasurements[Dilation[i, BoxMatrix[5]] , "Centroid"]


{1 -> {297.633, 23.7345}}

mean = Mean@cm[[All, 2]] (* not needed if only one component found*)

HighlightImage[i,
List@mean,
Method -> {"DiskMarkers", 3},
HighlightColor -> Green]


I'm not happy with the Dilation approach, though, which is a bit sloppy.

-
I thought of Dilation at first, but afraid it may change the center position given that the image pattern is not symmetric. –  Silvia Apr 22 '13 at 10:59
@Silvia I couldn't think of any dilation-towards-the-center commands. MorphologicalTransform doesn't do it either... :( I was close, anyway! –  cormullion Apr 22 '13 at 11:07
+1. Instead of Dilation, you can use the ComponentMeasurements overload that takes a label matrix instead of an image: ComponentMeasurements[ImageData[Binarize[i]], "Centroid"]. That way, you'll always get a single component, because the label matrix only contains 0 and 1. –  nikie Apr 22 '13 at 11:32

Suppose your original image is img.

First we Binarize it:

imgBinarized = img // Binarize;


Then crop all the black boarders:

imgConcerned = ImageCrop[imgBinarized]


So now, using the HitMissTransform (also wiki page), we can easily get its center position:

centerPos = HitMissTransform[imgBinarized, ImageData[imgConcerned]]


To achieve the appearance of your last picture, we construct a diamond marker first:

markerMatrix = Graphics[{EdgeForm[{White, Thick}], FaceForm[],
Rotate[Rectangle[], π/4]},
Background -> Black,
ImageSize -> {15, 15}] // Rasterize //
ColorConvert[#, "Grayscale"] & // ImageData;


Then convolve it with the centerPos image:

imgCenter = ImageConvolve[centerPos, markerMatrix];


Colorize it:

imgCenterColored = Image[
# ImageData[imgCenter] & /@ {0, 3/4, 0, 1},
"Real", ColorSpace -> "RGB", Interleaving -> False];


Compose it with the original image img:

ImageCompose[img, imgCenterColored]


You can explore the image processing functions in Mathematica and find yourself how to add the red boundary box.

# Edit

As cormullion suggested, in case you want the coordinate of the center point, you can extract it from the mask image centerPos straightway:

Position[Transpose[ImageData[centerPos, DataReversed -> True]], 1]


{{297, 26}}

-
I like your HitMissTransform. If the OP wants the coordinates of the point, can they get that from centerPos? –  cormullion Apr 22 '13 at 10:36
@cormullion I think ImageData then using Cases or ArrayRule etc to extract it would be viable. –  Silvia Apr 22 '13 at 10:38
If he's new, he might want to see that...! (or Position[Transpose[ImageData[centerPos, DataReversed -> True]], 1] might work...) –  cormullion Apr 22 '13 at 10:40
@cormullion Thanks for suggestion. Have added it :) –  Silvia Apr 22 '13 at 10:57

A variation of BoLe's center-of-gravity method for version 7:

img = Import["http://i.stack.imgur.com/opzqB.png"]

SparseArray[ImageData@Binarize@img]["NonzeroPositions"] // Mean // N

{30.1769, 299.396}


Output in (y,x) order.

SparseArray is much faster than Position:

dat = ImageData@Binarize@img;

Do[SparseArray[dat]["NonzeroPositions"], {1500}] // Timing // First

Do[Position[dat, 1], {1500}] // Timing // First


0.14

2.465

Links to other uses of SparseArray Properties here.

-
Thank you for this very general answer! +1 for legacy support. Why is the resulting vector the wrong way arround: {y,x} instead of {x,y} ? –  Josua Schmid Apr 23 '13 at 13:11
@Josua You're welcome. It's just an artifact of the way array elements are indexed in Mathematica: row, then column. –  Mr.Wizard Apr 23 '13 at 13:24
@Josua By the way I just revised the timings to better emphasize the superiority of SparseArray over Position. –  Mr.Wizard Apr 23 '13 at 13:27

Here's one way that exploits the fact that you have a constant black background. If your image is called img, then

small = ImageCrop[img]


crops it and leaves just the central rectangle.

Now you want the center of the cropped image, which can be found

cen = ImageDimensions[ImageCrop[img]]/2


To display the small image and the centerpoint:

Show[small, Graphics[{Thick, Orange, Point[{{cen, cen}}]}]]


This gives you a little orange dot at the center.

You can of course, change the orange dot to whatever you like.

-
+1. In this case ImageCrop works but in more complicated cases it can do wrong cropping: "Precise cropping with ImageCrop." The reliable way to crop is ImagePad[img, -BorderDimensions[img, 0]]. –  Alexey Popkov Apr 22 '13 at 13:47
Sure, that's why I prefaced my answer with "Here's one way that exploits the fact that you have a constant black background." In general you might want to binarize the image, and perhaps then apply some kind of erosion in order to make the background uniform. But in the present case the image was already binary and already noise-free. –  bill s Apr 23 '13 at 6:55