I have no knowledge in mathematica, python, and other great softwares.

I saw the following picture in a website;

enter image description here

Where a software detects objects (like bus) and tells about its height or width or such things.

Well, I work in a chemical laboratory. Cameras are used to take pictures of the samples to be analyzed.

See a real picture:

enter image description here

Then I need to measure the distance (the black gap). What I always do is to open the image using Paint in my Personal Computer, select the range manually, and take the reading. See the illustration of what I do:

enter image description here

$549$ in this particular example, (I do not need the height like the $185$) (only width is required).

My question now, can mathematica, or any other software, detects those two red vertical lines (which are not in the original image, I put them for illustration purpose) and measure the distance between them in (px)?

Some Other examples:

  1. Small gap: enter image description here

  2. Not a very clear gap. However, the required distance is the one in the middle (not in the left) white/grey-to-white/gray (by averaging) (Errors are accepted up to +/- 5 px). enter image description here

If you need me to clarify something, please let me know. I am sorry I do not know English very well. That is the reason.

Any help would be really appreciated. Thanks in advance!


Update 2: A simpler/faster approach to get the gap length:


hWidth = Composition[Max, Cases[p : {0 ..} :> Length[p]], 
   Drop[#, #[[-1, 1]] - 1] &, Drop[#, 1 - #[[1, 1]]] &, 
   Split, Map[Max], Transpose, ImageData, MorphologicalBinarize, ImageCrop];

hWidth /@ {img1, img2, img3}
 {548, 17, 61}



hWidth2 = Composition[# - 1 &, Max, Differences, 
   SparseArray[#]["NonzeroPositions"] &, 
   Map[Max], Transpose, ImageData, MorphologicalBinarize, ImageCrop];

hWidth2 /@ {img1, img2, img3}
 {548, 17, 61}

Original answer:

A three-step procedure:

  1. Full vertical dilation of the input image using Dilation

  2. Selecting components (all rectangles after dilation) using SelectComponents and

  3. Using ComponentMeasurements to get the "BoundingBox" and "CaliperWidth" of the selected components

ClearAll[vDilate, internalRectangles, dataSet]

vDilate = ColorNegate @ 
   Dilation[MorphologicalBinarize@#, ConstantArray[1, {ImageDimensions[#][[1]], 1}]] &;

internalRectangles[w_: 1] := Rectangle @@@ Values[
      SelectComponents[vDilate @ #, 
       #CaliperWidth > w && #AdjacentBorders == {Bottom, Top} &], 
     "BoundingBox"]] &;

dataSet[w_:1] := ComponentMeasurements[
   SelectComponents[vDilate @ #,
     #CaliperWidth > w && #AdjacentBorders == {Bottom, Top} &], 
  {"BoundingBox", "CaliperWidth"}, "Dataset"] &;


{img1, img2, img3} = Import /@ 

enter image description here

For the first two images we get a single rectangle using the default value (1) for the caliper width threshold:

dataSet[] @ img1

enter image description here

HighlightImage[#, internalRectangles[]@#] & @ img1

enter image description here

dataSet[] @ img2

enter image description here

HighlightImage[#, internalRectangles[] @ #] & @ img2

enter image description here

For img3, we get many rectangles with the default caliper width threshold:

dataSet[] @ img3

enter image description here

HighlightImage[#, internalRectangles[]@#] & @ img3

enter image description here

Using a larger value for the threshold caliper width gives a single rectangle:

dataSet[25] @ img3

enter image description here

HighlightImage[#, internalRectangles[25]@#] & @ img3

enter image description here

Alternatively, we can select the rectangle with maximal width from internalRectangles[]@img3:

   MaximalBy[#[[2, 1]] - #[[1, 1]] &]@(internalRectangles[]@#)] &@img3

same picture

Update: To get the internal rectangle with maximum width in a single step, we can define dataSet as follows:

dataSet2 = MaximalBy[#CaliperWidth &] @
     SelectComponents[vDilate @ #,  #AdjacentBorders == {Bottom, Top} &], 
    { "BoundingBox", "CaliperWidth"}, "Dataset"] &;

dataSet2 /@ {img1, img2, img3}

enter image description here

  • $\begingroup$ Overly extended answer with far too many updates, for the sake of clarity, please post only what you think is the correct answer. $\endgroup$ – Pedro Lobito Jun 11 '20 at 9:54
  • $\begingroup$ It seems easy for some command writers. Dear, as I said: "I have no knowledge in mathematica, python, and other great softwares." Well, where should I paste the code you provided? Possible in mathematica online? or I must download it? Can I use that code for around $10,000$ pictures, .. Please dear, I need you to consider my knowledge. What I need is a very simple procedure that I should do,like;(Download the software from "LINK". Open the "Window".Paste the provided code. Drag your pictures, click "Run". Provide some screenshot if possible my dear.That would be really really appreciated.(+1) $\endgroup$ – Hussain-Alqatari Jun 11 '20 at 15:38
  • $\begingroup$ @Hussain-Alqatari, you can use the code with Mathematica Online. Go to the page wolframcloud.com and follow the instructions to open an account. Then copy/paste/evaluate the code in this post in a new notebook. $\endgroup$ – kglr Jun 11 '20 at 18:54
  • $\begingroup$ @kglr Well, See this pic ibb.co/N7nN0m9 , am I doing right, pasting it in the right place? If right, what should I change now, like #AdjacentBorders and #CaliperWidth and "BoundingBox" and such things,, need to be changed? Ok, then where to upload the pictures to be processed. Please help me sir, your support is really appreciated. $\endgroup$ – Hussain-Alqatari Jun 11 '20 at 20:02
  • 2
    $\begingroup$ @kglr I think (NOT SURE), I can continue now. Words are powerless to thank you, bounty after $7$ hours :). $\endgroup$ – Hussain-Alqatari Jun 12 '20 at 11:16

This is perhaps a less precise but very fast approach which returns reasonably good widths for these three images. It binarizes the images, and collapses all rows down onto a single row by adding them together and unitizing them. It then looks for runs of zeros in the middle of the row and returns the lengths. I take the Max of these lengths after I call the function on each image:

gapSizes[img_] := 
 Module[{values, splits, 
   bimg = Binarize[MinFilter[img, 4], Method -> "Mean"], dsc},
  (* remove small blobs with < 1% of the total pixel count *)
  dsc = Round[0.01*Times @@ ImageDimensions[img]];
  bimg = DeleteSmallComponents[bimg, dsc];
  values = Unitize[Total[ImageData[bimg]]];
  If[values[[1]] == 0, values = Drop[values, First@FirstPosition[values, 1] - 1]];
  splits = Select[Split[values], #[[1]] == 0 &];
  Length /@ If[splits[[-1, 1]] == 0, Most[splits], splits]]

imgs = Import /@ {"https://i.stack.imgur.com/kRjAA.jpg", 
Max[gapSizes[#]] & /@ imgs

(* returns {552, 15, 51, 140} *)

Note it is quite sensitive and images with low brightness areas will perform badly.

  • $\begingroup$ It worked much much much faster than the previous code, also with reasonably good widths. Thanks a lot. BUT, for some images, like i.stack.imgur.com/b9M7d.jpg, it shows $173$ (NOT reasonably good width). See i.stack.imgur.com/Icrlx.png Almost $137-139$, not $173$. Can we improve this? THANKS A LOT! $\endgroup$ – Hussain-Alqatari Jun 14 '20 at 9:00
  • $\begingroup$ I am still trying but could not know why for some images it shows (not) good width. It work fast and good for some images, that is really great job from you. I believe you can. $\endgroup$ – Hussain-Alqatari Jun 14 '20 at 11:36
  • $\begingroup$ I've updated the answer. Some of your images have poor brightness on the leading edge of the right hand side. I don't think a one-size-fits-all solution exists without at least some manual cleanup of the images. $\endgroup$ – flinty Jun 14 '20 at 12:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.