// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.com
//
// Copyright © Øivind Due Trier, 2013
// oivind.due.trier at nr.no
// Dept. Informatics, Univ. Oslo
//
//
// 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.Tools;
import Catalano.Math.ComplexNumber;
import java.util.ArrayList;
/**
* Compute Zernike moments.
* Zernike polynomials are widely used as basis functions of image moments.
* Since Zernike polynomials are orthogonal to each other, Zernike moments can represent properties of an image with no redundancy
* or overlap of information between the moments. Although Zernike moments are significantly dependent on the scaling and the translation
* of the object in an ROI, their magnitudes are independent of the rotation angle of the object.
* Thus, they can be utilized to extract features from images that describe the shape characteristics of an object.
* For instance, Zernike moments are utilized as shape descriptors to classify benign and malignant breast masses.
* @author Diego Catalano
*/
public final class ZernikeMoments {
/**
* Don't let anyone instantiate this class.
*/
private ZernikeMoments() {}
/**
* Computes the Zernike radial polynomial, Rnm(p), in the definition of V(n,m,x,y).
* @param n Moment order.
* @param m_in Moment order.
* @param x X axis coordinate.
* @param y Y axis coordinate.
* @return Value of Rnm(p).
*/
public static double RadialPolynomial(int n, int m_in, double x, double y){
int a; // (n-s)!
int b; // s!
int c; // [(n+|m|)/2-s]!
int d; // [(n-|m|)/2-s]!
int sign;
int m = Math.abs(m_in);
if ((n - m) % 2 != 0){
return 0;
}
double res = 0;
if ((x * x + y * y) <= 1.0) {
sign = 1;
a = Factorial(n);
b=1;
c = Factorial((n + m) / 2);
d = Factorial((n - m) / 2);
// Before the loop is entered, all the integer variables
// (sign, a, b, c, d) have their correct values for the
// s=0 case.
for(int s = 0; s <= (n - m) / 2; s++){
res += sign*(a * 1.0 / (b * c * d)) * Math.pow((x * x + y * y),(n / 2.0) -s);
// Now update the integer variables before the next
// iteration of the loop.
if (s < (n - m) / 2){
sign = -sign;
a /= (n - s);
b *= (s + 1);
c /= ((n + m) / 2 - s);
d /= ((n - m) / 2 - s);
}
}
}
return res;
}
/**
* Computes the Zernike basis function V(n,m,x,y).
* @param n Moment order.
* @param m Moment order.
* @param x X axis coordinate.
* @param y Y axis coordinate.
* @return Complex number for V(n,m,x,y).
*/
public static ComplexNumber ZernikeBasisFunction(int n, int m, double x, double y) {
if ((x * x + y * y) > 1.0) {
return new ComplexNumber(0.0, 0.0);
}
else {
double r = RadialPolynomial(n,m,x,y);
double arg = m * Math.atan2(y,x);
double real = r * Math.cos(arg);
double imag = r * Math.sin(arg);
return new ComplexNumber(real,imag);
}
}
/**
* Compute zernike moments at n and m moments order.
* Also compute the width, height, and centroid of the shape.
* @param x X axis coordinates.
* @param y Y axis coordinates.
* @param nPoints Indicates the total number of points in the digitized shape.
* @param n Moment order.
* @param m Moment order.
* @return Zernike moment.
*/
public static ComplexNumber ZernikeMoments(double[] x, double[] y, int nPoints, int n, int m){
int diff = n-Math.abs(m);
if ((n<0) || (Math.abs(m) > n) || (diff%2!=0)){
throw new IllegalArgumentException("zer_mom: n="+n+", m="+m+", n-|m|="+diff);
}
double xmin = Double.MAX_VALUE;
double ymin = Double.MAX_VALUE;
double xmax = Double.MIN_VALUE;
double ymax = Double.MIN_VALUE;
for(int i = 0; i < nPoints; i++){
xmin=Math.min(xmin,x[i]);
xmax=Math.max(xmax,x[i]);
ymin=Math.min(ymin,y[i]);
ymax=Math.max(ymax,y[i]);
}
double w = xmax-xmin;//width
double h = ymax-ymin;//height
double cx = xmin+w/2;
double cy = ymin+h/2;
return ZernikeMoments(x, y, nPoints, w, h, cx, cy, n, m);
}
/**
* Compute zernike moments at specified order.
* @param x X axis coordinates.
* @param y Y axis coordinates.
* @param nPoints Indicates the total number of points in the digitized shape.
* @param w Width of the bounding box of the shape.
* @param h Height of the bounding box of the shape.
* @param cx X axis of centroid point of the shape.
* @param cy Y axis of centroid point of the shape.
* @param n Moment order.
* @param m Moment order.
* @return Zernike moment.
*/
public static ComplexNumber ZernikeMoments(double[] x, double[] y, int nPoints, double w, double h, double cx, double cy, int n, int m){
double i_0, j_0;
double i_scale, j_scale;
double X,Y;
ComplexNumber v;
//double isize, jsize;
int diff = n-Math.abs(m);
if ((n<0) || (Math.abs(m) > n) || (diff%2!=0)){
throw new IllegalArgumentException("zer_mom: n="+n+", m="+m+", n-|m|="+diff);
}
//isize = ww;
//jsize = hh;
i_0 = cx;
j_0 = cy;
double radius = w/2;
i_scale=Math.sqrt(2)*radius;
radius=h/2;
j_scale=Math.sqrt(2)*radius; //note we want to construct a circle to contain the rectangle
ComplexNumber res = new ComplexNumber();
for(int i=0; i
Ads help maintain this website.