Skip to content

Commit

Permalink
Add RgbAdjustment class to build RgbaMatrices
Browse files Browse the repository at this point in the history
* Add RgbaMatrix interface implementation.
* Add Builder class for easy adjustments.
* Adjust existing RgbaMatrixPixelTests to use new RgbAdjustment class.

PiperOrigin-RevId: 465545429
  • Loading branch information
leonwind authored and marcbaechinger committed Oct 19, 2022
1 parent e63d594 commit 6502fce
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.Matrix;
import android.util.Pair;
import androidx.media3.common.FrameProcessingException;
import androidx.test.ext.junit.runners.AndroidJUnit4;
Expand All @@ -48,19 +47,17 @@
* as recommended in {@link GlEffectsFrameProcessorPixelTest}.
*/
@RunWith(AndroidJUnit4.class)
public final class RgbaMatrixPixelTest {
public final class RgbAdjustmentPixelTest {
public static final String ORIGINAL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/original.png";
public static final String ONLY_RED_CHANNEL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/only_red_channel.png";
public static final String INCREASE_RED_CHANNEL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/increase_red_channel.png";
public static final String INCREASE_BRIGHTNESS_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/increase_brightness.png";
public static final String GRAYSCALE_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/grayscale.png";
public static final int COLOR_MATRIX_RED_INDEX = 0;
public static final int COLOR_MATRIX_GREEN_INDEX = 5;
public static final int COLOR_MATRIX_BLUE_INDEX = 10;
public static final int COLOR_MATRIX_ALPHA_INDEX = 15;

private final Context context = getApplicationContext();

Expand Down Expand Up @@ -113,9 +110,8 @@ private static RgbaMatrixProcessor createRgbaMatrixProcessor(Context context, fl
@Test
public void drawFrame_identityMatrix_leavesFrameUnchanged() throws Exception {
String testId = "drawFrame_identityMatrix";
float[] identityMatrix = new float[16];
Matrix.setIdentityM(identityMatrix, /* smOffset= */ 0);
rgbaMatrixProcessor = createRgbaMatrixProcessor(context, identityMatrix);
RgbaMatrix identityMatrix = new RgbAdjustment.Builder().build();
rgbaMatrixProcessor = new RgbaMatrixProcessor(context, identityMatrix, /* useHdr= */ false);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ORIGINAL_PNG_ASSET_PATH);

Expand All @@ -135,12 +131,9 @@ public void drawFrame_identityMatrix_leavesFrameUnchanged() throws Exception {
@Test
public void drawFrame_removeColors_producesBlackFrame() throws Exception {
String testId = "drawFrame_removeColors";
float[] removeColorFilter = new float[16];
Matrix.setIdentityM(removeColorFilter, /* smOffset= */ 0);
removeColorFilter[COLOR_MATRIX_RED_INDEX] = 0;
removeColorFilter[COLOR_MATRIX_GREEN_INDEX] = 0;
removeColorFilter[COLOR_MATRIX_BLUE_INDEX] = 0;
rgbaMatrixProcessor = createRgbaMatrixProcessor(context, removeColorFilter);
RgbaMatrix removeColorMatrix =
new RgbAdjustment.Builder().setRedScale(0).setGreenScale(0).setBlueScale(0).build();
rgbaMatrixProcessor = new RgbaMatrixProcessor(context, removeColorMatrix, /* useHdr= */ false);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap =
BitmapTestUtil.createArgb8888BitmapWithSolidColor(
Expand All @@ -162,11 +155,8 @@ public void drawFrame_removeColors_producesBlackFrame() throws Exception {
@Test
public void drawFrame_redOnlyFilter_setsBlueAndGreenValuesToZero() throws Exception {
String testId = "drawFrame_redOnlyFilter";
float[] redOnlyFilter = new float[16];
Matrix.setIdentityM(redOnlyFilter, /* smOffset= */ 0);
redOnlyFilter[COLOR_MATRIX_GREEN_INDEX] = 0;
redOnlyFilter[COLOR_MATRIX_BLUE_INDEX] = 0;
rgbaMatrixProcessor = createRgbaMatrixProcessor(context, redOnlyFilter);
RgbaMatrix redOnlyMatrix = new RgbAdjustment.Builder().setBlueScale(0).setGreenScale(0).build();
rgbaMatrixProcessor = new RgbaMatrixProcessor(context, redOnlyMatrix, /* useHdr= */ false);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(ONLY_RED_CHANNEL_PNG_ASSET_PATH);

Expand All @@ -183,13 +173,34 @@ public void drawFrame_redOnlyFilter_setsBlueAndGreenValuesToZero() throws Except
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
}

@Test
public void drawFrame_increaseRedChannel_producesBrighterAndRedderFrame() throws Exception {
String testId = "drawFrame_increaseRedChannel";
RgbaMatrix increaseRedMatrix = new RgbAdjustment.Builder().setRedScale(5).build();
rgbaMatrixProcessor = new RgbaMatrixProcessor(context, increaseRedMatrix, /* useHdr= */ false);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(INCREASE_RED_CHANNEL_PNG_ASSET_PATH);

rgbaMatrixProcessor.drawFrame(inputTexId, /* presentationTimeUs= */ 0);
Bitmap actualBitmap =
BitmapTestUtil.createArgb8888BitmapFromCurrentGlFramebuffer(
outputSize.first, outputSize.second);

BitmapTestUtil.maybeSaveTestBitmapToCacheDirectory(
testId, /* bitmapLabel= */ "actual", actualBitmap);
float averagePixelAbsoluteDifference =
BitmapTestUtil.getAveragePixelAbsoluteDifferenceArgb8888(
expectedBitmap, actualBitmap, testId);
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
}

@Test
public void drawFrame_increaseBrightness_increasesAllValues() throws Exception {
String testId = "drawFrame_increaseBrightness";
float[] increaseBrightnessMatrix = new float[16];
Matrix.setIdentityM(increaseBrightnessMatrix, /* smOffset= */ 0);
Matrix.scaleM(increaseBrightnessMatrix, /* mOffset= */ 0, /* x= */ 5, /* y= */ 5, /* z= */ 5);
rgbaMatrixProcessor = createRgbaMatrixProcessor(context, increaseBrightnessMatrix);
RgbaMatrix increaseBrightnessMatrix =
new RgbAdjustment.Builder().setRedScale(5).setGreenScale(5).setBlueScale(5).build();
rgbaMatrixProcessor =
new RgbaMatrixProcessor(context, increaseBrightnessMatrix, /* useHdr = */ false);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(INCREASE_BRIGHTNESS_PNG_ASSET_PATH);

Expand All @@ -207,15 +218,16 @@ public void drawFrame_increaseBrightness_increasesAllValues() throws Exception {
}

@Test
// TODO(b/239430283): Move test to RgbFilterPixelTest once it exists.
public void drawFrame_grayscale_producesGrayscaleImage() throws Exception {
String testId = "drawFrame_grayscale";
// Grayscale transformation matrix with the BT.709 standard from
// https://en.wikipedia.org/wiki/Grayscale#Converting_colour_to_grayscale
float[] grayscaleFilter = {
float[] grayscaleMatrix = {
0.2126f, 0.2126f, 0.2126f, 0, 0.7152f, 0.7152f, 0.7152f, 0, 0.0722f, 0.0722f, 0.0722f, 0, 0,
0, 0, 1
};
rgbaMatrixProcessor = createRgbaMatrixProcessor(context, grayscaleFilter);
rgbaMatrixProcessor = createRgbaMatrixProcessor(/* context= */ context, grayscaleMatrix);
Pair<Integer, Integer> outputSize = rgbaMatrixProcessor.configure(inputWidth, inputHeight);
Bitmap expectedBitmap = BitmapTestUtil.readBitmap(GRAYSCALE_PNG_ASSET_PATH);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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 androidx.media3.effect;

import static com.google.android.exoplayer2.util.Assertions.checkArgument;

import android.opengl.Matrix;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Scales the red, green, and blue color channels of a frame. */
public final class RgbAdjustment implements RgbaMatrix {

/** A builder for {@link RgbAdjustment} instances. */
public static final class Builder {
private float redScale;
private float greenScale;
private float blueScale;

/** Creates a new instance with default values. */
public Builder() {
redScale = 1;
greenScale = 1;
blueScale = 1;
}

/**
* Scales the red channel of the frame by {@param redScale}.
*
* @param redScale The scale to apply to the red channel. Needs to be non-negative and the
* default value is {@code 1}.
*/
@CanIgnoreReturnValue
public Builder setRedScale(float redScale) {
checkArgument(0 <= redScale, "Red scale needs to be non-negative.");
this.redScale = redScale;
return this;
}

/**
* Scales the green channel of the frame by {@param greenScale}.
*
* @param greenScale The scale to apply to the green channel. Needs to be non-negative and the
* default value is {@code 1}.
*/
@CanIgnoreReturnValue
public Builder setGreenScale(float greenScale) {
checkArgument(0 <= greenScale, "Green scale needs to be non-negative.");
this.greenScale = greenScale;
return this;
}

/**
* Scales the blue channel of the frame by {@param blueScale}.
*
* @param blueScale The scale to apply to the blue channel. Needs to be non-negative and the
* default value is {@code 1}.
*/
@CanIgnoreReturnValue
public Builder setBlueScale(float blueScale) {
checkArgument(0 <= blueScale, "Blue scale needs to be non-negative.");
this.blueScale = blueScale;
return this;
}

/** Creates a new {@link RgbAdjustment} instance. */
public RgbAdjustment build() {
float[] rgbaMatrix = new float[16];
Matrix.setIdentityM(rgbaMatrix, /* smOffset= */ 0);
Matrix.scaleM(
rgbaMatrix,
/* smOffset= */ 0,
/* x= */ redScale,
/* y= */ greenScale,
/* z= */ blueScale);

return new RgbAdjustment(rgbaMatrix);
}
}

private final float[] rgbaMatrix;

private RgbAdjustment(float[] rgbaMatrix) {
this.rgbaMatrix = rgbaMatrix;
}

@Override
public float[] getMatrix(long presentationTimeUs) {
return rgbaMatrix;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6502fce

Please sign in to comment.