Image processing - diameter distribution of fibers

Hello everyone,

I have a bunch of SEM images of a network of fibers. I would like to get the distribution of diameters, but please note that fibers' diameter changes along the fibers' length - so even if there are 10 fibers in the image, the distribution should not be made from 10 points, rather from a large number of step by step detected diameters (or better, width) for each fiber.

I have played around with the image processing demo experiment and not found a suitable way to do that. I was thinking of trying a brute force approach, making the image binary, then scanning pixel by pixel to check if the actual pixel is at the border of a fiber, then take the previous and next 5 pixels on the border and make a line through them, then create the perpendicular line to this one and finally check when this line touches the other side of the fiber.
Maybe difficult to be done and definitely time consuming, scanning millions and millions of pixels in a bunch of images...

Do you think there is a more elegant and time saving way to do that?
Or is it possible to somehow implement this feature as a series of steps in the already existing Image Processing functions?
Or even better, if someone has done it already!

Let me add, this would be an extremely appreciated feature, as my alternative is to buy a very expensive stand-alone software that does just and only that.

Thanks in advance for your answers!
One possibility worth trying might be the ImageAnalyzeParticles  [ flags ] keyword  imageMatrix
operation. In particular, the following key word looks interesting:
stats   Measures the particles in the image. Results of the measurements are
          reported for all particles whose area exceeds the minArea  specified by the
          /A flag. The results of the measurements are:
    V_NumParticles  Number of particles that exceed minArea  limit.
    W_ImageObjArea  Area (in pixels) for each particle.
    W_ImageObjPerimeter     Perimeter (in pixels) of each particle. The perimeter
                           calculation involves estimates for 45-degree edges resulting in
                           noninteger values.

This pre-supposes your fibers are isolated and can be treated as "particles". Masking might be needed at the edges of the large image wave. If this works, the mean fiber diameter would be its area divided by the perimeter.
Hello,
thanks for your answer. I think that does not work, because the fiber must not be considered as a single particle. I need the distribution of diameter along its length. To make an example, if I have one fiber in my image, I don't need 1 diameter - rather a full distribution centered at the most common diameter.

The approach you kindly suggested may work in case it would be possible to change the Wavemetrics code, detecting particles but then not excluding the area covered by this particle from next detection. And the particles, should be forced to be circles. Kind of filling the interior of the fiber with circles. Maybe :)
I am a bit confused. Could you post a representative image that you are trying to analyze?
--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
The word "diameter" suggests round elements in your image, but the other part of your description suggest that maybe it contains linear elements and you want the widths of those linear elements at a number of locations. You might get something with ImageLineProfile. Have you found the Image Processing tutorial? Select File->Examples->Tutorials->Image Processing Tutorial.

Jeff Weimer is right- we need an example image.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
I'd echo J.J. Weimer's suggestion that you post a sample image (it is, after all worth 1k words).

Without a picture or more details it would seem to me as if you have an image of a fiber that has a varying diameter as a function of length and you want to map that diameter. If that is the case then a tricky way of getting a "measure" of the diameter is actually using ImageLineProfile. If you set the profile path along the center of the fiber, choose a width w such that w is just a bit larger than the largest diameter and use the /S flag you will get a distribution of the standard deviation of the intensity profile perpendicular to the axis of the fiber. Clearly this method will fail if you have significant background or foreground intensity fluctuations. In which case I'd approach the problem differently:
1. identify the path of the fiber using thresholding followed by morphological operations to get a path corresponding to the axis of the fiber.
2. for points along the axis of the fiber compute local derivative to find the local normal to the axis.
3. use ImageLineProfile for two points on both sides of the local normal.
4. extract the edges of the fiber from the path profile.

I hope this helps,

A.G.
WaveMetrics, Inc.
...if you have significant background or foreground intensity fluctuations. In which case I'd approach the problem differently:
1. identify the path of the fiber using thresholding followed by morphological operations to get a path corresponding to the axis of the fiber.
2. ...


if the thresholding is good enough ---i.e., if you don't gain much from determining the fibre edge locations from each line profile separately--- a Euclidean distance transform will give you the fiber diameter distribution pretty much directly. The histogram of the EDT is something like the integral of the desired fibre width distribution, because the EDT will have a triangular profile across the width of the fiber, reaching half the fibre width in the middle. There's no need to find the middle of the fibers and determine directions normal to the fiber axis, because the EDT does that. This method would be quite sensitive to outlier background pixels inside the fiber. Some morphological operations to get rid of those first might be wise. Also, getting the distribution from its integral is liable to be numerically unstable (If the radius of curvature of the centerline is smaller than the radius of the fiber, parts of the triangular profile go missing.), so this method will only work well for the distribution of the thickest fibers.

I've posted a function to calculate something close to the EDT in snippets. It's been a while since I used that code, and with those for loops it's probably not nearly as fast as an XOP or built-in function would be, but I seem to remember it worked more than well enough for my needs at the time.
thanks everyone for your kind answers, very appreciated.
I am sorry I messed it up with the description: my aim is to measure the diameter of fibers indeed, however in a SEM image these are the "widths of linear elements" as suggested above.
I attached a couple of examples. The diameter ermmm width seems to be the same along the fiber, however this is not a general example.

I will try to have a look at the ImageLineProfile, but my guts feeling is that the task is a little more complicated than what can be done with standard features. As for A.G. comment and his 4 suggested steps... "identify the path of the fiber using thresholding followed by morphological operations to get a path corresponding to the axis of the fiber" seems very difficult, please see the attached examples. I will try somehow, and sorry again if I wasted your time with a not too clear description of the problem. I hope the attachments can help.

Sbossuyt, I didn't really understand what the Euclidean Distance Transform does, so I will just try it and see if it works.
DavideT wrote:
As for A.G. comment and his 4 suggested steps... "identify the path of the fiber using thresholding followed by morphological operations to get a path corresponding to the axis of the fiber" seems very difficult, please see the attached examples.


Now that I see the images in question it is clear that what I suggested is not applicable. In fact, I wonder if any simple method is applicable because it appears that the fibers in the image are not necessarily parallel to the plane of the image complicated further by the fact that you have fibers intersecting each other. This makes me wonder if you really need to know the variation of the diameter along the length of individual fibers or if you need to determine the general distribution of diameters.

If you need to find the actual variation along each fiber then you first have to extract the relevant portion of a fiber from the image and only then apply one of the methods mentioned above. If you are only interested in the distribution of fiber diameters within the image I'd explore the possibility of obtaining that information from spatial transforms.

A.G.
WaveMetrics, Inc.
My thoughts here are to consider first how I would do this when I had to do it by hand. I can think of one method that is equivalent to what is done for grain size analysis. Consider that you had only one fiber extending at some tilt but mostly in a horizontal direction all the way along inside the image. This presumes the fiber is perfectly straight along its travel ... all that varies is its diameter.

* Draw two perfectly vertical lines at opposite ends of the image frame. They are at x positions of 0 and xI, where xI is the width (in x) of the image.
* Find the intercepts of those lines with the fibers. You will have four points ... LEFT (x = 0) : (yL1, yL2) and RIGHT (x = xI) : (yR1, yR2).
* The equation along the fiber center line is yCL = mCL x + bCL. Find it.
* Use geometry to find the equation for the perpendicular to the center line at the two ends
* Use geometry to get the fiber diameter at the two ends

You could do the same with perfectly horizontal lines instead when you have fibers that are nearly vertical.

This suggests to me that a function could be designed do all of the math when given the (six) coordinates xL, yL1, yL2, xR, yR1, yR2. All that would be left is a GUI to allow you to pick off coordinates on any given fiber from two vertical or two horizontal lines that intercept the fiber at some reasonable separation distance.

An alternative method that comes to mind is to draw straight lines along the two fiber edges, then find the angle between them using vector algebra (dot product). Use geometry to convert the change in angle to a change in diameter per length. With this, and given the diameter at one end of a fiber, you can calculate its diameter at the other end.

--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
DavideT wrote:
Sbossuyt, I didn't really understand what the Euclidean Distance Transform does, so I will just try it and see if it works.


Given a binary image, i.e., an image where every pixel is labeled as either foreground (1) or background (0), the Euclidean Distance Transform returns a grayscale image where every pixel value is the euclidean distance (i.e., straight-line distance) to the nearest background pixel. For the background pixels, that distance is 0, for pixels directly adjacent to a bacgkground pixel it is 1, for pixels sharing a corner but not an edge with a background pixel it is sqrt(2), etc...

The algorithm actually calculates the distance squared, which is separable according to Pythagoras' theorem, and then takes the square root of that at the end. The algorithm I implemented isn't exact, but it has the benefit that it's easy to follow what's going on: it scans across the image left-to-right and top-to-bottom, and then right-to left and bottom-to-top, keeping a record of the nearest background pixel of each pixel and checking nearest background pixels to the nearest neighbors of the current pixel as possibly nearer background pixels to the current pixel than what it has recorded until then. It should be fairly straightforward to modify the code so that it creates an extra wave flagging where the nearest background pixel is very different to that of a neighbor, for example storing a copy of the distance value from the EDT for those pixels. The binary image indicating the location of those pixels is called the skeletonization of the binary image, and includes the centerlines of the fibers. It also includes side branches where the fiber changes direction. (Interestingly, the combination of the skeletonization and the EDT values for those pixels is enough information to reproduce the original binary image.) The distance values will be larger than the fiber diameter where fibers overlap in the image (because the nearest background pixels will be the corner pixels of where the fibers overlap, rather than the edge pixels of each fiber), but where the fibers do not overlap, the value of the EDT on the centerline is half the diameter of the fiber.

It will likely be non-trivial to deal with the cases where fibers overlap in the image in any sort of automated way. Also, have you thought about the need to avoid sampling bias in the way you measure the fiber width distribution? The number of width values you take from each fiber to build up the distribution should reflect how much you want that fiber to count towards the distribution, rather than how many width values you could get out of the image for that fiber! The fiber crossings will affect how many width values you can get out of the image for a given fiber, for example. Depending on what you want, you may
1) just manually identify the ridges in the EDT image and store a handful of values from that, or
2) you may want to mask out the fiber crossings and side branches from the skeletonization, but otherwise gather statistics on all the EDT values from the pixels picked out by the skeletonization, or
3) mask each fiber individually, and get separate statistics for the width distribution of that fiber along its length from that, or
4) maybe something else still.

On second thought, I just had an idea for what might be a good way to deal with the cases where the fibers cross in the image: if you decide to modify the EDT code I posted, it should be fairly straightforward to identify when the new nearest-background-pixel is (close enough to) diametrically opposed from the old one, and only store a copy of the distance for those pixels instead of doing the skeletonization properly. Rx and Ry in the code give the relative position to the nearest-background pixel from the current pixel. At the very end, of the second pass, you find out what direction that is, take the nearest neighbor pixel to the current pixel in the opposite direction, and if that has Rx and Ry values close to the opposite of the current pixel, you keep a copy of its distance value. If you care about the orientation of the fiber, you might keep a copy of Rx and Ry instead. I don't have time to try this for myself, but I'm curious how well it works. Looking at your example images, I think it should work reasonably well if your method of classifying pixels as foreground or background pixels works well enough. The methods does still introduce sampling bias in the width distribution of the fibers, however.

Kind regards,

-- Sven Bossuyt
Dear all,

thanks for your comments, I have some more ideas now how to approach the thingy - and some interesting steps forward. I will just be stubborn and keep on trying. After all, I realized that the task is difficult and also the alternative software does not really give "the" distribution, rather a good approximation. Hopefully I'll manage to get a rather good aprroximation myself. I could buy tens of Igor licenses (per year!) for all my colleagues, with the money saved from that other software :)

I'll keep you informed of the progress.
Hello,
here I am again... we switched to a FEG-SEM and used the back scattered electrons detector, so the contrast is better.
Then I wrote a few lines of code that take the image, make it binary with a certain threshold (I used this, so I was able to delete the SEM info without using ROI etc. In the future I will identify the threshold from the histogram. At the moment I pass 120 and it uses it for the threshold), detect the edges of the fibers, and find the centers.
Finding the centers with my own code kind of works, it just "overdetects" at the edges but I am relatively confident I can fix that, adding an if-endif. [corrected editing this post, it doesn't anymore]
I attached a test wave that is one example image, if you run this code you will get three images indeed (binary, edges, edges+centers).

It looks like it's time to use your previous suggestions with line profile or euclidean distance transform.
I thought of posting this, both such that other users can use something similar if needed, and also to possibly get some more ideas from you looking at these examples.
A cool thing would be to get thinner edges.

And... I hope you will be able to load and run this function, working with images and their .tif at the end into the name of the waves gives me headaches... just in case I did something wrong, I also attached the jpeg of how these images look like.

Thanks in advance for any idea!

function change_it(threshold)    //using 120 works quite fine with this example
variable threshold
 
wave 'area1_001b.tif'   //at the moment, it works on this specific image/wave
duplicate/o 'area1_001b.tif', thisone
 
variable c1,c2,c3,c4,c5 //counters
 
variable columns=1024
variable rows=1024 //one is wrong but doesn't matter for now
 
for(c1=0;c1<columns;c1+=1)
for(c2=0;c2<rows;c2+=1)
 
if(c2<58)       // 58 to get the SEM info away
thisone[c1][c2]=0
endif
 
if(thisone[c1][c2]<=threshold)
thisone[c1][c2]=0
endif
 
if(thisone[c1][c2]>threshold)
thisone[c1][c2]=254
endif
 
endfor
endfor
 
 
Display/k=1;AppendImage thisone
//MatrixFilter/N=(3)/P=1 median thisone // median filter maybe?
 
ImageEdgeDetection/M=4/N Kirsch, thisone    //edge detection with fuzzy entropy
wave m_imageedges
display/k=1;appendimage m_imageedges

 
 
//here on, try detecting fiber center

 duplicate/o M_imageedges,edges
duplicate/o edges,centers   //to have them superimposed

// slim_it()   tried an auxiliary function to slim the edges which quite works, not perfect
 
for(c1=0;c1<columns;c1+=1)
    for(c2=0;c2<rows;c2+=1)
 
    if(edges[c1][c2]<1)     //means it is an edge point - edge is 3 pixels thick!?
    if(thisone[c1][c2]>0)           // and it is not a "void" between fibers
 
        for(c3=1;c3<100;c3+=1)  //moves horizontally or vertically, not sure. and scans looking for the next edge      
        c5=c2+c3
        if(thisone[c1][c5]<1)       //it didn't touch the other side till this comes true
        c4=c2+c3/2                    //middle position should be the center
        centers[c1][c4]=255
        c2=c2+10                //hopefully move away from the identified center
        c3=101                  //stop the for cycle... loved GOTO in basic
        endif
        endfor     
 
    endif
    endif
 
 
    endfor
endfor

let_edges_be_edges()

display/k=1;appendimage edges
display/k=1;appendimage centers
 
end
//******************************************************
function let_edges_be_edges()

wave centers,edges
variable c1,c2,c3,c4,c5 //counters
 
variable columns=1024
variable rows=1024 //one is wrong but doesn't matter for now
 
for(c1=0;c1<columns;c1+=1)
    for(c2=0;c2<rows;c2+=1)
   
    if(centers[c1][c2]>254)
    if(edges[c1][c2]<1)
    centers[c1][c2]=0
    endif
    endif
    endfor
endfor

end
DavideT wrote:
Hello,
here I am again... we switched to a FEG-SEM and used the back scattered electrons detector, so the contrast is better.


Do you have an EDS system attached? You could run element maps or better spectral images on those samples and try to map out the fibres based on spectral features, rather than on grey scales. That could make it easier to separate the objects of interest from the background.

Regarding the wave name. Why not pass the wave as parameter to the function, e.g.

function ChangeIt(W_Image, threshold)
    wave W_Image   //this is your image
    variable threshold

   //main code
end


then call it:

ChangeIt('area1_001b.tif', 120)


Ideally get rid of the '.tif' ending during import.
Hello, yes we have an EDS but acquisition time would be far too high to have a good signal-to-noise and high enough throughput. Besides, it would see only carbon everywhere since these are polymers. Perhaps some different contrast from the geometrical arrangment of the network. In any case, the contrast with BSE is very good, I would consider the background problem solved - fibers are detected pretty good and I was able till now to do some Igor on them.

Thanks for the advice about the wave. When the actual algorithm will be done, I will run a loader function that loads and renames all loaded waves as wave0, wave1, wave2 etc so the beginning of the analysis will be to create strings with content "wave0", "wave1", "wave2" etc, duplicate the waves with those names into the wave ***thisone*** and go ahead with the hopefully working code that I pasted above.


I forgot to add, I don't need indeed the distribution of diameters-widths along each fiber, an overall distribution is more than fine. I tried to FFT the image with the edges and create an isotropic Power Spectral Density. That looks pretty much like a SAXS intensity vs q but I wasn't able to actually extract the distribution I am looking for. A spatial transform of sort might work fine indeed, only I don't know what else to try there. I am starting to think that real "measurement" across the centers is the only way. Or perhaps growing circles, point by point in the "centers" image till the circle touches the edge.
You are probably loading the image using ImageLoad. If that is the case you should use the /N flag and provide your own base name, e.g.,
ImageLoad/N=myWave /T=tiff "path to file"


Otherwise I think the advice you got above is good, i.e., you should pass the wave as an argument to the function instead of using a hard-wired name.

If you want to get an over-all distribution you might be able to simplify your problem by subdividing the image into sufficiently small rectangles so that each one contains a very small number (ideally one or two) fibers. The other advantage of this approach is that you could assume that there is only a single fiber width in each one of these sub-images and a single characteristic width. To obtain that width I would use thresholding followed by particle analysis and then use the fitting ellipse (or the raw-moments data) to obtain an estimate of the width.

As for the spatial-transform approach: I am not sure how you are analyzing your data. Simple observation of the FFT is not going to be helpful. To use this approach effectively you need to model your fibers and then fit the transform results to the model to obtain an estimate of the distribution. This is theoretically similar to the problem of estimating surface roughness from laser speckle images.

AG
AG wrote:
If you want to get an over-all distribution you might be able to simplify your problem by subdividing the image into sufficiently small rectangles so that each one contains a very small number (ideally one or two) fibers.

If you can do this, it might be useful to use Igor's Hough Transform (in ImageTransform) to identify fiber line segments.
DavideT wrote:
Or perhaps growing circles, point by point in the "centers" image till the circle touches the edge.


That is equivalent to growing circles from the edge, until they touches a circle from the opposite side. Which is exactly what the Euclidean Distance Transform does, quickly and efficiently, for all edge pixels at once. (It would be much quicker still if it was implemented as a native function or an XOP, but the algorithm I implemented is pretty efficient: it is a two-pass filter using only information from nearest-neighbour pixels, so the run-time per pixel is constant.) The optional parameter bg in the Make_fastEDT function I posted allows to directly pass a grayscale image and set the threshold for the background level.

As I explained earlier, the EDT function internally keeps a record of the relative location of the nearest edge pixel. I've updated the function with a quick test whether the next pixel away from the nearest edge pixel has a different edge pixel as its nearest edge pixel, or one that is nearly diametrically opposed. If you want access to the various waves the function uses internally, just remove or comment out the "KillDatafolder :" line at the end, but with the updated function you can also simply pass another optional parameter skel, which will set all the redundant values of the EDT to NaN. As I understand it, this is what you want. Based on some quick tests on simple geometrical masks and running in on the 'area1_001b.TIF' image you posted, it seems to work quite well. It still does not give an unbiased sample of the fiber diameters, but if you weight the histogram of the skeletonized EDT with the square of the EDT (appropriate if the fibers are round), the histogram looks reasonable.

Attached is a picture of the result of:
•make_FastEDT('area1_001b.TIF',bg=120,skel=-1)
NewImage/K=0  root:M_DistanceTransform
ModifyImage M_DistanceTransform ctab= {*,*,BlueGreenOrange,0}

and
Make/O/N=20 W_Histogram
SetScale/P x 1,2,"", W_Histogram
histogram/B=2 M_DistanceTransform, W_Histogram
•W_Histogram*= x^2 // mass-fraction of each diameter, assuming fibers are round


The histogram is based on a bit over 12500 distance measurements from your 1 megapixel image, which are calculated in about 5 seconds on a not-very-new MacBook Pro.
Hello Sven,
thanks that is great.

These days I have programmed a little and done it in two ways:
1) Real brute force with horizontal imagelinesection of each line, measuring the width, then correcting it for the local angle between the fibre and the horizontal line - takes far too long though
2) Pretty much as described in my previous post, finding the centers and then growing two lines (one horizontal and one vertical) till these two hit the end of the fiber. Then Pitagora's theorem to evaluate the half width, assuming the fiber is locally straight. This takes a reasonable time, ~10 seconds of my ThinkPad per image, detecting 40% to 80% of the diameters. So it asks for a path, loads all 16 images (4 areas at 4 different magnifications) in the folder, recognizes the magnification, runs the analysis, saves the .tif of all produced images (edges, centers, detected diameters) and the distributions. Total 3 minutes per folder.

I will try also your EDT approach now, so the detection will be done in 2 different ways. Just to validate each other and be on the safer side - I would guess this will be the closest thing to the "real" distribution.

Thanks again.


--- Sven, I tried your code... it is marvellous! It works perfectly! ---
DavideT wrote:

--- Sven, I tried your code... it is marvellous! It works perfectly! ---


A.G., is there any chance of getting the Euclidean Distance Transform added to the Imagemorphology operation? I haven's asked before, because I thought I might be the only one who would use it, but for many image processing problems, using the EDT really is an elegant (and very fast) way to do it. For example, thresholding the EDT is equivalent to binary erosion with a circular structure element, and for large circular structure elements it is much faster. In fact, I wrote this originally because I needed to do morphological operations with circles of radius > 10 pixels and with periodic boundary conditions.

Also, I think this algorithm is just about the worst case scenario for being slower than it needs to be, when written as an Igor user function. The algorithm accesses the matrix values in a systematic way, and all the operations except the square root at the end are simple operations on integers, with integers guaranteed to be smaller than the image's width, height, or square of the diagonal, respectively. It is possible to rewrite it as a combination of MatrixOps and Imagemorphology operations (using structure elements like {1,0,0} to do pixel shifts), but it becomes so complicated that it's almost impossible to read the code, it doesn't preserve how systematically the matrix values are accessed, and it takes so many of the fast operations that writing it that way isn't that much faster in the end.
sbossuyt wrote:

A.G., is there any chance of getting the Euclidean Distance Transform added to the Imagemorphology operation? I haven's asked before, because I thought I might be the only one who would use it, but for many image processing problems, using the EDT really is an elegant (and very fast) way to do it. For example, thresholding the EDT is equivalent to binary erosion with a circular structure element, and for large circular structure elements it is much faster. In fact, I wrote this originally because I needed to do morphological operations with circles of radius > 10 pixels and with periodic boundary conditions.


Some time back I implemented distance transform as part of the ImageTransform operation. This will be available in IP7. There are options for Manhattan, Euclidian and scaled-Euclidian distances. Looking at the code today I see that it would benefit from automatic multithreading which I may implement if I have time before IP7 beta.

AG
Igor wrote:
Some time back I implemented distance transform as part of the ImageTransform operation. This will be available in IP7.

AG


I guess that's one more reason to look forward to IP7 then!
I don't think it is nice to tease us about IP7.

Some of us having been wishing, pining, dreaming, praying,p leading,... for it to break cover.

Our hopes are inevitably dashed and we are told to wait.

We are a patient lot.
hegedus wrote:
I don't think it is nice to tease us about IP7.


Not meant to tease. It was a direct response to:
sbossuyt wrote:
is there any chance of getting the Euclidean Distance Transform


AG
sbossuyt][quote=DavideT wrote:


Attached is a picture of the result of:
•make_FastEDT('area1_001b.TIF',bg=120,skel=-1)
NewImage/K=0  root:M_DistanceTransform
ModifyImage M_DistanceTransform ctab= {*,*,BlueGreenOrange,0}

and
Make/O/N=20 W_Histogram
SetScale/P x 1,2,"", W_Histogram
histogram/B=2 M_DistanceTransform, W_Histogram
•W_Histogram*= x^2 // mass-fraction of each diameter, assuming fibers are round


The histogram is based on a bit over 12500 distance measurements from your 1 megapixel image, which are calculated in about 5 seconds on a not-very-new MacBook Pro.


Sorry to resurrect an old thread, but I'm trying to accomplish exactly the same task as the original poster.

This is my first day trying out Igor Pro. I've figured out how to generate the M_DistanceTransform image, although I'm not sure how to get that scale that you've got on the side of yours. Again, I've basically been following the steps outlined above verbatim, except of course replacing my own filename in the code.

So my (newb) questions are:

Correlation between histogram and scale bar: It took me a good long while to figure out how to make the histogram properly, even following the example given. But ultimately how do I correlate the values from the histogram into real dimensions (from the scalebar on the original SEM image)?

Giving credit: What would be the preferred way for me to cite this software and the EDT code in any future publications?

Correcting for specimen coating: Also, how is the thickness of the sputtered metal coating taken into account in instances like this (or perhaps it isn't taken into account at all?).



Sorry for the obnoxiously neophyte questions.

I've attached my own image files in case they provide any clues (the cropped grayscale one is the one I'm using for my Wave file, and the original one has the scale bar).

I'm very excited at the possibilities here, just need a little nudging in the right direction. Thank you!!!
Actually, I think I understand better now, after reading this thread carefully (for about the 10th time!).

If I'm understanding correctly, the values I'm getting are the number of pixels in the radius of the fibers (x axis) and the frequency count of measurements matching that radius (y axis).

So it seems that all I have to do is convert the number of pixels into a nm dimension, based on the scale bar in my original image.

I'm doing a few spot checks with various images but so far this seems like a great method (assuming I've understood it correctly).
1moreRobot wrote:
What would be the preferred way for me to cite this software and the EDT code in any future publications?

I guess you could cite the URL for this thread. Using the EDT for this problem seems too small a contribution to write up as a more traditional publication.

Note that the algorithm used gives only an approximation of the Euclidean Distance Transform. The algorithm is described in the paper cited at the EDT code snippet, but frankly that paper is not very good: it offers a proof that the algorithm is exact, but that is not in fact the case. The error in the proof arises because a pixel's nearest background point is not necessarily the nearest background point of any of the pixel's nearest-neighbor pixels. For example, the algorithm fails for a pixel whose nearest point is at offset (-4,1) if there are also points at (-3,3) and (-5,0), erroneously returning sqrt(18) instead of sqrt(17) for that pixel.
1moreRobot wrote:
If I'm understanding correctly, the values I'm getting are the number of pixels in the radius of the fibers (x axis) and the frequency count of measurements matching that radius (y axis).

That would be correct if the distance to the nearest background pixel could be relied upon to equal the radius of the fiber. For a straight fiber segment without spurious background pixels, that is the case. Where fibers cross each other or the edge of the image, it is not, but maybe the errors are small enough to be acceptable.

Issues at the edge of the image you can deal with by cropping the EDT image, discarding a border equal to the maximum fiber diameter, before taking the histogram.

Where fibers cross, you are systematically under-counting the smaller of the two fibers in the histogram and inflating the diameter of the larger fiber. For two equal-diameter fibers crossing perpendicularly, the nearest background pixel to the center of the crossing is at a distance of sqrt(2) times the fiber diameter. Unless you get multiple fibers crossing, that is the worst case for inflated values of the fiber diameter. You can estimate for yourself, from looking at the images, how badly those two caveats are likely to affect the histogram.

1moreRobot wrote:
So it seems that all I have to do is convert the number of pixels into a nm dimension, based on the scale bar in my original image.

That is correct.