3
$\begingroup$

I have this picture:

enter image description here

I'de like to obtain the distance between two of these points. The black scale bar is 3$\mu m$.

To do so, I import the image to Mathematica and call it pic. Then by finding dominant colors, I define red as

red = RGBColor[0.827134595933227, 0.00309700865836607, 
      0.08256587730260687, 1.];

And then I obtain the coordinates of the centers of the dots as

  centroid[pic_,color_] := ComponentMeasurements[
  Binarize@ColorDetect[pic,color],
  "Centroid"
  ][[All,2]]

  coordinates = centroid[pic,red]

So

            {{5.5, 39.5}, {13.9828, 37.5345}, {34.2308, 37.8846}, {23.6034, 
            34.8103}, {5.38571, 32.5571}, {15., 29.}, {1.19231, 24.8077}}

Now, let's say we want to obtain the distance between two points of coordinates: {15., 29.} and {23.6034, 34.8103} which is 10.3816.

Now, my question is that how can I also determine the size of scale and obtain the real distance between these two points?

$\endgroup$
2
  • $\begingroup$ You can try using the function copyCurve [here][1]. It was aimed for other purposes, but it can also easily do what you need. [1]: mathematica.stackexchange.com/questions/44355/… $\endgroup$ Commented Feb 10, 2021 at 8:55
  • $\begingroup$ This can be done with the copyCurve function. As well as the distance definition. $\endgroup$ Commented Feb 10, 2021 at 13:05

1 Answer 1

1
$\begingroup$

First, let's get the scale bar out of the image. There are a few ways to do this (MorphologicalBinarize and friends), but I went with the more eye-bally approach of using ColorReplace. Here, we're replacing black pixels with white, and all other pixels with black, to get a mask. Note that ColorReplace has a third argument d, which you could use to fine-tune what it sees as "black".

i = Import["https://i.sstatic.net/yR6UY.gif"]

c = ColorReplace[i, {Black -> White, _ -> Black}]

enter image description here

Compare with a simple MorphologicalBinarize - the choice is yours:

enter image description here

Now we can get the bounding box of our rectangle, and simply take the difference:

box = ComponentMeasurements[c, "BoundingBox"][[1, 2]]

Differences /@ Transpose@box

{{38.}, {5.}}

So the longest dimension of our bounding box is 38 pixels long, and the short side is 5 pixels long.

Just to check, I counted the pixels another way, using SequenceCases on our replaced image, and each line of 1s, the pixels of the scale bar, had a length of 38.

SequenceCases[
 Flatten@MorphologicalComponents[c], {p : Repeated[1]} :> Length@{p}]

{38, 38, 38, 38, 38}

Either way, we get about 38 pixels, so now we know the length of the bar, and thus we can get the size of a pixel (assuming pixels are square here).

pixelLength = Quantity[3, "Microns"] / 38

Quantity[0.0789474, "Microns"]

and now if we get the Euclidean distance between your points and multiply it by our pixel length, we get the distance in microns.

EuclideanDistance[{5.5, 39.5}, {13.9828, 37.5345}] * pixelLength

Quantity[0.687437, "Microns"]

This answer will not hold up well if the bar is not always aligned perfectly to the image border, as it is in your test image. You could consider ComponentMeasurements[c, "Length"] or "CaliperLength" to get the length of the bar instead in that case. However, it may take the distances between opposite corners of the bar, which may not be what you want.

$\endgroup$
4
  • $\begingroup$ I did not have my coffee before this answer - I would wait until a better-caffeinated person comes by and gives a better response. I suspect my approach is not scientific enough and potentially just plain wrong. $\endgroup$
    – Carl Lange
    Commented Feb 10, 2021 at 9:43
  • $\begingroup$ Not that I'm aware of, but you could simply create a function called pixelDistanceInMicrons[p1_, p2_, pixelLength_] := EuclideanDistance[p1, p2] * pixelLength and call it like pixelDistanceInMicrons[{5.5, 39.5}, {13.9828, 37.5345}, Quantity[0.0789474, "Microns"]]. $\endgroup$
    – Carl Lange
    Commented Feb 10, 2021 at 10:36
  • $\begingroup$ You could also count the pixels by hand, I suppose. $\endgroup$
    – Carl Lange
    Commented Feb 11, 2021 at 9:02
  • $\begingroup$ If the bar is diagonal, counting pixels in the way I have done it here will not work, as I am counting them left to right. As I mention, I think "CaliperLength" will do a reasonable job in this case. I am hopeful someone else will post a more scientific answer as I'm not convinced this method would be enough for Real Science and I'm not sure I can help you further because of that. Hope my answer at least helps you find your way to a more useful result :) $\endgroup$
    – Carl Lange
    Commented Feb 11, 2021 at 12:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.