Add support for skipping windows in MssimCalculator.

By skipping every other row and column, SSIM calculation time reduces by 10-30%.

PiperOrigin-RevId: 474286702
This commit is contained in:
samrobinson 2022-09-14 14:35:12 +00:00 committed by Marc Baechinger
parent 3e0ad6d804
commit 6dd2a6dac6
2 changed files with 55 additions and 3 deletions

View File

@ -44,23 +44,47 @@ import static java.lang.Math.pow;
private MssimCalculator() {}
/**
* Calculates the Mean Structural Similarity (MSSIM) between two images with window skipping.
*
* @see #calculate(byte[], byte[], int, int, boolean).
*/
public static double calculate(
byte[] referenceBuffer, byte[] distortedBuffer, int width, int height) {
return calculate(
referenceBuffer, distortedBuffer, width, height, /* enableWindowSkipping= */ true);
}
/**
* Calculates the Mean Structural Similarity (MSSIM) between two images.
*
* <p>The images are split into a grid of windows. For each window, the structural similarity
* (SSIM) is calculated. The MSSIM returned from this method is the mean of these SSIM values. If
* window skipping is enabled, only every other row and column are considered, thereby only one in
* four windows are evaluated.
*
* @param referenceBuffer The luma channel (Y) buffer of the reference image.
* @param distortedBuffer The luma channel (Y) buffer of the distorted image.
* @param width The image width in pixels.
* @param height The image height in pixels.
* @param enableWindowSkipping Whether to skip every other row and column when evaluating windows
* for SSIM calculation.
* @return The MSSIM score between the input images.
*/
public static double calculate(
byte[] referenceBuffer, byte[] distortedBuffer, int width, int height) {
byte[] referenceBuffer,
byte[] distortedBuffer,
int width,
int height,
boolean enableWindowSkipping) {
double totalSsim = 0;
int windowsCount = 0;
for (int currentWindowY = 0; currentWindowY < height; currentWindowY += WINDOW_SIZE) {
int dimensionIncrement = WINDOW_SIZE * (enableWindowSkipping ? 2 : 1);
for (int currentWindowY = 0; currentWindowY < height; currentWindowY += dimensionIncrement) {
int windowHeight = computeWindowSize(currentWindowY, height);
for (int currentWindowX = 0; currentWindowX < width; currentWindowX += WINDOW_SIZE) {
for (int currentWindowX = 0; currentWindowX < width; currentWindowX += dimensionIncrement) {
windowsCount++;
int windowWidth = computeWindowSize(currentWindowX, width);
int bufferIndexOffset =

View File

@ -62,6 +62,34 @@ public class MssimCalculatorTest {
.isEqualTo(63);
}
@Test
public void calculateSsim_withWindowSkipping_similarToWithout() throws Exception {
Bitmap referenceBitmap = readBitmap("media/bitmap/sample_mp4_first_frame/original.png");
Bitmap distortedBitmap =
readBitmap("media/bitmap/sample_mp4_first_frame/increase_brightness.png");
byte[] referenceLuminosity = bitmapToLuminosityArray(referenceBitmap);
byte[] distortedLuminosity = bitmapToLuminosityArray(distortedBitmap);
assertThat(
(int)
(MssimCalculator.calculate(
referenceLuminosity,
distortedLuminosity,
referenceBitmap.getWidth(),
referenceBitmap.getHeight(),
/* enableWindowSkipping= */ false)
* 100))
.isEqualTo(
(int)
(MssimCalculator.calculate(
referenceLuminosity,
distortedLuminosity,
referenceBitmap.getWidth(),
referenceBitmap.getHeight(),
/* enableWindowSkipping= */ true)
* 100));
}
private static Bitmap readBitmap(String assetString) throws IOException {
try (InputStream inputStream = getApplicationContext().getAssets().open(assetString)) {
return BitmapFactory.decodeStream(inputStream);