/* * Gray8CannyVert.java * * Created on August 27, 2006, 4:32, PM * * To change this templatef, choose Tools | Template Manager * and open the template in the editor. * * Copyright 2007 by Jon A. Webb * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package jjil.algorithm; import jjil.core.Error; import jjil.core.Gray8Image; import jjil.core.Image; import jjil.core.PipelineStage; /** * Computes the vertical Canny operator for an input gray image. The sigma * value for the operator is set in the constructor or setSigma. All calculations * are done in integer (per CLDC 1.0) and the sigma value is specified as * multiplied by 10.0. The minimum value for the unmultiplied sigma is 0.1; * the maximum value is about 10.0. Larger sigma values give an operator which * is less sensitive to high frequencies and more sensitive to low frequencies. * <p> * @author webb */ public class Gray8CannyVert extends PipelineStage { /** cSigma is the sigma value we'll be using. It has been multiplied * by 10.0 and converted to integer because CLDC 1.0 doesn't allow * floating point. The minimum legal value for cSigma is 1; the maximum * is given by the length of nCoeff below. */ private int cSigma; /** nCoeff row i is the precomputed Canny coefficients for sigma = i/10.0. * They have been scaled by 256 and converted to integer because CLDC 1.0 * doesn't allow floating point. The coefficients have been scaled and * normalized so the sum is 0 and the sum of the absolute values is * 256 (counting both sides of the Canny operator -- the operator is * symmetric so we just give one side below.) The number of coefficients * was determined by generating them out to the point at which the * unscaled coefficient was less than a threshold, here 0.05. */ private int[][] nCoeff = { {0}, {-127, 21, 21, 21}, {-127, 21, 21, 21}, {-127, 25, 19, 19}, {-128, 42, 10, 10}, {-127, 56, 3, 3}, {-128, 57, 5}, {-128, 48, 15}, {-128, 32, 29, 1}, {-128, 16, 42, 5}, {-128, 0, 51, 11}, {-104, -11, 45, 16, 1}, {-89, -19, 39, 20, 3}, {-79, -24, 33, 24, 5}, {-72, -27, 27, 26, 8, 1}, {-67, -30, 21, 27, 11, 2}, {-63, -32, 16, 27, 14, 4}, {-61, -33, 12, 27, 17, 6}, {-58, -34, 8, 26, 20, 8}, {-56, -35, 4, 25, 22, 11}, {-55, -36, 0, 22, 22, 12, 4, 1}, {-51, -35, -2, 19, 22, 14, 6, 2}, {-48, -34, -5, 16, 21, 15, 7, 2}, {-45, -33, -7, 13, 20, 16, 9, 3}, {-43, -32, -9, 11, 19, 17, 10, 5}, {-41, -32, -10, 9, 18, 17, 11, 6}, {-40, -31, -11, 7, 17, 18, 13, 7}, {-39, -31, -12, 5, 16, 18, 14, 8}, {-38, -31, -13, 4, 15, 18, 15, 10}, {-37, -30, -14, 2, 14, 18, 16, 11}, {-36, -30, -15, 1, 13, 18, 17, 12}, {-34, -29, -16, -1, 10, 15, 15, 11, 7, 4}, {-33, -28, -16, -2, 9, 14, 15, 12, 8, 4}, {-32, -27, -16, -3, 7, 13, 14, 12, 8, 5}, {-31, -27, -16, -4, 6, 13, 14, 13, 9, 6}, {-30, -26, -17, -4, 5, 12, 14, 13, 10, 7}, {-29, -26, -17, -5, 4, 11, 14, 13, 11, 7}, {-29, -25, -17, -6, 3, 11, 14, 14, 11, 8}, {-28, -25, -17, -6, 3, 10, 14, 14, 12, 9}, {-27, -25, -17, -7, 2, 9, 13, 14, 13, 10}, {-27, -24, -17, -7, 1, 9, 13, 14, 13, 11}, {-26, -23, -17, -9, 0, 6, 11, 12, 11, 9, 7, 5}, {-25, -23, -17, -9, 0, 5, 10, 12, 11, 10, 7, 5}, {-25, -22, -17, -9, -1, 5, 9, 11, 11, 10, 8, 6}, {-24, -22, -17, -9, -2, 4, 9, 11, 11, 10, 8, 6}, {-23, -22, -17, -10, -2, 4, 8, 11, 12, 11, 9, 7}, {-23, -21, -17, -10, -3, 3, 8, 11, 12, 11, 9, 7}, {-23, -21, -16, -10, -3, 3, 7, 10, 12, 11, 10, 8}, {-22, -21, -16, -10, -3, 2, 7, 10, 12, 11, 10, 8}, {-22, -20, -16, -10, -4, 2, 7, 10, 12, 12, 10, 9}, {-22, -20, -16, -11, -4, 1, 6, 10, 11, 12, 11, 9}, {-21, -19, -16, -11, -5, 0, 4, 8, 9, 10, 9, 8, 6, 5}, {-20, -19, -16, -11, -5, 0, 4, 7, 9, 10, 9, 8, 7, 5}, {-20, -19, -16, -11, -6, 0, 3, 7, 9, 10, 10, 8, 7, 6}, {-20, -19, -16, -11, -6, -1, 3, 7, 9, 10, 10, 9, 7, 6}, {-19, -18, -15, -11, -6, -1, 3, 6, 9, 10, 10, 9, 8, 6}, {-19, -18, -15, -11, -6, -1, 2, 6, 8, 10, 10, 9, 8, 7}, {-19, -18, -15, -11, -6, -1, 2, 6, 8, 10, 10, 9, 8, 7}, {-18, -18, -15, -11, -7, -2, 2, 5, 8, 9, 10, 10, 9, 7}, {-18, -17, -15, -11, -7, -2, 1, 5, 8, 9, 10, 10, 9, 8}, {-18, -17, -15, -11, -7, -2, 1, 5, 8, 9, 10, 10, 9, 8}, {-17, -17, -14, -11, -7, -3, 0, 3, 6, 8, 8, 8, 8, 7, 6, 5}, {-17, -16, -14, -11, -7, -3, 0, 3, 6, 7, 8, 8, 8, 7, 6, 5}, {-17, -16, -14, -11, -8, -4, 0, 3, 5, 7, 8, 9, 8, 7, 6, 5}, {-17, -16, -14, -11, -8, -4, 0, 2, 5, 7, 8, 9, 8, 8, 7, 6}, {-16, -16, -14, -11, -8, -4, 0, 2, 5, 7, 8, 9, 8, 8, 7, 6}, {-16, -16, -14, -11, -8, -4, 0, 2, 5, 7, 8, 9, 8, 8, 7, 6}, {-16, -15, -14, -11, -8, -4, -1, 2, 4, 6, 8, 8, 9, 8, 7, 6}, {-16, -15, -14, -11, -8, -4, -1, 1, 4, 6, 8, 8, 9, 8, 8, 7}, {-16, -15, -13, -11, -8, -4, -1, 1, 4, 6, 8, 8, 9, 8, 8, 7}, {-15, -15, -13, -11, -8, -5, -1, 1, 4, 6, 8, 8, 9, 9, 8, 7}, {-15, -14, -13, -11, -8, -5, -2, 0, 3, 5, 6, 7, 7, 7, 7, 6, 5, 5}, {-15, -14, -13, -11, -8, -5, -2, 0, 2, 4, 6, 7, 7, 7, 7, 6, 6, 5}, {-15, -14, -13, -11, -8, -5, -2, 0, 2, 4, 6, 7, 7, 7, 7, 7, 6, 5}, {-14, -14, -13, -11, -8, -5, -2, 0, 2, 4, 6, 7, 7, 8, 7, 7, 6, 5}, {-14, -14, -13, -11, -8, -5, -3, 0, 2, 4, 6, 7, 7, 8, 7, 7, 6, 6}, {-14, -14, -12, -11, -8, -6, -3, 0, 2, 4, 5, 7, 7, 8, 7, 7, 6, 6}, {-14, -14, -12, -11, -8, -6, -3, 0, 1, 4, 5, 7, 7, 8, 8, 7, 7, 6}, {-14, -13, -12, -11, -8, -6, -3, 0, 1, 3, 5, 6, 7, 8, 8, 7, 7, 6}, {-14, -13, -12, -10, -8, -6, -3, 0, 1, 3, 5, 6, 7, 8, 8, 7, 7, 6}, {-13, -13, -12, -10, -8, -6, -3, -1, 1, 3, 5, 6, 7, 8, 8, 8, 7, 7}, {-13, -13, -12, -10, -8, -6, -4, -1, 0, 2, 4, 5, 6, 6, 7, 7, 6, 6, 5, 4}, {-13, -13, -12, -10, -8, -6, -4, -1, 0, 2, 4, 5, 6, 6, 7, 7, 6, 6, 5, 5}, {-13, -12, -12, -10, -8, -6, -4, -1, 0, 2, 3, 5, 6, 6, 7, 7, 6, 6, 5, 5}, {-13, -12, -11, -10, -8, -6, -4, -2, 0, 2, 3, 5, 6, 6, 7, 7, 6, 6, 6, 5}, {-13, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 5, 6, 6, 7, 7, 7, 6, 6, 5}, {-12, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 4, 6, 6, 7, 7, 7, 6, 6, 5}, {-12, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 4, 5, 6, 7, 7, 7, 6, 6, 5}, {-12, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 4, 5, 6, 7, 7, 7, 7, 6, 6}, {-12, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 4, 5, 6, 7, 7, 7, 7, 6, 6}, {-12, -12, -11, -10, -8, -6, -4, -2, 0, 1, 3, 4, 5, 6, 7, 7, 7, 7, 6, 6}, {-12, -11, -11, -10, -8, -6, -5, -3, -1, 0, 2, 3, 4, 5, 6, 6, 6, 6, 6, 5, 5, 4}, {-11, -11, -11, -10, -8, -6, -5, -3, -1, 0, 2, 3, 4, 5, 6, 6, 6, 6, 6, 5, 5, 4}, {-11, -11, -11, -9, -8, -6, -5, -3, -1, 0, 1, 3, 4, 5, 6, 6, 6, 6, 6, 5, 5, 4}, {-11, -11, -10, -9, -8, -6, -5, -3, -1, 0, 1, 3, 4, 5, 6, 6, 6, 6, 6, 6, 5, 5}, {-11, -11, -10, -9, -8, -7, -5, -3, -1, 0, 1, 3, 4, 5, 5, 6, 6, 6, 6, 6, 5, 5}, {-11, -11, -10, -9, -8, -7, -5, -3, -1, 0, 1, 3, 4, 5, 5, 6, 6, 6, 6, 6, 5, 5}, {-11, -11, -10, -9, -8, -7, -5, -3, -1, 0, 1, 2, 4, 5, 5, 6, 6, 6, 6, 6, 5, 5}, {-11, -11, -10, -9, -8, -7, -5, -3, -1, 0, 1, 2, 4, 5, 5, 6, 6, 6, 6, 6, 6, 5}, {-11, -11, -10, -9, -8, -7, -5, -3, -1, 0, 1, 2, 4, 5, 5, 6, 6, 6, 6, 6, 6, 5}, }; /** * Creates a new instance of Gray8CannyVert * @param cSigma the sigma value for the operator, which is the sigma * in the Gaussian distribution multipied by 10.0 and converted to integer. * @throws jjil.core.Error if cSigma is out of range. */ public Gray8CannyVert(int cSigma) throws jjil.core.Error { this.setSigma(cSigma); } /** * Apply the Canny operator vertically to the input input image. * The sigma value for the operator is set in the class constructor. * We handle the borders of the image a little carefully to avoid creating * spurious edges at them. The image value at the border is reflected so * that image(0,-1), for example, is made equal to image(0,1). * @param image the input Gray8Image * @throws jjil.core.Error if the input is not a Gray8Image. */ public void push(Image image) throws jjil.core.Error { if (!(image instanceof Gray8Image)) { throw new Error( Error.PACKAGE.ALGORITHM, ErrorCodes.IMAGE_NOT_GRAY8IMAGE, image.toString(), null, null); } Gray8Image input = (Gray8Image) image; Gray8Image result = new Gray8Image(image.getWidth(), image.getHeight()); byte[] bIn = input.getData(); byte[] bResult = result.getData(); int[] wCoeff = this.nCoeff[this.cSigma]; int cWidth = input.getWidth(); for (int j=0; j<cWidth; j++) { for (int i=0; i<input.getHeight(); i++) { /* top side of Canny operator */ int wSum = 0; /* Use Math.abs to mirror the index at the border */ for (int k=1; k<wCoeff.length; k++) { wSum += wCoeff[k] * bIn[Math.abs(i-k)*cWidth + j]; } /* bottom side of Canny operator */ for (int k=0; k<wCoeff.length; k++) { if (i + k < input.getHeight()) { wSum += wCoeff[k] * bIn[(i+k)*cWidth + j]; } else { // reflect at border. j + k >= cWidth so // 2*cWidth - (j + k + 1) < cWidth int cPos = 2*input.getHeight() - (i + k + 1); wSum += wCoeff[k] * bIn[cPos*cWidth + j]; } } /* Canny coefficients are scaled so sum of absolute values * is 256. */ wSum = wSum >> 8; bResult[i*cWidth + j] = (byte) wSum; } } super.setOutput(result); } /** Returns the current value of sigma. * * @return the sigma value */ public int getSigma() { return this.cSigma; } /** * sets a new value for sigma. Sigma controls the frequency of edges * that the operator responds to. A small sigma value gives higher * frequency edges, while a larger sigma gives lower frequency edges. * @param cSigma the new sigma value * @throws jjil.core.Error if cSigma is out of range. */ public void setSigma(int cSigma) throws jjil.core.Error { if (cSigma <= 1 || cSigma >= this.nCoeff.length) { throw new Error( Error.PACKAGE.ALGORITHM, ErrorCodes.PARAMETER_OUT_OF_RANGE, new Integer(cSigma).toString(), new Integer(1).toString(), new Integer(this.nCoeff.length).toString()); } this.cSigma = cSigma; } /** returns a string describing this Canny operator. * * @return a string describing the Canny operator. */ public String toString() { return super.toString() + " (" + this.cSigma + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } }