/*
* Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.alg.misc;
import boofcv.misc.AutoTypeImage;
import boofcv.misc.CodeGeneratorBase;
import boofcv.struct.image.ImageType;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* Generates functions inside of {@link ImageMiscOps}.
*
* @author Peter Abeles
*/
public class GenerateImageStatistics extends CodeGeneratorBase {
String className = "ImageStatistics";
private AutoTypeImage input;
public void generate() throws FileNotFoundException {
printPreamble();
printAll();
out.println("}");
}
private void printPreamble() throws FileNotFoundException {
setOutputFile(className);
out.print("import boofcv.struct.image.*;\n" +
"import javax.annotation.Generated;\n" +
"import boofcv.alg.InputSanityCheck;\n" +
"\n" +
"/**\n" +
" * Computes statistical properties of pixels inside an image.\n" +
" *\n" +
" * <p>DO NOT MODIFY: Generated by "+getClass().getName()+"</p>.\n"+
" *\n"+
" * @author Peter Abeles\n" +
" */\n" +
generatedString() +
"public class "+className+" {\n\n");
}
public void printAll() {
AutoTypeImage types[] = AutoTypeImage.getSpecificTypes();
ImageType.Family families[] = new ImageType.Family[]{ImageType.Family.GRAY,ImageType.Family.INTERLEAVED};
List<CodeGenerator> functions = new ArrayList<>();
functions.add( new GenerateMin());
functions.add( new GenerateMax());
functions.add( new GenerateMaxAbs());
functions.add( new GenerateMeanDiffSq() );
functions.add( new GenerateMeanDiffAbs() );
for( AutoTypeImage t : types ) {
input = t;
for( CodeGenerator generator : functions ) {
for( ImageType.Family f : families ) {
generator.printHighLevel(f);
}
generator.printLowLevel();
}
for( ImageType.Family f : families ) {
printSum(f);
printMean(f);
}
printVariance();
printHistogram();
}
}
public void printHistogram() {
if( input.isSigned() ) {
out.print("\t/**\n" +
"\t * Computes the histogram of intensity values for the image.\n" +
"\t * \n" +
"\t * @param input (input) Image.\n" +
"\t * @param minValue (input) Minimum possible intensity value \n" +
"\t * @param histogram (output) Storage for histogram. Number of elements must be equal to max value.\n" +
"\t */\n" +
"\tpublic static void histogram( "+input.getSingleBandName()+" input , int minValue , int histogram[] ) {\n" +
"\t\tfor( int i = 0; i < histogram.length; i++ )\n" +
"\t\t\thistogram[i] = 0;\n" +
"\t\t\n" +
"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
"\t\t\tint index = input.startIndex + y*input.stride;\n" +
"\t\t\tint end = index + input.width;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n" +
"\t\t\t\t// floor value. just convert to int rounds towards zero\n");
if( input.isInteger()) {
if( input.getNumBits() == 64 )
out.print("\t\t\t\thistogram[(int)input.data[index] - minValue]++;\n");
else
out.print("\t\t\t\thistogram[input.data[index] - minValue ]++;\n");
} else
out.print("\t\t\t\thistogram[(int)input.data[index] - minValue ]++;\n");
out.print("\t\t\t}\n" +
"\t\t}\n" +
"\t}\n\n");
} else {
out.print("\t/**\n" +
"\t * Computes the histogram of intensity values for the image.\n" +
"\t * \n" +
"\t * @param input (input) Image.\n" +
"\t * @param histogram (output) Storage for histogram. Number of elements must be equal to max value.\n" +
"\t */\n" +
"\tpublic static void histogram( "+input.getSingleBandName()+" input , int histogram[] ) {\n" +
"\t\tfor( int i = 0; i < histogram.length; i++ )\n" +
"\t\t\thistogram[i] = 0;\n" +
"\t\t\n" +
"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
"\t\t\tint index = input.startIndex + y*input.stride;\n" +
"\t\t\tint end = index + input.width;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n" +
"\t\t\t\thistogram[input.data[index]"+input.getBitWise()+"]++;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t}\n\n");
}
}
public void printMaxAbs() {
out.print("\t/**\n" +
"\t * Returns the absolute value of the element with the largest absolute value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Largest pixel absolute value.\n" +
"\t */\n" +
"\tpublic static "+input.getSumType()+" maxAbs( "+input.getSingleBandName()+" input ) {\n" +
"\n" +
"\t\t"+input.getSumType()+" max = 0;\n" +
"\n" +
"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
"\t\t\tint index = input.startIndex + y*input.stride;\n" +
"\t\t\tint end = index + input.width;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n");
if( input.isSigned() )
out.print("\t\t\t\t"+input.getSumType()+" v = Math.abs(input.data[index]);\n");
else
out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index]"+input.getBitWise()+";\n");
out.print("\t\t\t\tif( v > max )\n" +
"\t\t\t\t\tmax = v;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\treturn max;\n" +
"\t}\n\n");
}
public void printMax() {
out.print("\t/**\n" +
"\t * Returns the maximum element value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Maximum pixel value.\n" +
"\t */\n" +
"\tpublic static "+input.getSumType()+" max( "+input.getSingleBandName()+" input ) {\n" +
"\n" +
"\t\t"+input.getSumType()+" max = input.get(0,0);\n" +
"\n" +
"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
"\t\t\tint index = input.startIndex + y*input.stride;\n" +
"\t\t\tint end = index + input.width;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n");
out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index] "+input.getBitWise()+";\n");
out.print("\t\t\t\tif( v > max )\n" +
"\t\t\t\t\tmax = v;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\treturn max;\n" +
"\t}\n\n");
}
public void printMin() {
out.print("\t/**\n" +
"\t * Returns the minimum element value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Minimum pixel value.\n" +
"\t */\n" +
"\tpublic static "+input.getSumType()+" min( "+input.getSingleBandName()+" input ) {\n" +
"\n" +
"\t\t"+input.getSumType()+" min = input.get(0,0);\n" +
"\n" +
"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
"\t\t\tint index = input.startIndex + y*input.stride;\n" +
"\t\t\tint end = index + input.width;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n");
out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index] "+input.getBitWise()+";\n");
out.print("\t\t\t\tif( v < min )\n" +
"\t\t\t\t\tmin = v;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\treturn min;\n" +
"\t}\n\n");
}
public void printSum( ImageType.Family family ) {
String bitWise = input.getBitWise();
String columns = family == ImageType.Family.INTERLEAVED ? "*img.numBands" : "";
String sumType = input.getSumType();
out.print("\t/**\n" +
"\t * <p>\n" +
"\t * Returns the sum of all the pixels in the image.\n" +
"\t * </p>\n" +
"\t * \n" +
"\t * @param img Input image. Not modified.\n" +
"\t */\n" +
"\tpublic static "+sumType+" sum( "+input.getImageName(family)+" img ) {\n" +
"\n" +
"\t\tfinal int rows = img.height;\n" +
"\t\tfinal int columns = img.width"+columns+";\n" +
"\n" +
"\t\t"+sumType+" total = 0;\n" +
"\t\t\n" +
"\t\tfor (int y = 0; y < rows; y++) {\n" +
"\t\t\tint index = img.startIndex + y * img.stride;\n" +
"\t\t\t\n" +
"\t\t\tint indexEnd = index+columns;\n" +
"\t\t\tfor (; index < indexEnd; index++ ) {\n" +
"\t\t\t\ttotal += img.data[index] "+bitWise+";\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\t\n" +
"\t\treturn total;\n" +
"\t}\n\n");
}
public void printMean( ImageType.Family family ) {
String columns = family == ImageType.Family.INTERLEAVED ? "*img.numBands" : "";
out.print("\t/**\n" +
"\t * Returns the mean pixel intensity value.\n" +
"\t * \n" +
"\t * @param img Input image. Not modified.\n" +
"\t * @return Mean pixel intensity value\n" +
"\t */\n" +
"\tpublic static double mean( "+input.getImageName(family)+" img ) {\n" +
"\t\treturn sum(img)/(double)(img.width*img.height"+columns+");\n" +
"\t}\n\n");
}
public void printVariance() {
String bitWise = input.getBitWise();
out.print("\t/**\n" +
"\t * Computes the variance of pixel intensity values inside the image.\n" +
"\t *\n" +
"\t * @param img Input image. Not modified.\n" +
"\t * @param mean Mean pixel intensity value. \n" +
"\t * @return Pixel variance \n" +
"\t */\n" +
"\tpublic static double variance( "+input.getSingleBandName()+" img , double mean ) {\n" +
"\n" +
"\t\tdouble variance = 0;\n" +
"\n" +
"\t\tfor (int y = 0; y < img.height; y++) {\n" +
"\t\t\tint index = img.getStartIndex() + y * img.getStride();\n" +
"\n" +
"\t\t\tint indexEnd = index+img.width;\n" +
"\t\t\t// for(int x = 0; x < img.width; x++ ) {\n" +
"\t\t\tfor (; index < indexEnd; index++ ) {\n" +
"\t\t\t\tdouble d = (img.data[index]"+bitWise+") - mean; \n" +
"\t\t\t\tvariance += d*d;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\n" +
"\t\treturn variance/(img.width*img.height);\n" +
"\t}\n\n");
}
public void printMeanDiffSq() {
String imageName = input.getSingleBandName();
String bitWise = input.getBitWise();
String sumType = input.getSumType();
String largeSumType = input.getLargeSumType();
out.print("\t/**\n" +
"\t * <p>Computes the mean squared error (MSE) between the two images.</p>\n" +
"\t *\n" +
"\t * @param imgA first image. Not modified.\n" +
"\t * @param imgB second image. Not modified.\n" +
"\t * @return error between the two images.\n" +
"\t */\n" +
"\tpublic static double meanDiffSq("+imageName+" imgA, "+imageName+" imgB ) {\n" +
"\t\t"+largeSumType+" total = 0;\n" +
"\n" +
"\t\tfor (int y = 0; y < imgA.height; y++) {\n" +
"\t\t\tint indexA = imgA.getStartIndex() + y * imgA.getStride();\n" +
"\t\t\tint indexB = imgB.getStartIndex() + y * imgB.getStride();\n" +
"\t\t\tfor (int x = 0; x < imgA.width; x++,indexA++,indexB++) {\n" +
"\t\t\t\t"+sumType+" difference = (imgA.data[indexA]"+bitWise+")-(imgB.data[indexB]"+bitWise+");\n" +
"\t\t\t\ttotal += difference*difference;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\n" +
"\t\treturn total / (double)(imgA.width*imgA.height);\n" +
"\t}\n\n");
}
public void printMeanDiffAbs() {
String imageName = input.getSingleBandName();
String bitWise = input.getBitWise();
String sumType = input.getSumType();
out.print("\t/**\n" +
"\t * <p>Computes the mean squared error (MSE) between the two images.</p>\n" +
"\t *\n" +
"\t * @param imgA first image. Not modified.\n" +
"\t * @param imgB second image. Not modified.\n" +
"\t * @return error between the two images.\n" +
"\t */\n" +
"\tpublic static double meanDiffAbs("+imageName+" imgA, "+imageName+" imgB ) {\n" +
"\t\t"+sumType+" total = 0;\n" +
"\n" +
"\t\tfor (int y = 0; y < imgA.height; y++) {\n" +
"\t\t\tint indexA = imgA.getStartIndex() + y * imgA.getStride();\n" +
"\t\t\tint indexB = imgB.getStartIndex() + y * imgB.getStride();\n" +
"\t\t\tfor (int x = 0; x < imgA.width; x++,indexA++,indexB++) {\n" +
"\t\t\t\t"+sumType+" difference = (imgA.data[indexA]"+bitWise+")-(imgB.data[indexB]"+bitWise+");\n" +
"\t\t\t\ttotal += Math.abs(difference);\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\n" +
"\t\treturn total / (double)(imgA.width*imgA.height);\n" +
"\t}\n\n");
}
private class GenerateMin extends InitValue {
public GenerateMin() {
super("min", "v < output",
"\t/**\n" +
"\t * Returns the minimum element value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Minimum pixel value.\n" +
"\t */"
);
}
@Override
public String getValueMassage() { return "array[index] "+input.getBitWise(); }
}
private class GenerateMax extends InitValue {
public GenerateMax() {
super("max", "v > output",
"\t/**\n" +
"\t * Returns the maximum element value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Maximum pixel value.\n" +
"\t */"
);
}
@Override
public String getValueMassage() { return "array[index] "+input.getBitWise(); }
}
private class GenerateMaxAbs extends InitValue {
public GenerateMaxAbs() {
super("maxAbs", "v > output",
"\t/**\n" +
"\t * Returns the maximum element value.\n" +
"\t * \n" +
"\t * @param input Input image. Not modified.\n" +
"\t * @return Maximum pixel value.\n" +
"\t */"
);
}
@Override
public String getValueMassage() {
if( input.isSigned() )
return "Math.abs(array[index])";
else
return "array[index] "+input.getBitWise();
}
}
private class GenerateMeanDiffSq extends GenerateDifference {
public GenerateMeanDiffSq() {
super("meanDiffSq", "difference*difference",
"\t/**\n" +
"\t * <p>Computes the mean squared error (MSE) between the two images.</p>\n" +
"\t *\n" +
"\t * @param imgA first image. Not modified.\n" +
"\t * @param imgB second image. Not modified.\n" +
"\t * @return error between the two images.\n" +
"\t */");
}
}
private class GenerateMeanDiffAbs extends GenerateDifference {
public GenerateMeanDiffAbs() {
super("meanDiffAbs", "Math.abs(difference)",
"\t/**\n" +
"\t * <p>Computes the mean of absolute value error between the two images.</p>\n" +
"\t *\n" +
"\t * @param imgA first image. Not modified.\n" +
"\t * @param imgB second image. Not modified.\n" +
"\t * @return error between the two images.\n" +
"\t */");
}
}
private abstract class InitValue implements CodeGenerator {
String name;
String conditional;
String javaDoc;
public InitValue(String name, String conditional, String javaDoc) {
this.name = name;
this.conditional = conditional;
this.javaDoc = javaDoc;
}
public void printHighLevel( ImageType.Family family ) {
String sumType = input.getSumType();
String columns = family == ImageType.Family.INTERLEAVED ? "input.width*input.numBands" : "input.width";
String nameUn = this.name + (input.isSigned() ? "" : "U");
out.println(javaDoc);
out.print("\tpublic static "+sumType+" "+name+"( "+input.getImageName(family)+" input ) {\n" +
"\t\treturn "+nameUn+"( input.data, input.startIndex, input.height, "+columns+" , input.stride );\n" +
"\t}\n\n");
}
public void printLowLevel() {
String sumType = input.getSumType();
String name = this.name + (input.isSigned() ? "" : "U");
out.print("\tprivate static "+sumType+" "+name+"( "+input.getDataType()+"[] array , int startIndex , int rows , int columns , int stride ) {\n" +
"\n" +
"\t\t"+sumType+" output = array[startIndex]"+input.getBitWise()+";\n" +
"\n" +
"\t\tfor( int y = 0; y < rows; y++ ) {\n" +
"\t\t\tint index = startIndex + y*stride;\n" +
"\t\t\tint end = index + columns;\n" +
"\n" +
"\t\t\tfor( ; index < end; index++ ) {\n" +
"\t\t\t\t"+sumType+" v = "+getValueMassage()+";\n" +
"\t\t\t\tif( "+conditional+" )\n" +
"\t\t\t\t\toutput = v;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\treturn output;\n" +
"\t}\n\n");
}
public abstract String getValueMassage();
}
private class GenerateDifference implements CodeGenerator {
String name;
String operation;
String javaDoc;
public GenerateDifference(String name, String operation, String javaDoc) {
this.name = name;
this.operation = operation;
this.javaDoc = javaDoc;
}
@Override
public void printHighLevel(ImageType.Family family) {
String columns = family == ImageType.Family.INTERLEAVED ? "imgA.width*imgA.numBands" : "imgA.width";
String nameUn = this.name + (input.isSigned() ? "" : "U");
String imageName = input.getImageName(family);
out.println(javaDoc);
out.print("\tpublic static double "+name+"("+imageName+" imgA, "+imageName+" imgB ) {\n" +
"\t\tInputSanityCheck.checkSameShape(imgA,imgB);\n" +
"\t\treturn "+nameUn+"(imgA.data,imgA.startIndex,imgA.stride, imgB.data,imgB.startIndex,imgB.stride,\n" +
"\t\t\t\timgA.height, "+columns+");\n" +
"\t}\n\n");
}
@Override
public void printLowLevel() {
String dataType = input.getDataType();
String sumType = input.getSumType();
String name = this.name + (input.isSigned() ? "" : "U");
String bitWise = input.getBitWise();
out.print("\tprivate static double "+name+"("+dataType+" []dataA, int startIndexA , int strideA,\n" +
"\t\t\t\t\t\t\t\t\t"+dataType+" []dataB, int startIndexB , int strideB,\n" +
"\t\t\t\t\t\t\t\t\tint rows , int columns ) {\n" +
"\t\t"+sumType+" total = 0;\n" +
"\n" +
"\t\tfor (int y = 0; y < rows; y++) {\n" +
"\t\t\tint indexA = startIndexA + y * strideA;\n" +
"\t\t\tint indexB = startIndexB + y * strideB;\n" +
"\t\t\t\n" +
"\t\t\tint indexEnd = indexA+columns;\n" +
"\t\t\t\n" +
"\t\t\tfor (; indexA < indexEnd; indexA++,indexB++) {\n" +
"\t\t\t\t"+sumType+" difference = (dataA[indexA]"+bitWise+")-(dataB[indexB]"+bitWise+");\n" +
"\t\t\t\ttotal += "+operation+";\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\n" +
"\t\treturn total / (double)(rows*columns);\n" +
"\t}\n\n");
}
}
private interface CodeGenerator {
void printHighLevel( ImageType.Family family );
void printLowLevel();
}
public static void main( String args[] ) throws FileNotFoundException {
GenerateImageStatistics gen = new GenerateImageStatistics();
gen.generate();
}
}