Add RgbAdjustment class to build RgbaMatrices
* Add RgbaMatrix interface implementation. * Add Builder class for easy adjustments. * Adjust existing RgbaMatrixPixelTests to use new RgbAdjustment class. PiperOrigin-RevId: 465545429
This commit is contained in:
parent
348662283a
commit
a76dbfd363
@ -27,7 +27,6 @@ import android.graphics.Color;
|
||||
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.media3.common.util.GlUtil;
|
||||
@ -48,19 +47,17 @@ import org.junit.runner.RunWith;
|
||||
* 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();
|
||||
|
||||
@ -113,9 +110,8 @@ public final class RgbaMatrixPixelTest {
|
||||
@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);
|
||||
|
||||
@ -135,12 +131,9 @@ public final class RgbaMatrixPixelTest {
|
||||
@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(
|
||||
@ -162,11 +155,8 @@ public final class RgbaMatrixPixelTest {
|
||||
@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);
|
||||
|
||||
@ -183,13 +173,34 @@ public final class RgbaMatrixPixelTest {
|
||||
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);
|
||||
|
||||
@ -207,15 +218,16 @@ public final class RgbaMatrixPixelTest {
|
||||
}
|
||||
|
||||
@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);
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.opengl.Matrix;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
|
||||
/** Scales the red, green, and blue color channels of a frame. */
|
||||
@UnstableApi
|
||||
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;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 462 KiB |
Loading…
x
Reference in New Issue
Block a user