diff --git a/src/main/java/azgracompress/DataCompressor.java b/src/main/java/azgracompress/DataCompressor.java index c13921e600965df0ef4ed6d8b614a47be16a5c61..be30166ac47127da5fb25f4d95b09451959aa4fb 100644 --- a/src/main/java/azgracompress/DataCompressor.java +++ b/src/main/java/azgracompress/DataCompressor.java @@ -4,7 +4,7 @@ import azgracompress.benchmark.CompressionBenchmark; import azgracompress.cli.CliConstants; import azgracompress.cli.CustomFunctionBase; import azgracompress.cli.ParsedCliOptions; -import azgracompress.cli.functions.MeasurePlaneErrorFunction; +import azgracompress.cli.functions.EntropyCalculation; import azgracompress.compression.ImageCompressor; import azgracompress.compression.ImageDecompressor; import org.apache.commons.cli.*; @@ -69,7 +69,8 @@ public class DataCompressor { case CustomFunction: { // NOTE(Moravec): Custom function class here | // V - CustomFunctionBase customFunction = new MeasurePlaneErrorFunction(parsedCliOptions); + //CustomFunctionBase customFunction = new MeasurePlaneErrorFunction(parsedCliOptions); + CustomFunctionBase customFunction = new EntropyCalculation(parsedCliOptions); if (!customFunction.run()) { System.err.println("Errors occurred during custom function."); } diff --git a/src/main/java/azgracompress/cli/functions/EntropyCalculation.java b/src/main/java/azgracompress/cli/functions/EntropyCalculation.java new file mode 100644 index 0000000000000000000000000000000000000000..5175eed1ad566e249e0963245587fb21c76a4f3d --- /dev/null +++ b/src/main/java/azgracompress/cli/functions/EntropyCalculation.java @@ -0,0 +1,41 @@ +package azgracompress.cli.functions; + +import azgracompress.cli.CustomFunctionBase; +import azgracompress.cli.ParsedCliOptions; +import azgracompress.data.ImageU16; +import azgracompress.io.RawDataIO; +import azgracompress.utilities.Utils; + +import java.io.IOException; + +public class EntropyCalculation extends CustomFunctionBase { + /** + * Base constructor with parsed CLI options. + * + * @param options Parsed cli options. + */ + public EntropyCalculation(ParsedCliOptions options) { + super(options); + } + + @Override + public boolean run() { + ImageU16 plane = null; + System.out.println(String.format("Input file: %s", options.getInputFile())); + + for (int planeIndex = 0; planeIndex < options.getImageDimension().getZ(); planeIndex++) { + try { + plane = RawDataIO.loadImageU16(options.getInputFile(), + options.getImageDimension(), + planeIndex); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + final double planeEntropy = Utils.calculateEntropy(plane.getData()); + + System.out.println(String.format("%d\t%.4f", planeIndex, planeEntropy)); + } + return true; + } +} diff --git a/src/main/java/azgracompress/utilities/Utils.java b/src/main/java/azgracompress/utilities/Utils.java index a249eea5893dd0609e88cb4d0395b92677567c69..cdce2aaa7641a29f96f44e81a5927c6defd56e6d 100644 --- a/src/main/java/azgracompress/utilities/Utils.java +++ b/src/main/java/azgracompress/utilities/Utils.java @@ -1,5 +1,7 @@ package azgracompress.utilities; +import azgracompress.U16; + import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -99,6 +101,43 @@ public class Utils { return new MinMaxResult<Integer>(min, max); } + /** + * Calculate individual pixel values frequencies. + * Same result as of histogram of bin width = 1 + * + * @param data Pixel data. + * @return Pixel value frequencies. + */ + public static double[] calculatePixelFrequencies(final int[] data) { + double[] frequencies = new double[U16.Max + 1]; + for (final int pixelValue : data) { + ++frequencies[pixelValue]; + } + return frequencies; + } + + public static double log2(final double v) { + return (Math.log(v) / Math.log(2)); + } + + public static double calculateEntropy(final int[] pixelData) { + final double pixelCount = pixelData.length; + final double[] pixelFrequencies = Utils.calculatePixelFrequencies(pixelData); + final double[] pixelProbabilities = new double[pixelFrequencies.length]; + + for (int i = 0; i < pixelFrequencies.length; i++) { + pixelProbabilities[i] = pixelFrequencies[i] / pixelCount; + } + + double entropy = 0.0; + + for (double pixelProbability : pixelProbabilities) { + if (pixelProbability > 0.0) { + entropy += pixelProbability * log2(pixelProbability); + } + } + return (-entropy); + } public static double calculateMse(final int[] difference) { double sum = 0.0;