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}