Circle Finder

File

Circles are a notable class of feature in many images. This ImageJ macro provides a filter which assists in finding circles of a particular size (either hollow or filled) in an image. After filtering the centres of circles are bright spots.

Circle_Finder.ijm

//Circle finder
//This macro lets you automatically find circles in an image.
//It performs a set of image filters which convert regions in the
//image which are similar to a circle of the expected size into
//bright spots at the centre of the circle.
//Combine with a "Find Maxima..." search to identify circle centres.

//Global variables
var radius=10;
var inverted=false;
var filled=true;
var slices=true;
var diameter=2*round(radius)+1;

//The main macro
macro "Find Circles" {
    //Create and show the user input dialog
    Dialog.create("Find Circles");
        //Expected circle radius to search for
        //Diameter is derived from 2*round(radius)+1 (diameter must be odd)
        Dialog.addNumber("Expected radius: ", radius, 0, 5, "px");
        //Whether or not to invert the image for analysis
        //The macro searches for light circles on a dark background
        Dialog.addCheckbox("Invert image", inverted);
        //Whether or not to search for filled circles
        //The macro will find filled or hollow circles, but specifying it to expect hollow circles improves the results
        Dialog.addCheckbox("Find filled circles", filled);
        //Whether or not to process all slices
        if (nSlices()!=1) {
            Dialog.addCheckbox("Process all slices", slices);
        }
    Dialog.show();
        radius=Dialog.getNumber();
        inverted=Dialog.getCheckbox();
        filled=Dialog.getCheckbox();
        if (nSlices()!=1) {
            slices=Dialog.getCheckbox();
        }
    //Derive the diameter to search for
    diameter=2*round(radius)+1;

    snapshot();
    //If RGB then convert to 8-bit for analysis
    if (bitDepth()==24) {
        run("8-bit");
    }
    //Pick a good value for maximum image value for image inversion
    if (bitDepth()==32) {
        max=0;
    } else {
        max=pow(2, bitDepth())-1;
    }

    //Derive the slice range to analyse
    if (nSlices==1) {
        startSlice=1;
        endSlice=1;
    } else {
        if (slices==true) {
            startSlice=1;
            endSlice=nSlices();
        } else {
            startSlice=getSliceNumber();
            endSlice=getSliceNumber();
        }
    }

    for (i=startSlice; i<=endSlice; i++) {
        setSlice(i);
        //Invert (if selected)
        if (inverted==true) {
            run("Macro...", "code=[v="+max+"-v] slice");
        }
        //Find edges in the image prior to analysis (if optimised to also find filled circles)
        if (filled==true) {
            run("Find Edges", "slice");
        }
        //Find circles in the image
        circleKernel(diameter);            
    }
}

//Function to generate and apply a circle shaped kernel
//The diameter must be odd and greater than zero, very large diameters are slow
function circleKernel(diameter) {
    //Make an image to generate the kernel pattern in
    newImage("Kernel", "8-bit Black", diameter, diameter, 1);
    //And make a hollow circle 1px in thickness with a value of one
    makeOval(0, 0, diameter, diameter);
    setColor(1);
    fill();
    makeOval(1, 1, diameter-2, diameter-2);
    setColor(0);
    fill();
    run("Select None");
    setMinAndMax(0, 1);

    //Record the kernel by reading the image values to a 2D array
    kernel=newArray(diameter*diameter);
    kernelstring="";
    for (x=0; x<diameter; x++) {
        for (y=0; y<diameter; y++) {
            kernel[x+y*diameter]=getPixel(x, y);
            kernelstring+=""+getPixel(x, y)+" ";
        }
        kernelstring+="\n";
    }
    //And close the kernel image
    close();

    //Apply the kernel to the image
    run("Convolve...", "text1=["+kernelstring+"] normalize slice");
}

ImageJ Macros Gallery