Doese ImageJ contains Hill Shade ?

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

Doese ImageJ contains Hill Shade ?

Feras
Hi All,

I just wonder if imageJ contains Hillshade functionality ? If so how can I find it?

Thanks,
Feras

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
Reply | Threaded
Open this post in threaded view
|

Re: Doese ImageJ contains Hill Shade ?

Michael Schmid
Hi Feras,

there is no hillshade plugin that I am aware of.

It would be simple to implement a very basic one; see e.g.
http://edndoc.esri.com/arcobjects/9.2/net/shared/geoprocessing/spatial_analyst_tools/how_hillshade_works.htm

A rough approximation is the gradient. This can be calculated by convolution with a simple 3x3 matrix: You have to convert the image to float first (Image>Type>32 bit). E.g. convolving (Image>Process>Filters>Convolve) with something like the following looks like illumination from the left:

-0.25 0 0.25
-0.5  0 0.5
-0.25 0 0.25

Process>Shadows calculates the sum of the image and the gradient.

Michael
________________________________________________________________
On Oct 10, 2014, at 18:41, Feras wrote:

> Hi All,
>
> I just wonder if imageJ contains Hillshade functionality ? If so how can I find it?
>
> Thanks,
> Feras

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
Reply | Threaded
Open this post in threaded view
|

Re: Doese ImageJ contains Hill Shade ?

Michael Schmid
In reply to this post by Feras
Hi Feras,

as I like the idea of having it, I have quickly written a Hill_Shade (shaded relief) plugin; I have pasted it to the end of this mail.
Mind possible line breaks introduced by the mailer; all code between 'public class' and the last closing braces should be indented. If it isn't, join it with the end of the previous line.

One major limitation so far: It does not handle edge pixels yet; these remain black.

It has a very basic 'preview' functionality which puts the result as an overlay into the current image (unclicking 'preview' does not remove it); and it will create a new image if you press 'OK'.

I found that I had to use a nonlinear transfer function (seemingly not present in the standard hill shade algorithm) to get both gentle slopes and details of steep slopes visible in the same image. You can adjust it or disable it with "nonlinear contrast = 0". With "nonlinear contrast = 2", it's better than anything I can get by simply calculating the slope and manipulating it, so having a 'true' hill shade algorithm is certainly worth while!

If you have a nice color lookup table for the original image with the height levels, you can convert it to RGB and blend it with the shaded image, to get an image that looks like a topographic map (a sample image showing the surroundings of the highest mountain in Austria is attached).

Michael
________________________________________________________________
On Oct 10, 2014, at 18:41, Feras wrote:

> Hi All,
>
> I just wonder if imageJ contains Hillshade functionality ? If so how can I find it?
>
> Thanks,
> Feras
>
> --
> ImageJ mailing list: http://imagej.nih.gov/ij/list.html

// ---------------------------- S N I P ----------------------------

import ij.*;
import ij.process.*;
import ij.plugin.filter.ExtendedPlugInFilter;
import ij.plugin.filter.PlugInFilterRunner;
import ij.gui.*;
import java.awt.*;

/** Hill_Shade
 *  Produces relief shading similar to topographic maps
 *
 * V 1.0  Michael Schmid 2014-11-18 Restriction: Does not handle edge pixels
 */

public class Hill_Shade implements ExtendedPlugInFilter, DialogListener {
    private static int FLAGS =      //bitwise or of the following flags:
            DOES_8G | DOES_16 | DOES_32 | //data types handled
            NO_CHANGES;             //it leaves the original untouched
    //remember these settings within an ImageJ session
    private static double xPixelSizeS = 90;
    private static double yPixelSizeS = 90;
    private static double elevationS = 45;
    private static double azimuthS = 315;
    private static double nonlinContrastS = 2;
    //parameters
    private double xPixelSize;
    private double yPixelSize;
    private double elevation;
    private double azimuth;
    private double nonlinContrast;
    //internal
    boolean previewing;
    ImagePlus imp;

    /**
     * This method is called by ImageJ for initialization.
     * @param arg Unused here. For plugins in a .jar file this argument string can
     *            be specified in the plugins.config file of the .jar archive.
     * @param imp The ImagePlus containing the image (or stack) to process.
     * @return    The method returns flags (i.e., a bit mask) specifying the
     *            capabilities (supported formats, etc.) and needs of the filter.
     *            See PlugInFilter.java and ExtendedPlugInFilter in the ImageJ
     *            sources for details.
     */
    public int setup (String arg, ImagePlus imp) {
        return FLAGS;
    }

    /** Ask the user for the parameters. This method of an ExtendedPlugInFilter
     *  is called by ImageJ after setup.
     * @param imp       The ImagePlus containing the image (or stack) to process.
     * @param command   The ImageJ command (as it appears the menu) that has invoked this filter
     * @param pfr       A reference to the PlugInFilterRunner, needed for preview
     * @return          Flags, i.e. a code describing supported formats etc.
     */
    public int showDialog (ImagePlus imp, String command, PlugInFilterRunner pfr) {
        this.imp = imp;
        Overlay ovly = imp.getOverlay();
        GenericDialog gd = new GenericDialog(command+"...");
        gd.addNumericField("x Pixel Size", xPixelSizeS, 1, 8, "(height units)");
        gd.addNumericField("y Pixel Size", yPixelSizeS, 1, 8, "(height units)");
        gd.addNumericField("Elevation of Sun", elevationS, 0, 8, "\u00b0");
        gd.addNumericField("Azimuth of Sun", azimuthS, 0, 8, "\u00b0");
        gd.addNumericField("Nonlinear Contrast", nonlinContrastS, 0, 8, "");
        gd.addPreviewCheckbox(pfr);
        gd.addDialogListener(this);
        previewing = true;
        gd.showDialog();           // user input (or reading from macro) happens now
        previewing = false;
        imp.setOverlay(ovly);
        if (gd.wasCanceled())      // dialog cancelled?
            return DONE;
        xPixelSizeS = xPixelSize;
        yPixelSizeS = yPixelSize;
        elevationS = elevation;
        azimuthS = azimuth;
        return FLAGS;              // makes the user process the slice
    }

    /** Listener to modifications of the input fields of the dialog.
     *  Here the parameters should be read from the input dialog.
     *  @param gd The GenericDialog that the input belongs to
     *  @param e  The input event
     *  @return whether the input is valid and the filter may be run with these parameters
     */
    public boolean dialogItemChanged (GenericDialog gd, AWTEvent e) {
        xPixelSize = gd.getNextNumber();
        yPixelSize = gd.getNextNumber();
        elevation = gd.getNextNumber();
        azimuth = gd.getNextNumber();
        nonlinContrast = gd.getNextNumber();
        return !gd.invalidNumber() && xPixelSize>0 && yPixelSize>0;
    }

    /**
     * This method is called by ImageJ for processing
     * @param ip The image that should be processed
     */
    public void run (ImageProcessor ip) {
        ByteProcessor bp = makeHillshade(ip, xPixelSize, yPixelSize, elevation, azimuth);
        if (previewing) {
            if (!Thread.currentThread().isInterrupted())
                imp.setOverlay(new Overlay(new ImageRoi(0,0,bp)));
        } else
            new ImagePlus(imp.getTitle()+"_shaded", bp).show();
    }

    /** And here we create the actual hillshade */
    private ByteProcessor makeHillshade(ImageProcessor ip, double xPixelSize, double yPixelSize,
            double elevation, double azimuth) {
        int width = ip.getWidth();
        int height = ip.getHeight();
        ByteProcessor bp = new ByteProcessor(width, height);
        elevation *= Math.PI/180;   //to radians
        azimuth  *= Math.PI/180;
        for (int y=1; y<height-1; y++)
            for (int x=1; x<width-1; x++) {
                float a = ip.getPixelValue(x-1, y-1);
                float b = ip.getPixelValue(x, y-1);
                float c = ip.getPixelValue(x+1, y-1);
                float d = ip.getPixelValue(x-1, y);
                float f = ip.getPixelValue(x+1, y);
                float g = ip.getPixelValue(x-1, y+1);
                float h = ip.getPixelValue(x, y+1);
                float i = ip.getPixelValue(x+1, y+1);
                double xSlope = ((a+2*d+g)-(c+2*f+i))/(8*xPixelSize);
                double ySlope = ((a+2*b+c)-(g+2*h+i)) /(8*yPixelSize);
                double slopeAngle = Math.atan(Math.sqrt(xSlope*xSlope + ySlope*ySlope));
                double aspect = xSlope != 0 ?
                        Math.atan2(ySlope, -xSlope) :
                        (ySlope > 0 ? 0.5 : -0.5)*Math.PI;
                double hillshade = (Math.sin(elevation) * Math.cos(slopeAngle)) +
                        (Math.cos(elevation) * Math.sin(slopeAngle) * Math.cos(azimuth-aspect));
                hillshade = hillshade * hillshade;  //puts the 'neutral point' to 0.5
                if (nonlinContrast > 1e-5) {
                    double sign = hillshade > 0.5 ? +2.0 : -2.0;
                    double tmp = sign*(hillshade - 0.5); //0...1 range
                    tmp = Math.log(1./nonlinContrast + tmp);
                    double min = Math.log(1./nonlinContrast);
                    double max = Math.log(1./nonlinContrast + 1);
                    tmp = (tmp - min)/(max - min);
                    hillshade = 0.5 + 0.25*tmp*sign;
                }
                hillshade *= 255.0;
                bp.putPixelValue(x,y, hillshade);
            }
        return bp;
    }

    /** Set the number of calls of the run(ip) method. This information is
     *  needed for displaying a progress bar; unused here.
     */
    public void setNPasses (int nPasses) {}

}

// ---------------------------- S N A P ----------------------------

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html

shadedExample.jpg (265K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Doese ImageJ contains Hill Shade ?

Michael Schmid
Hi Feras & everyone,

an updated version of the Hill_Shade plugin is available at
  http://imagejdocu.tudor.lu/doku.php?id=plugin:filter:hill_shade:start

Michael

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html