Coverage Report - org.geotoolkit.gui.swing.image.GradientKernelEditor
 
Classes in this File Line Coverage Branch Coverage Complexity
GradientKernelEditor
88 %
30/34
N/A
1,556
GradientKernelEditor$Editor
100 %
42/42
100 %
10/10
1,556
 
 1  
 /*
 2  
  *    Geotoolkit.org - An Open Source Java GIS Toolkit
 3  
  *    http://www.geotoolkit.org
 4  
  *
 5  
  *    (C) 2003-2012, Open Source Geospatial Foundation (OSGeo)
 6  
  *    (C) 2009-2012, Geomatys
 7  
  *
 8  
  *    This library is free software; you can redistribute it and/or
 9  
  *    modify it under the terms of the GNU Lesser General Public
 10  
  *    License as published by the Free Software Foundation;
 11  
  *    version 2.1 of the License.
 12  
  *
 13  
  *    This library is distributed in the hope that it will be useful,
 14  
  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  
  *    Lesser General Public License for more details.
 17  
  */
 18  
 package org.geotoolkit.gui.swing.image;
 19  
 
 20  
 import java.text.ParseException;
 21  
 import javax.swing.JLabel;
 22  
 import javax.swing.JComponent;
 23  
 import javax.swing.BorderFactory;
 24  
 import javax.swing.border.Border;
 25  
 import java.awt.GridBagConstraints;
 26  
 import java.awt.GridBagLayout;
 27  
 import java.awt.Component;
 28  
 
 29  
 import javax.media.jai.KernelJAI;
 30  
 import javax.media.jai.operator.GradientMagnitudeDescriptor;
 31  
 
 32  
 import org.geotoolkit.gui.swing.Dialog;
 33  
 import org.geotoolkit.resources.Vocabulary;
 34  
 import org.geotoolkit.internal.swing.SwingUtilities;
 35  
 
 36  
 import static java.awt.GridBagConstraints.*;
 37  
 import static org.geotoolkit.math.XMath.SQRT2;
 38  
 
 39  
 
 40  
 /**
 41  
  * A widget for editing the horizontal and vertical kernels for a
 42  
  * {@linkplain GradientMagnitudeDescriptor gradient magnitude} operation.
 43  
  * This widget combine two {@link KernelEditor} side-by-side: one for the
 44  
  * horizontal component and one for the vertical component.
 45  
  *
 46  
  * <table cellspacing="24" cellpadding="12" align="center"><tr valign="top"><td>
 47  
  * <img src="doc-files/GradientKernelEditor.png">
 48  
  * </td><td width="500" bgcolor="lightblue">
 49  
  * {@section Demo}
 50  
  * The image on the left side gives an example of this widget appearance.
 51  
  * To try this component in your browser, see the
 52  
  * <a href="http://www.geotoolkit.org/demos/geotk-simples/applet/GradientKernelEditor.html">demonstration applet</a>.
 53  
  * </td></tr></table>
 54  
  *
 55  
  * @author Martin Desruisseaux (IRD)
 56  
  * @version 3.00
 57  
  *
 58  
  * @see KernelEditor
 59  
  * @see GradientMagnitudeDescriptor
 60  
  * @see org.geotoolkit.coverage.processing.operation.GradientMagnitude
 61  
  *
 62  
  * @since 2.3
 63  
  * @module
 64  
  */
 65  
 @SuppressWarnings("serial")
 66  
 public class GradientKernelEditor extends JComponent implements Dialog {
 67  
     /**
 68  
      * Horizontal gradient mask according Prewitt (also know as smoothed).
 69  
      */
 70  1
     public static final KernelJAI PREWITT_HORIZONTAL = new KernelJAI(3,3,new float[] {
 71  
         -1,  0,  1,
 72  
         -1,  0,  1,
 73  
         -1,  0,  1,
 74  
     });
 75  
 
 76  
     /**
 77  
      * Vertical gradient mask according Prewitt (also know as smoothed).
 78  
      */
 79  1
     public static final KernelJAI PREWITT_VERTICAL = new KernelJAI(3,3,new float[] {
 80  
         -1, -1, -1,
 81  
          0,  0,  0,
 82  
          1,  1,  1,
 83  
     });
 84  
 
 85  
     /**
 86  
      * Horizontal gradient mask (isotropic).
 87  
      */
 88  1
     public static final KernelJAI ISOTROPIC_HORIZONTAL = new KernelJAI(3,3,new float[] {
 89  
                 -1,      0,          1,
 90  
         (float) -SQRT2,  0,  (float) SQRT2,
 91  
                 -1,      0,          1,
 92  
     });
 93  
 
 94  
     /**
 95  
      * Vertical gradient mask (isotropic).
 96  
      */
 97  1
     public static final KernelJAI ISOTROPIC_VERTICAL = new KernelJAI(3,3,new float[] {
 98  
         -1,   (float) -SQRT2,   -1,
 99  
          0,            0,        0,
 100  
          1,   (float)  SQRT2,    1,
 101  
     });
 102  
 
 103  
     /**
 104  
      * Horizontal gradient mask according Sobel.
 105  
      */
 106  1
     public static final KernelJAI SOBEL_HORIZONTAL = KernelJAI.GRADIENT_MASK_SOBEL_HORIZONTAL;
 107  
 
 108  
     /**
 109  
      * Vertical gradient mask according Sobel.
 110  
      */
 111  1
     public static final KernelJAI SOBEL_VERTICAL = KernelJAI.GRADIENT_MASK_SOBEL_VERTICAL;
 112  
     /*
 113  
      * NOTE: Sobel masks were interchanged in KernelJAI prior and up to JAI 1.1.2-rc.
 114  
      *       See for example J.J. Simpson (1990) in Remote sensing environment, 33:17-33.
 115  
      *       This bug was fixed in JAI 1.1.2 final.
 116  
      */
 117  
 
 118  
     /**
 119  
      * Horizontal gradient mask according Kirsch.
 120  
      */
 121  1
     public static final KernelJAI KIRSCH_HORIZONTAL = new KernelJAI(3,3,new float[] {
 122  
         -3, -3,  5,
 123  
         -3,  0,  5,
 124  
         -3, -3,  5,
 125  
     });
 126  
 
 127  
     /**
 128  
      * Vertical gradient mask according Kirsch.
 129  
      *
 130  
      * @todo Why positives numbers are on the first row? This is the opposite
 131  
      *       of other vertical gradient masks. Need to verify in J.J. Simpson (1990).
 132  
      */
 133  1
     public static final KernelJAI KIRSCH_VERTICAL = new KernelJAI(3,3,new float[] {
 134  
          5,  5,  5,
 135  
         -3,  0, -3,
 136  
         -3, -3, -3,
 137  
     });
 138  
 
 139  
     /**
 140  
      * The horizontal kernel editor.
 141  
      */
 142  1
     private final KernelEditor kernelH = new Editor(true);
 143  
 
 144  
     /**
 145  
      * The vertical kernel editor.
 146  
      */
 147  1
     private final KernelEditor kernelV = new Editor(false);
 148  
 
 149  
     /**
 150  
      * Constructs a new editor for gradient kernels.
 151  
      */
 152  1
     public GradientKernelEditor() {
 153  1
         setLayout(new GridBagLayout());
 154  1
         final Vocabulary resources = Vocabulary.getResources(getDefaultLocale());
 155  1
         final Border border = BorderFactory.createCompoundBorder(
 156  
                               BorderFactory.createLoweredBevelBorder(),
 157  
                               BorderFactory.createEmptyBorder(3,3,3,3));
 158  1
         kernelH.setBorder(border);
 159  1
         kernelV.setBorder(border);
 160  
         final JLabel labelH, labelV;
 161  1
         labelH = new JLabel(resources.getString(Vocabulary.Keys.HORIZONTAL_COMPONENT), JLabel.CENTER);
 162  1
         labelV = new JLabel(resources.getString(Vocabulary.Keys.VERTICAL_COMPONENT),   JLabel.CENTER);
 163  1
         final GridBagConstraints c = new GridBagConstraints();
 164  
 
 165  1
         c.insets.top = 6;
 166  1
         c.gridy=0; c.weightx=1; c.gridwidth=1; c.gridheight=1; c.fill=BOTH;
 167  1
         c.gridx=0; add(labelH, c);
 168  1
         c.gridx=1; add(labelV, c);
 169  1
         c.gridy=1; c.weighty=1; c.insets.bottom = 6;
 170  1
         c.gridx=0; c.insets.left=6; c.insets.right=3; add(kernelH, c);
 171  1
         c.gridx=1; c.insets.left=3; c.insets.right=6; add(kernelV, c);
 172  1
     }
 173  
 
 174  
     /**
 175  
      * Adds a set of predefined kernels. This convenience method invokes {@link
 176  
      * KernelEditor#addDefaultKernels()} on both {@linkplain #getHorizontalEditor
 177  
      * horizontal} and {@linkplain #getVerticalEditor vertical} kernel editors.
 178  
      * The default implementation for those editors will add a set of Sobel kernels.
 179  
      */
 180  
     public void addDefaultKernels() {
 181  1
         kernelH.addDefaultKernels();
 182  1
         kernelV.addDefaultKernels();
 183  1
     }
 184  
 
 185  
     /**
 186  
      * Returns the horizontal kernel editor.
 187  
      *
 188  
      * @return The horizontal kernel editor.
 189  
      */
 190  
     public KernelEditor getHorizontalEditor() {
 191  0
         return kernelH;
 192  
     }
 193  
 
 194  
     /**
 195  
      * Returns the vertical kernel editor.
 196  
      *
 197  
      * @return The vertical kernel editor.
 198  
      */
 199  
     public KernelEditor getVerticalEditor() {
 200  0
         return kernelV;
 201  
     }
 202  
 
 203  
     /**
 204  
      * A kernel editor for horizontal or vertical gradient kernel.
 205  
      *
 206  
      * @author Martin Desruisseaux (IRD)
 207  
      * @version 3.00
 208  
      *
 209  
      * @since 2.3
 210  
      * @module
 211  
      */
 212  
     @SuppressWarnings("serial")
 213  
     private static final class Editor extends KernelEditor {
 214  
         /**
 215  
          * {@code true} if this editor is for the horizontal component,
 216  
          * or {@code false} for the vertical component.
 217  
          */
 218  
         private final boolean horizontal;
 219  
 
 220  
         /**
 221  
          * Construct a new kernel editor for the specified component.
 222  
          */
 223  
         public Editor(final boolean horizontal) {
 224  2
             super();
 225  2
             this.horizontal = horizontal;
 226  2
         }
 227  
 
 228  
         /**
 229  
          * Add a set of predefined kernels.
 230  
          */
 231  
         @Override
 232  
         public void addDefaultKernels() {
 233  2
             final String GRADIENT_MASKS = getResources().getString(Vocabulary.Keys.GRADIENT_MASKS);
 234  
             final KernelJAI prewitt, isotropic, kirsch, sobel;
 235  2
             if (horizontal) {
 236  1
                 prewitt   = PREWITT_HORIZONTAL;
 237  1
                 isotropic = ISOTROPIC_HORIZONTAL;
 238  1
                 kirsch    = KIRSCH_HORIZONTAL;
 239  1
                 sobel     = SOBEL_HORIZONTAL;
 240  
             } else {
 241  1
                 prewitt   = PREWITT_VERTICAL;
 242  1
                 isotropic = ISOTROPIC_VERTICAL;
 243  1
                 kirsch    = KIRSCH_VERTICAL;
 244  1
                 sobel     = SOBEL_VERTICAL;
 245  
             }
 246  2
             addKernel(GRADIENT_MASKS, "Prewitt",        prewitt);
 247  2
             addKernel(GRADIENT_MASKS, "Isotropic",      isotropic);
 248  2
             addKernel(GRADIENT_MASKS, "Kirsch",         kirsch);
 249  2
             addKernel(GRADIENT_MASKS, "Sobel 3\u00D73", sobel);
 250  2
             final StringBuffer buffer = new StringBuffer("Sobel ");
 251  2
             final int base = buffer.length();
 252  14
             for (int i=5; i<=15; i+=2) {
 253  12
                 buffer.setLength(base);
 254  12
                 buffer.append(i).append('\u00D7').append(i);
 255  12
                 addKernel(GRADIENT_MASKS, buffer.toString(), getSobel(i, horizontal));
 256  
             }
 257  2
             setKernel(sobel);
 258  2
         }
 259  
 
 260  
         /**
 261  
          * Retourne une extension de l'opérateur de Sobel. Pour chaque élément dont la position
 262  
          * par rapport à l'élément central est (x,y), on calcule la composante horizontale avec
 263  
          * le cosinus de l'angle divisé par la distance. On peut l'écrire comme suit:
 264  
          *
 265  
          * {@preformat math
 266  
          *     cos(atan(y/x)) / sqrt(x²+y²)
 267  
          * }
 268  
          *
 269  
          * En utilisant l'identité 1/cos² = (1+tan²), on peut réécrire l'équation comme suit:
 270  
          *
 271  
          * {@preformat math
 272  
          *     x / (x²+y²)
 273  
          * }
 274  
          *
 275  
          * @param size Taille de la matrice. Doit être un nombre positif et impair.
 276  
          * @param horizontal {@code true} pour l'opérateur horizontal,
 277  
          *        ou {@code false} pour l'opérateur vertical.
 278  
          */
 279  
         private static KernelJAI getSobel(final int size, final boolean horizontal) {
 280  12
             final int key = size/2;
 281  12
             final float[] data = new float[size*size];
 282  78
             for (int y=key; y>=0; y--) {
 283  66
                 int row1 = (key-y)*size + key;
 284  66
                 int row2 = (key+y)*size + key;
 285  66
                 final int y2 = y*y;
 286  398
                 for (int x=key; x!=0; x--) {
 287  332
                     final int x2 = x*x;
 288  332
                     final float v = (float) (2.0*x / (x2+y2));
 289  332
                     if (horizontal) {
 290  166
                         data[row1-x] = data[row2-x] = -v;
 291  166
                         data[row1+x] = data[row2+x] = +v;
 292  
                     } else {
 293  
                         // Swap x and y.
 294  166
                         row1 = (key-x)*size + key;
 295  166
                         row2 = (key+x)*size + key;
 296  166
                         data[row1-y] = data[row1+y] = -v;
 297  166
                         data[row2-y] = data[row2+y] = +v;
 298  
                     }
 299  
                 }
 300  
             }
 301  12
             return new KernelJAI(size, size, key, key, data);
 302  
         }
 303  
     }
 304  
 
 305  
     /**
 306  
      * {@inheritDoc}
 307  
      *
 308  
      * @since 3.12
 309  
      */
 310  
     @Override
 311  
     public void commitEdit() throws ParseException {
 312  0
     }
 313  
 
 314  
     /**
 315  
      * {@inheritDoc}
 316  
      */
 317  
     @Override
 318  
     public boolean showDialog(final Component owner, final String title) {
 319  0
         return SwingUtilities.showDialog(owner, this, title);
 320  
     }
 321  
 }