/* * 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(); } }