// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.com
//
// Copyright © Peter Kovesi, 2002
// pk at csse uwa edu au
//
//    This library 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 2.1 of the License, or (at your option) any later version.
//
//    This library 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 GNU Lesser General Public
//    License along with this library; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

package Catalano.Imaging.Filters;

import Catalano.Imaging.FastBitmap;
import Catalano.Imaging.IApplyInPlace;
import Catalano.Math.Matrix;

/**
 * Perona-Malik Anisotropic diffusion.
 * 
 * 

Is a technique aiming at reducing image noise without removing significant parts * of the image content, typically edges, lines or other details that * are important for the interpretation of the image.

* * @author Diego Catalano */ public class PeronaMalikAnisotropicDiffusion implements IApplyInPlace{ /** * Diffusion equation. */ public static enum Diffusion { /** * Favors high-contrast edges over low-contrast ones. */ HighContrastEdges, /** * Favors wide regions over smaller ones. */ WideRegions }; private int iterations; private double kappa; private double lambda; private Diffusion diffusion; /** * Get number of iterations. * @return Number of iterations. */ public int getIterations() { return iterations; } /** * Set number of iterations. * @param iterations Number of iterations. */ public void setIterations(int iterations) { this.iterations = iterations; } /** * Get kappa value. * @return Kappa value, */ public double getKappa() { return kappa; } /** * Set kappa value, * @param kappa Kappa value. */ public void setKappa(double kappa) { this.kappa = kappa; } /** * Get lambda value. * @return Lambda value. */ public double getLambda() { return lambda; } /** * Set lambda value, * @param lambda Lambda value. */ public void setLambda(double lambda) { this.lambda = lambda; } /** * Get diffusion equation. * @return Diffusion equation. */ public Diffusion getDiffusion() { return diffusion; } /** * Set diffusion equation. * @param diffusion Diffusion equation. */ public void setDiffusion(Diffusion diffusion) { this.diffusion = diffusion; } /** * Initializes a new instance of the PeronaMalikAnisotropicDiffusion class. */ public PeronaMalikAnisotropicDiffusion() { this(20); } /** * Initializes a new instance of the PeronaMalikAnisotropicDiffusion class. * @param iterations Number of iterations. */ public PeronaMalikAnisotropicDiffusion(int iterations) { this(iterations,10,0.25,Diffusion.HighContrastEdges); } /** * Initializes a new instance of the PeronaMalikAnisotropicDiffusion class. * @param iterations Number of iterations. * @param kappa Controls conduction as a function of gradient. * @param lambda Controls the speed of diffusion. * @param diffusion Diffusion equation. */ public PeronaMalikAnisotropicDiffusion(int iterations, double kappa, double lambda, Diffusion diffusion) { this.iterations = iterations; this.kappa = kappa; this.lambda = lambda; this.diffusion = diffusion; } @Override public void applyInPlace(FastBitmap fastBitmap) { if(fastBitmap.isGrayscale()){ double[][] diff = fastBitmap.toMatrixGrayAsDouble(); int h = diff.length; int w = diff[0].length; double[][] deltaN = new double[h][w]; double[][] deltaS = new double[h][w]; double[][] deltaE = new double[h][w]; double[][] deltaW = new double[h][w]; for (int i = 0; i < iterations; i++) { //North diff for (int y = 0; y < w; y++) { deltaN[0][y] = -diff[0][y]; } for (int x = 1; x < h; x++) { for (int y = 0; y < w; y++) { deltaN[x][y] = diff[x-1][y] - diff[x][y]; } } //South diff for (int x = 0; x < h - 1; x++) { for (int y = 0; y < w; y++) { deltaS[x][y] = diff[x+1][y] - diff[x][y]; } } for (int y = 0; y < w; y++) { deltaS[h - 1][y] = -diff[h - 1][y]; } //East diff for (int x = 0; x < h; x++) { for (int y = 0; y < w - 1; y++) { deltaE[x][y] = diff[x][y+1] - diff[x][y]; } } for (int x = 0; x < h; x++) { deltaE[x][w - 1] = -diff[x][w - 1]; } //West diff for (int x = 0; x < h; x++) { for (int y = 1; y < w; y++) { deltaW[x][y] = diff[x][y-1] - diff[x][y]; } } for (int x = 0; x < h; x++) { deltaW[x][0] = -diff[x][0]; } if(diffusion == Diffusion.HighContrastEdges){ for (int x = 0; x < h; x++) { for (int y = 0; y < w; y++) { double cN = Math.exp(-Math.pow(deltaN[x][y] / kappa, 2)); double cS = Math.exp(-Math.pow(deltaS[x][y] / kappa, 2)); double cE = Math.exp(-Math.pow(deltaE[x][y] / kappa, 2)); double cW = Math.exp(-Math.pow(deltaW[x][y] / kappa, 2)); diff[x][y] = diff[x][y] + lambda * (deltaN[x][y]*cN + deltaS[x][y]*cS + deltaE[x][y]*cE + deltaW[x][y]*cW); } } } else{ for (int x = 0; x < h; x++) { for (int y = 0; y < w; y++) { double cN = 1 / (1 + Math.pow((deltaN[x][y] / kappa),2)); double cS = 1 / (1 + Math.pow((deltaS[x][y] / kappa),2)); double cE = 1 / (1 + Math.pow((deltaE[x][y] / kappa),2)); double cW = 1 / (1 + Math.pow((deltaW[x][y] / kappa),2)); diff[x][y] = diff[x][y] + lambda * (deltaN[x][y]*cN + deltaS[x][y]*cS + deltaE[x][y]*cE + deltaW[x][y]*cW); } } } } //Just clamp the values [0..255] double min = Matrix.Min(diff); double max = Matrix.Max(diff); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { fastBitmap.setGray(i, j, (int)Catalano.Math.Tools.Scale(min, max, 0, 255, diff[i][j])); } } } else{ throw new IllegalArgumentException("Perona-Malik only works in grayscale images."); } } }

Ads help maintain this website.