|
3 | 3 | import java.awt.image.BufferedImage; |
4 | 4 |
|
5 | 5 | public class SSIMComparisonTool { |
| 6 | + private static final double S_WEIGHT = 0.03, M_WEIGHT = 0.05, L_WEIGHT = 0.15; |
| 7 | + |
| 8 | + private static final double[][] blurWeights = { |
| 9 | + {S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT}, |
| 10 | + {S_WEIGHT, M_WEIGHT, M_WEIGHT, M_WEIGHT, S_WEIGHT}, |
| 11 | + {S_WEIGHT, M_WEIGHT, L_WEIGHT, M_WEIGHT, S_WEIGHT}, |
| 12 | + {S_WEIGHT, M_WEIGHT, M_WEIGHT, M_WEIGHT, S_WEIGHT}, |
| 13 | + {S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT} |
| 14 | + }; |
| 15 | + private static final int blurOffset = blurWeights.length / 2; |
| 16 | + private static final int blurStartOffset = -blurOffset; |
| 17 | + private static final int blurEndOffset = blurStartOffset + blurWeights.length; |
| 18 | + |
6 | 19 | private static final double STANDARD_RED_WEIGHT = 0.299; |
7 | 20 | private static final double STANDARD_GREEN_WEIGHT = 0.587; |
8 | 21 | private static final double STANDARD_BLUE_WEIGHT = 0.114; |
@@ -49,9 +62,33 @@ public SSIMComparisonTool(double luminanceStabilizer, double contrastStabilizer, |
49 | 62 | this.STRUCTURE_STABILIZER = structureStabilizer; |
50 | 63 | } |
51 | 64 |
|
| 65 | + /** |
| 66 | + * Returns a modified version of SSIM analysis, where structure calculations are done on blurred variants. |
| 67 | + * This is done to reduce "error" based on shifted pixels and different fonts. |
| 68 | + * @param original |
| 69 | + * @param contender |
| 70 | + * @return |
| 71 | + */ |
| 72 | + public Results performModifiedSSIMComparison(BufferedImage original, BufferedImage contender){ |
| 73 | + double[][] originalGrayscaleData = SSIMComparisonTool.getGrayScaleData(original); |
| 74 | + double[][] contenderGrayscaleData = SSIMComparisonTool.getGrayScaleData(contender); |
| 75 | + Results regularResults = this.performSSIMComparison(originalGrayscaleData, contenderGrayscaleData); |
| 76 | + double[][] blurredOGD = doubleWideBlur(originalGrayscaleData); |
| 77 | + double[][] blurredCGD = doubleWideBlur(contenderGrayscaleData); |
| 78 | + Results blurredResults = this.performSSIMComparison(blurredOGD, blurredCGD); |
| 79 | + double newProduct = regularResults.luminanceComponent() * regularResults.contrastComponent() * blurredResults.structureComponent(); |
| 80 | + return new Results(newProduct, regularResults.luminanceComponent(), regularResults.contrastComponent(), blurredResults.structureComponent()); |
| 81 | + |
| 82 | + } |
| 83 | + |
52 | 84 | public Results performSSIMComparison(BufferedImage original, BufferedImage contender){ |
53 | 85 | double[][] originalGrayscaleData = SSIMComparisonTool.getGrayScaleData(original); |
54 | 86 | double[][] contenderGrayscaleData = SSIMComparisonTool.getGrayScaleData(contender); |
| 87 | + return this.performSSIMComparison(originalGrayscaleData, contenderGrayscaleData); |
| 88 | + } |
| 89 | + |
| 90 | + |
| 91 | + private Results performSSIMComparison(double[][] originalGrayscaleData, double[][] contenderGrayscaleData){ |
55 | 92 | double originalPSM = SSIMComparisonTool.pixelSampleMean(originalGrayscaleData); |
56 | 93 | double contenderPSM = SSIMComparisonTool.pixelSampleMean(contenderGrayscaleData); |
57 | 94 | double originalVariance = SSIMComparisonTool.sampleVariance(originalGrayscaleData, originalPSM); |
@@ -83,7 +120,7 @@ private double contrastCalculation(double originalVariance, double contenderVari |
83 | 120 | return numerator / denominator; |
84 | 121 | } |
85 | 122 |
|
86 | | - private double structureCalculation(double originalVariance, double contenderVariance, double coVariance){ |
| 123 | + private double structureCalculation(double originalVariance, double contenderVariance, double coVariance) { |
87 | 124 | double numerator = coVariance + this.STRUCTURE_STABILIZER; |
88 | 125 | double denominator = Math.sqrt(originalVariance * contenderVariance) + this.STRUCTURE_STABILIZER; |
89 | 126 | return numerator / denominator; |
@@ -135,5 +172,26 @@ private static double[][] getGrayScaleData(BufferedImage image){ |
135 | 172 | return convertedData; |
136 | 173 | } |
137 | 174 |
|
| 175 | + private static double[][] doubleWideBlur(double[][] grayscaleData){ |
| 176 | + return wideBlur(wideBlur(grayscaleData)); |
| 177 | + } |
| 178 | + |
| 179 | + private static double[][] wideBlur(double[][] grayscaleData){ |
| 180 | + double[][] convertedData = new double[grayscaleData.length][grayscaleData[0].length]; |
| 181 | + for (int y = 0; y < grayscaleData.length; y++) { |
| 182 | + for (int x = 0; x < grayscaleData[0].length; x++) { |
| 183 | + double newValue = 0.0; |
| 184 | + for (int i = blurStartOffset; i < blurEndOffset; i++) { |
| 185 | + for (int j = blurStartOffset; j < blurEndOffset; j++) { |
| 186 | + if (y + i < 0 || x + j < 0 || y + i >= grayscaleData.length || x + j >= grayscaleData[0].length) continue; |
| 187 | + newValue += grayscaleData[y + i][x + j] * blurWeights[i + blurOffset][j + blurOffset]; |
| 188 | + } |
| 189 | + } |
| 190 | + convertedData[y][x] = newValue; |
| 191 | + } |
| 192 | + } |
| 193 | + return convertedData; |
| 194 | + } |
| 195 | + |
138 | 196 | public record Results (double product, double luminanceComponent, double contrastComponent, double structureComponent) {} |
139 | 197 | } |
0 commit comments