edge detection

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

edge detection

Ru Wang
Hi,

I meet a problem when I try to detect the grain boundary using ImageJ 1.37.
There are some unconected lines in the image. I just want to remove them.
But I didn't fund the related operations. If you have any clue on it, could you
please inform me? Thanks a lot.

Ru
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Thomas Boudier
Ru Wang a écrit :
> Hi,
>
> I meet a problem when I try to detect the grain boundary using ImageJ
> 1.37.
> There are some unconected lines in the image. I just want to remove them.
> But I didn't fund the related operations. If you have any clue on it,
> could you
> please inform me? Thanks a lot.

Hi,

You can try the canny-deriche edge detection filter :
http://imagejdocu.tudor.lu/Members/tboudier/plonearticle.2006-07-12.2524848149/ 


for connection you can use the hysteresis thresholding plugin :
http://imagejdocu.tudor.lu/Members/tboudier/plonearticle.2007-01-03.5171158213/

or this one :
http://imagejdocu.tudor.lu/Members/tboudier/plonearticle.2007-01-08.0483309465

Thomas

>
> Ru
>
>


--
/*****************************************************/
 Thomas Boudier, MCU Université Pierre et Marie Curie
 UMR 7101 / IFR 83. Bat A 328, Campus Jussieu
 Tél : 01 44 27 35 78  Fax : 01 44 27 25 08
/*****************************************************/


 
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Michael Schmid
In reply to this post by Ru Wang
Hi Ru,

if you have a skeletonized image and you want to remove all
lines that have an end somewhere inside the image (instead of
ending at junction of lines or at the edge), you can have a
look at the method
   void cleanupExtraLines(ImageProcessor ip)
in
   class MaximumFinder
of the current version of ImageJ.

cleanupExtraLines expects that your lines have pixel values
between 1 and 254, 255 is the background, and pixel value
0 is left untouched.

Unfortunately, that method is not public and cannot be made
public without a few small modifications: One has to call
makeDirectionOffsets(ip) before.
So it would be best to paste the code into your plugin.
You will also need to copy the methods isLineOrDot and
isWithin from MaximumFinder since these will be called by
cleanupExtraLines, and the class variables IS_LINE, IS_DOT,
dirOffset, dirXoffset, dirYoffset used by these methods.

To access the code of MaximumFinder, you have to load a
fairly recent version of the ImageJ sources from
http://rsb.info.nih.gov/ij/download/src/
The code is not online in the "Developer resources" section
of the ImageJ Web page, which contains the old version 1.37.

Michael
________________________________________________________________
Michael Schmid                    email: [hidden email]
Institut fuer Allgemeine Physik, Technische Universitaet Wien
Wiedner Hauptstr. 8-10/134, A 1040 Wien, Austria
Tel. +43 1 58801-13452 or -13453, Fax +43 1 58801 13499
________________________________________________________________

On 3 Mar 2007, at 01:09, Ru Wang wrote:

> Hi,
>
> I meet a problem when I try to detect the grain boundary using  
> ImageJ 1.37.
> There are some unconected lines in the image. I just want to remove  
> them.
> But I didn't fund the related operations. If you have any clue on  
> it, could you
> please inform me? Thanks a lot.
>
> Ru
Reply | Threaded
Open this post in threaded view
|

On edge detection

Juan Francisco-2
dear alls:
  I´m interesting in to use ImageJ in order to detect the centers (centroide) of a circle impresioned on a TIFF image inscribed on a rectangle, as well as the center of this rectangle. It´s would be possible to do this with ImageJ?.
  Thanks very much!!!
 

Michael Schmid <[hidden email]> escribió:
  Hi Ru,

if you have a skeletonized image and you want to remove all
lines that have an end somewhere inside the image (instead of
ending at junction of lines or at the edge), you can have a
look at the method
void cleanupExtraLines(ImageProcessor ip)
in
class MaximumFinder
of the current version of ImageJ.

cleanupExtraLines expects that your lines have pixel values
between 1 and 254, 255 is the background, and pixel value
0 is left untouched.

Unfortunately, that method is not public and cannot be made
public without a few small modifications: One has to call
makeDirectionOffsets(ip) before.
So it would be best to paste the code into your plugin.
You will also need to copy the methods isLineOrDot and
isWithin from MaximumFinder since these will be called by
cleanupExtraLines, and the class variables IS_LINE, IS_DOT,
dirOffset, dirXoffset, dirYoffset used by these methods.

To access the code of MaximumFinder, you have to load a
fairly recent version of the ImageJ sources from
http://rsb.info.nih.gov/ij/download/src/
The code is not online in the "Developer resources" section
of the ImageJ Web page, which contains the old version 1.37.

Michael
________________________________________________________________
Michael Schmid email: [hidden email]
Institut fuer Allgemeine Physik, Technische Universitaet Wien
Wiedner Hauptstr. 8-10/134, A 1040 Wien, Austria
Tel. +43 1 58801-13452 or -13453, Fax +43 1 58801 13499
________________________________________________________________

On 3 Mar 2007, at 01:09, Ru Wang wrote:

> Hi,
>
> I meet a problem when I try to detect the grain boundary using
> ImageJ 1.37.
> There are some unconected lines in the image. I just want to remove
> them.
> But I didn't fund the related operations. If you have any clue on
> it, could you
> please inform me? Thanks a lot.
>
> Ru


 
---------------------------------

LLama Gratis a cualquier PC del Mundo.
Llamadas a fijos y móviles desde 1 céntimo por minuto.
http://es.voice.yahoo.com
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Ru Wang
In reply to this post by Ru Wang
Thank you, Thomas and Michael.

I updated my ImageJ to the latest version and tried those plugins written by
Thomas. But those unconnected lines are still there. How to remove them?
The origianl image and the processed image are posted here :
http://picasaweb.google.com/wangru.ustc/Image_ProcessAlbum

BTW: What operations I have tried are : edge detection, contrast enhancement,
meadian filter,thresholding, skeletonize and dilite.

Thanks a lot.

Best regards,

Ru
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Jim Passmore
Ru,

A couple of thoughts.

Once you have a skeletonized, binary image, the erode function, with binary
option "count" (Process|Binary|Options on the menu) set to 7 will start
removing loose "ends."  You will likely need to play with the "iterations"
option to determine how far it goes.  When I did this on your image, there
were a couple of points that couldn't be removed because of branching in
the skeleton.  To take care of this, I did a simple dilate, then
skeletonize.  That left only a need for one more iteration of erode
(count=7) to remove the stray dots.

The problem with this approach is that the edges of the image are affected,
as well.  You might have to crop the edges off your image if you're doing
measurements.

Here is the recorded macro code that I used:

run("Make Binary");
run("Options...", "iterations=10 count=7");
run("Erode");
run("Options...", "iterations=1 count=1");
run("Dilate");
run("Skeletonize");
run("Options...", "iterations=1 count=7");
run("Erode");

I don't think this completely solves your problem, but I hope it gives you
something to work with.

Jim

P.S.  To give due credit, the "erode with count=7" idea came from notes
from Dr. John Russ's short course.  His Photoshop plugin package has a
"prune" function that may do what you want.  I'm guessing it may iterate
something similar to what I did until the image doesn't change.

----------------------------------------------
Jim Passmore
Research Associate
Sealed Air Corporation
[hidden email]
----------------------------------------------



ImageJ Interest Group <[hidden email]> wrote on 03/08/2007 04:54:49
PM:

> Thank you, Thomas and Michael.
>
> I updated my ImageJ to the latest version and tried those plugins written
by
> Thomas. But those unconnected lines are still there. How to remove them?
> The origianl image and the processed image are posted here :
> http://picasaweb.google.com/wangru.ustc/Image_ProcessAlbum
>
> BTW: What operations I have tried are : edge detection, contrast
enhancement,
> meadian filter,thresholding, skeletonize and dilite.
>
> Thanks a lot.
>
> Best regards,
>
> Ru
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Michael Schmid
In reply to this post by Ru Wang
Hi Ru,

here is a Plugin derived from the code in "MaximumFinder" that
eliminates unconnected lines.
Lines touching the edge are preserved.
Lines must be nonzero pixel values, background zero
(white background with inverted Lut, black background otherwise).

Michael
________________________________________________________________



import ij.*;
import ij.gui.*;
import ij.measure.*;
import ij.process.*;
//import java.awt.*;
//import java.util.*;
import ij.plugin.filter.*;

/** This ImageJ plug-in filter removes unconnected lines from
a binary image with background value 0 and lines having
pixel values > 0 */

public class Remove_Unconnected_Lines implements PlugInFilter {
     /** a point on a line (as a return type of isLineOrDot)*/
     final static int IS_LINE=1;
     /** an isolated point (as a return type of isLineOrDot)*/
     final static int IS_DOT=2;
     /**offsets of neighbor pixels for addressing*/
     int [] dirOffset, dirXoffset, dirYoffset;

     public int setup(String arg, ImagePlus imp) {
        return IJ.setupDialog(imp, DOES_8G);
     }
     public void run(ImageProcessor ip) {
     if (dirOffset==null) makeDirectionOffsets(ip);
         int width = ip.getWidth();
         int height = ip.getHeight();
         byte[] pixels =  (byte[])ip.getPixels();
         for (int y=0, i=0; y<height; y++) {
             for (int x=0; x<width; x++,i++) {
                 int v = pixels[i]&255;
                 if (v>0) {
                     int type = isLineOrDot(ip, x, y);
                     if (type==IS_DOT) {
                         pixels[i] = (byte)0;                  //
delete the point;
                     } else if (type==IS_LINE) {
                         int xEnd = x;
                         int yEnd = y;
                         boolean endFound = true;
                         while (endFound) {
                             pixels[xEnd + width*yEnd] = (byte)0;  //
delete the point
                             //if(movie.getSize()<100)movie.addSlice
("part-cleaned", ip.duplicate());
                             endFound = false;
                             for (int d=0; d<8; d++)  
{               //analyze neighbors of the point
                                 if (isWithin(ip, xEnd, yEnd, d)) {
                                     v = pixels[xEnd + width*yEnd +  
dirOffset[d]]&255;
                                     //if(x>210&&x<215&&y==13)IJ.write
("x,y start="+x+","+y+": look at="+xEnd+","+yEnd+"+dir "+d+": v="+v);
                                     if (v>0 && isLineOrDot(ip, xEnd
+dirXoffset[d], yEnd+dirYoffset[d])==IS_LINE) {
                                         xEnd += dirXoffset[d];
                                         yEnd += dirYoffset[d];
                                         endFound = true;
                                         break;
                                     }
                                 }
                             } // for directions d
                         } // while (endFound)
                     } // if IS_LINE
                 } // if v<255 && v>0
             } // for x
         } // for y

     }

     /** analyze the neighbors of a pixel (x, y) in a byte image;  
pixels >0 are
      * considered part of lines.
      * @param ip
      * @param x
      * @param y
      * @return  IS_LINE if pixel is part of a line, IS_DOT if a  
single dot
      */
     int isLineOrDot(ImageProcessor ip, int x, int y) {
         int result = 0;
         int width = ip.getWidth();
         int height = ip.getHeight();
         byte[] pixels = (byte[])ip.getPixels();
         int offset = x + y*width;
         int whiteNeighbors = 0;             //counts neighbors that  
are not part of a line
         int countTransitions = 0;
         boolean pixelSet;
         boolean prevPixelSet = true;
         boolean firstPixelSet = true;       //initialize to make the  
compiler happy
         for (int d=0; d<8; d++) {           //walk around the point  
and note every no-line->line transition
             if (isWithin(ip, x, y, d)) {
                 pixelSet = (pixels[offset+dirOffset[d]]!=0);
                 if (!pixelSet) whiteNeighbors++;
             } else {
                 pixelSet = true;
             }
             if (pixelSet && !prevPixelSet)
                 countTransitions ++;
             prevPixelSet = pixelSet;
             if (d==0)
                 firstPixelSet = pixelSet;
         }
         if (firstPixelSet && !prevPixelSet)
             countTransitions ++;
             //if (x>=210&&x<=215 && y>=10 && y<=17)IJ.write("x,y="+x
+","+y+": transitions="+countTransitions);
         if (countTransitions==1 && whiteNeighbors>=5)
             result = IS_LINE;
         else if (whiteNeighbors==8)
             result = IS_DOT;
         return result;
     } //isLineEnd

     /** create an array of offsets within a pixel array for  
directions in clockwise order: 0=(x,y-1), 1=(x+1,y-1), ... 7=(x-1,y)
      * uses class variable width: width of the image where the  
pixels should be addressed
      * returns as class variables: the arrays of the offsets to the  
8 neighboring pixels
      */
     private void makeDirectionOffsets(ImageProcessor ip) {
         int width = ip.getWidth();
         int height = ip.getHeight();
         dirOffset = new int[] { -width, -width+1, +1, +width+1,  
+width, +width-1,   -1, -width-1 };
         dirXoffset = new int[] {    0,       1,     1,     1,        
0,      -1,      -1,    -1    };
         dirYoffset = new int[] {   -1,      -1,     0,     1,        
1,       1,       0,    -1,   };
     }

     /** returns whether the neighbor in a given direction is within  
the image
      * NOTE: it is assumed that the pixel x,y itself is within the  
image!
      * Uses class variables width, height: dimensions of the image
      * @param x         x-coordinate of the pixel that has a  
neighbor in the given direction
      * @param y         y-coordinate of the pixel that has a  
neighbor in the given direction
      * @param direction the direction from the pixel towards the  
neighbor (see makeDirectionOffsets)
      * @return          true if the neighbor is within the image  
(provided that x, y is within)
      */
     boolean isWithin(ImageProcessor ip, int x, int y, int direction) {
         int width = ip.getWidth();
         int height = ip.getHeight();
         int xmax = width - 1;
         int ymax = height -1;
         switch(direction) {
             case 0:
                 return (y>0);
             case 1:
                 return (x<xmax && y>0);
             case 2:
                 return (x<xmax);
             case 3:
                 return (x<xmax && y<ymax);
             case 4:
                 return (y<ymax);
             case 5:
                 return (x>0 && y<ymax);
             case 6:
                 return (x>0);
             case 7:
                 return (x>0 && y>0);
         }
         return false;   //to make the compiler happy :-)
     } // isWithin

}


________________________________________________________________

On 8 Mar 2007, at 22:54, Ru Wang wrote:

> Thank you, Thomas and Michael.
>
> I updated my ImageJ to the latest version and tried those plugins  
> written by
> Thomas. But those unconnected lines are still there. How to remove  
> them?
> The origianl image and the processed image are posted here :
> http://picasaweb.google.com/wangru.ustc/Image_ProcessAlbum
>
> BTW: What operations I have tried are : edge detection, contrast  
> enhancement,
> meadian filter,thresholding, skeletonize and dilite.
>
> Thanks a lot.
>
> Best regards,
>
> Ru
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Ru Wang
In reply to this post by Ru Wang
Thank you so much Michael and Jim.

I tried Michael's plugin just now. It works very well though there are still
some incompleted lines left.

Now I have another question. I want to output the edge information. That is  
to say, I would like to know which point touch which point and there
coornidinates. I am not sure if my meaning is clear.Let me try to put it
this way.
I would like to know the vertexs and their coordinates.And I also would like
to know the two end points of each line.

How can I make it? I don't know how this kind of images are stored.
Any comments are highly appreciated.


Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Michael Schmid
Hi Ru,

If you have an example where my plugin does not remove an
unconnected line (one that does not end in a small loop
or patch), please send it to me!
If there is a bug in the code it might also affect the
segmentation by the "Find Maxima" filter of ImageJ.

Below is an plugin that may get the junctions - it is not
necessarily perfect, it may depend on the skeletonize code
whether it works correctly.

There is a parameter to play with in isJunction:
         if (maxAdjacentPixelsSet > 3)
Maybe the threshold should be 2 or 4 for some types of
skeletonize codes.

Michael
________________________________________________________________

On 13 Mar 2007, at 22:55, Ru Wang wrote:

> Thank you so much Michael and Jim.
>
> I tried Michael's plugin just now. It works very well though there  
> are still
> some incompleted lines left.
>
> Now I have another question. I want to output the edge information.  
> That is
> to say, I would like to know which point touch which point and there
> coornidinates. I am not sure if my meaning is clear.Let me try to  
> put it
> this way.
> I would like to know the vertexs and their coordinates.And I also  
> would like
> to know the two end points of each line.
>
> How can I make it? I don't know how this kind of images are stored.
> Any comments are highly appreciated.
>
>

________________________________________________________________
import ij.*;
//import ij.gui.*;
//import ij.measure.*;
import ij.process.*;
//import java.awt.*;
//import java.util.*;
import ij.plugin.filter.*;

/** This ImageJ plug-in filter finds triple or multiple
junctions of lines from a skeletonized binary image with
background value 0 and lines having pixel values > 0
*/

public class Line_Junctions implements PlugInFilter {
     /**offsets of neighbor pixels for addressing*/
     int [] dirOffset, dirXoffset, dirYoffset;

     public int setup(String arg, ImagePlus imp) {
        return IJ.setupDialog(imp, DOES_8G);
     }
     public void run(ImageProcessor ip) {
     if (dirOffset==null) makeDirectionOffsets(ip);
         int width = ip.getWidth();
         int height = ip.getHeight();
         byte[] pixels =  (byte[])ip.getPixels();
         byte[] pixels2 = new byte[width*height];
         for (int y=0, i=0; y<height; y++) {
             for (int x=0; x<width; x++,i++) {
                 if (isJunction(ip, x, y)) pixels2[i] = -1;
             } // for x
         } // for y
         System.arraycopy(pixels2,0,pixels,0,width*height);
     }

     /** analyze the neighbors of a pixel (x, y) in a byte image;  
pixels >0 are
      * considered part of lines.
      * @param ip
      * @param x
      * @param y
      * @return  whether pixel x,y it is a triple or multiple juction
      */
     boolean isJunction(ImageProcessor ip, int x, int y) {
         int result = 0;
         int width = ip.getWidth();
         int height = ip.getHeight();
         byte[] pixels = (byte[])ip.getPixels();
         int offset = x + y*width;
         int countTransitions = 0;
         int adjacentPixelsSet = 0;
         int adjacentPixelsSetStart = 0;
         int maxAdjacentPixelsSet = 0;
         boolean pixelSet;
         boolean prevPixelSet = true;
         boolean firstPixelSet = true;       //initialize to make the  
compiler happy
         for (int d=0; d<8; d++) {           //walk around the point  
and note every no-line->line transition
             if (isWithin(ip, x, y, d)) {
                 pixelSet = (pixels[offset+dirOffset[d]]!=0);
             } else {
                 pixelSet = true;
             }
             if (!pixelSet)
                 adjacentPixelsSet = 0;
             if (pixelSet) {
                 if (d==adjacentPixelsSetStart)
                     adjacentPixelsSetStart++;
                 adjacentPixelsSet++;
                 if (maxAdjacentPixelsSet < adjacentPixelsSet)
                     maxAdjacentPixelsSet = adjacentPixelsSet;
             }
             if (pixelSet && !prevPixelSet)
                 countTransitions ++;
             prevPixelSet = pixelSet;
             if (d==0)
                 firstPixelSet = pixelSet;
         }
         if (firstPixelSet && !prevPixelSet)
             countTransitions ++;
         if (maxAdjacentPixelsSet < adjacentPixelsSet
+adjacentPixelsSetStart)
             maxAdjacentPixelsSet = adjacentPixelsSet
+adjacentPixelsSetStart;
         if (maxAdjacentPixelsSet > 3)
             countTransitions ++;    //correct for poorly  
skeletonized junctions
         return (countTransitions>2);
     }

     /** create an array of offsets within a pixel array for  
directions in clockwise order: 0=(x,y-1), 1=(x+1,y-1), ... 7=(x-1,y)
      * uses class variable width: width of the image where the  
pixels should be addressed
      * returns as class variables: the arrays of the offsets to the  
8 neighboring pixels
      */
     private void makeDirectionOffsets(ImageProcessor ip) {
         int width = ip.getWidth();
         int height = ip.getHeight();
         dirOffset = new int[] { -width, -width+1, +1, +width+1,  
+width, +width-1,   -1, -width-1 };
         dirXoffset = new int[] {    0,       1,     1,     1,        
0,      -1,      -1,    -1    };
         dirYoffset = new int[] {   -1,      -1,     0,     1,        
1,       1,       0,    -1,   };
     }

     /** returns whether the neighbor in a given direction is within  
the image
      * NOTE: it is assumed that the pixel x,y itself is within the  
image!
      * Uses class variables width, height: dimensions of the image
      * @param x         x-coordinate of the pixel that has a  
neighbor in the given direction
      * @param y         y-coordinate of the pixel that has a  
neighbor in the given direction
      * @param direction the direction from the pixel towards the  
neighbor (see makeDirectionOffsets)
      * @return          true if the neighbor is within the image  
(provided that x, y is within)
      */
     boolean isWithin(ImageProcessor ip, int x, int y, int direction) {
         int width = ip.getWidth();
         int height = ip.getHeight();
         int xmax = width - 1;
         int ymax = height -1;
         switch(direction) {
             case 0:
                 return (y>0);
             case 1:
                 return (x<xmax && y>0);
             case 2:
                 return (x<xmax);
             case 3:
                 return (x<xmax && y<ymax);
             case 4:
                 return (y<ymax);
             case 5:
                 return (x>0 && y<ymax);
             case 6:
                 return (x>0);
             case 7:
                 return (x>0 && y>0);
         }
         return false;   //to make the compiler happy :-)
     } // isWithin

}
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Gabriel Landini
On Wednesday 14 March 2007 17:56:28 Michael Schmid wrote:
> Below is an plugin that may get the junctions - it is not
> necessarily perfect, it may depend on the skeletonize code
> whether it works correctly.
>
> There is a parameter to play with in isJunction:
>          if (maxAdjacentPixelsSet > 3)
> Maybe the threshold should be 2 or 4 for some types of
> skeletonize codes.

The plugin BinaryConnectivity :

http://www.dentistry.bham.ac.uk/landinig/software/software.html

creates an image where binary pixels are labelled according to the number of
connected pixels (+1) to each foreground pixel (8 neighbours). E.g.
background = 0, single pixel = 1, end of a line = 2, double or middle of a
line points= 3, triple points = 4, etc. Brightness/Contrast must be adjusted
to see the result.

I hope it helps.

G.
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Ru Wang
In reply to this post by Ru Wang
Thanks. I am testing the two plugins. And I will post my result status here.
For the removing unconnected lines example, I have pasted it here :
http://picasaweb.google.com/wangru.ustc/Image_ProcessAlbum
The first one is the origin grain image. The second is the edge detection
result achieved by ImageJ. The last one is the final result after I used the
Michael's plugin. It is seen that there are still some unconnected lines left.


Thanks.

Ru
Reply | Threaded
Open this post in threaded view
|

Re: edge detection

Michael Schmid
Hi Ru,

if I take your image #2, convert it to grayscale, threshold it to
get rid of the JPEG artifacts and run my plugin I get no loose
ends. If I don't threshold, I get the same result as you had.

So it seems that you had pixel values that were looking like
background, but are actually not 0 but 1 - possibly due to the
artifacts when storing an image as JPEG.

Michael
________________________________________________________________

On 19 Mar 2007, at 17:44, Ru Wang wrote:

> Thanks. I am testing the two plugins. And I will post my result  
> status here.
> For the removing unconnected lines example, I have pasted it here :
> http://picasaweb.google.com/wangru.ustc/Image_ProcessAlbum
> The first one is the origin grain image. The second is the edge  
> detection
> result achieved by ImageJ. The last one is the final result after I  
> used the
> Michael's plugin. It is seen that there are still some unconnected  
> lines left.
>
>
> Thanks.
>
> Ru