001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 *  image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause 
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 
006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 
007 * Visit http://imagingbook.com for additional details.
008 *******************************************************************************/
009
010package imagingbook.pub.color.edge;
011
012import ij.process.ColorProcessor;
013import ij.process.FloatProcessor;
014import imagingbook.lib.math.Matrix;
015import imagingbook.lib.math.VectorNorm;
016import imagingbook.lib.math.VectorNorm.NormType;
017
018/**
019 * Monochromatic color edge detector, as described in UTICS Vol. 3, Alg. 4.1.
020 * @author W. Burger
021 * @version 2014/02/16
022 */
023public class MonochromaticEdgeDetector extends ColorEdgeDetector {
024        
025
026        final ColorProcessor I;
027        final int M;    // image width
028        final int N;    // image height
029        final Parameters params;
030        
031        private FloatProcessor Emag;    // edge magnitude map
032        private FloatProcessor Eort;    // edge orientation map
033
034        public static class Parameters {
035                /** Specify which color distance to use */
036                public NormType norm = NormType.L2;
037        }
038        
039        
040        // Sobel-kernels for x/y-derivatives:
041    final float[] HxS = Matrix.multiply(1.0f/8, new float[] {
042                        -1, 0, 1,
043                    -2, 0, 2,
044                    -1, 0, 1
045                    });
046    
047    final float[] HyS = Matrix.multiply(1.0f/8, new float[] {
048                        -1, -2, -1,
049                         0,  0,  0,
050                         1,  2,  1
051                         });
052    
053    final int R = 0, G = 1, B = 2;              // RGB channel indexes
054        
055    FloatProcessor[] Ix;
056    FloatProcessor[] Iy;
057 
058        public MonochromaticEdgeDetector(ColorProcessor I) {
059                this(I, new Parameters());
060        }
061        
062        public MonochromaticEdgeDetector(ColorProcessor I, Parameters params) {
063                this.params = params;
064                this.I = I;
065                this.M = this.I.getWidth();
066                this.N = this.I.getHeight();
067                setup();
068                findEdges();
069        }
070        
071        protected void setup() {
072                Emag = new FloatProcessor(M, N);
073                Eort = new FloatProcessor(M, N);
074                Ix = new FloatProcessor[3];
075                Iy = new FloatProcessor[3];
076        }
077
078        void findEdges() {
079                for (int c = R; c <= B; c++) {
080                        Ix[c] =  getRgbFloatChannel(I, c);
081                        Iy[c] =  getRgbFloatChannel(I, c);
082                        Ix[c].convolve(HxS, 3, 3);
083                        Iy[c].convolve(HyS, 3, 3);
084                }
085                
086                //VectorNorm vNorm = VectorNorm.create(params.norm);
087                VectorNorm vNorm = params.norm.create();
088                for (int v = 0; v < N; v++) {
089                        for (int u = 0; u < M; u++) {
090                                // extract the gradients of the R, G, B channels:
091                                final float Rx = Ix[R].getf(u, v);      float Ry = Iy[R].getf(u, v);
092                                final float Gx = Ix[G].getf(u, v);      float Gy = Iy[G].getf(u, v);
093                                final float Bx = Ix[B].getf(u, v);      float By = Iy[B].getf(u, v);
094                                
095                                final float Er2 = Rx * Rx + Ry * Ry;
096                                final float Eg2 = Gx * Gx + Gy * Gy;
097                                final float Eb2 = Bx * Bx + By * By;
098                                
099                                // calculate local edge magnitude:
100                                double[] Ergb = {Math.sqrt(Er2), Math.sqrt(Eg2), Math.sqrt(Eb2)};
101                                float eMag = (float) vNorm.magnitude(Ergb);
102//                              float eMag = (float) Math.sqrt(Er2 + Eg2 + Eb2);        // special case of L2 norm
103                                Emag.setf(u, v, eMag);  
104                                
105                                // find the maximum gradient channel:
106                                float e2max = Er2, cx = Rx, cy = Ry;
107                                if (Eg2 > e2max) {
108                                        e2max = Eg2; cx = Gx; cy = Gy;
109                                }
110                                if (Eb2 > e2max) {
111                                        e2max = Eb2; cx = Bx; cy = By;
112                                }
113                                
114                                // calculate edge orientation for the maximum channel:
115                                float eOrt = (float) Math.atan2(cy, cx);
116                                Eort.setf(u, v, eOrt);
117                        }
118                }
119        }
120
121        
122//      float mag(float er2, float eg2, float eb2, ColorDistanceNorm norm) {
123//              double dist = 0;
124//              switch (norm) {
125//                      case L1 : dist = Math.sqrt(er2) + Math.sqrt(eg2) + Math.sqrt(eb2); break;
126//                      case L2 : dist = Math.sqrt(er2*er2 + eg2*eg2 + eb2*eb2); break;
127//                      case Lmax : dist = Math.sqrt(Math.max(er2, (Math.max(eg2, eb2)))); break;
128//              }
129//              return (float) dist;
130//      }
131        
132        public FloatProcessor getEdgeMagnitude() {
133                return Emag;
134        }
135
136        public FloatProcessor getEdgeOrientation() {
137                return Eort;
138        }
139        
140}