Support upscaling with LanczosResample
Avoid scaling the lanczos windown function when scaling up. When scaling up (output > input), we shouldn't scale the window function or we risk not sampling any of the input pixels. PiperOrigin-RevId: 641838149
This commit is contained in:
parent
228d63848e
commit
abf601feaa
@ -53,8 +53,11 @@ public class LanczosResampleTest {
|
|||||||
@Rule public final TestName testName = new TestName();
|
@Rule public final TestName testName = new TestName();
|
||||||
|
|
||||||
private static final String ORIGINAL_JPG_ASSET_PATH = "media/jpeg/ultraHDR.jpg";
|
private static final String ORIGINAL_JPG_ASSET_PATH = "media/jpeg/ultraHDR.jpg";
|
||||||
|
private static final String SMALLER_JPG_ASSET_PATH = "media/jpeg/london.jpg";
|
||||||
private static final String DOWNSCALED_6X_PNG_ASSET_PATH =
|
private static final String DOWNSCALED_6X_PNG_ASSET_PATH =
|
||||||
"test-generated-goldens/LanczosResampleTest/ultraHDR_512x680.png";
|
"test-generated-goldens/LanczosResampleTest/ultraHDR_512x680.png";
|
||||||
|
private static final String UPSCALED_3X_PNG_ASSET_PATH =
|
||||||
|
"test-generated-goldens/LanczosResampleTest/london_3060x2304.jpg";
|
||||||
|
|
||||||
private final Context context = getApplicationContext();
|
private final Context context = getApplicationContext();
|
||||||
|
|
||||||
@ -63,20 +66,12 @@ public class LanczosResampleTest {
|
|||||||
private @MonotonicNonNull EGLContext eglContext;
|
private @MonotonicNonNull EGLContext eglContext;
|
||||||
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
private @MonotonicNonNull EGLSurface placeholderEglSurface;
|
||||||
private @MonotonicNonNull GlShaderProgram lanczosShaderProgram;
|
private @MonotonicNonNull GlShaderProgram lanczosShaderProgram;
|
||||||
private int inputTexId;
|
|
||||||
private int inputWidth;
|
|
||||||
private int inputHeight;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void createGlObjects() throws Exception {
|
public void createGlObjects() throws Exception {
|
||||||
eglDisplay = GlUtil.getDefaultEglDisplay();
|
eglDisplay = GlUtil.getDefaultEglDisplay();
|
||||||
eglContext = GlUtil.createEglContext(eglDisplay);
|
eglContext = GlUtil.createEglContext(eglDisplay);
|
||||||
placeholderEglSurface = GlUtil.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
|
placeholderEglSurface = GlUtil.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
|
||||||
|
|
||||||
Bitmap inputBitmap = readBitmap(ORIGINAL_JPG_ASSET_PATH);
|
|
||||||
inputWidth = inputBitmap.getWidth();
|
|
||||||
inputHeight = inputBitmap.getHeight();
|
|
||||||
inputTexId = createGlTextureFromBitmap(inputBitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -94,20 +89,15 @@ public class LanczosResampleTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void queueInputFrame_with6xDownscale_matchesGoldenFile() throws Exception {
|
public void queueInputFrame_with6xDownscale_matchesGoldenFile() throws Exception {
|
||||||
|
GlTextureInfo inputTextureInfo = setupInputTexture(ORIGINAL_JPG_ASSET_PATH);
|
||||||
float scale = 1f / 6;
|
float scale = 1f / 6;
|
||||||
Size outputSize = new Size((int) (inputWidth * scale), (int) (inputHeight * scale));
|
Size outputSize =
|
||||||
|
new Size((int) (inputTextureInfo.width * scale), (int) (inputTextureInfo.height * scale));
|
||||||
lanczosShaderProgram =
|
lanczosShaderProgram =
|
||||||
LanczosResample.scaleToFit(outputSize.getWidth(), outputSize.getHeight())
|
LanczosResample.scaleToFit(outputSize.getWidth(), outputSize.getHeight())
|
||||||
.toGlShaderProgram(context, /* useHdr= */ false);
|
.toGlShaderProgram(context, /* useHdr= */ false);
|
||||||
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
Bitmap expectedBitmap = readBitmap(DOWNSCALED_6X_PNG_ASSET_PATH);
|
Bitmap expectedBitmap = readBitmap(DOWNSCALED_6X_PNG_ASSET_PATH);
|
||||||
GlTextureInfo inputTextureInfo =
|
|
||||||
new GlTextureInfo(
|
|
||||||
inputTexId,
|
|
||||||
/* fboId= */ C.INDEX_UNSET,
|
|
||||||
/* rboId= */ C.INDEX_UNSET,
|
|
||||||
inputWidth,
|
|
||||||
inputHeight);
|
|
||||||
|
|
||||||
lanczosShaderProgram.queueInputFrame(
|
lanczosShaderProgram.queueInputFrame(
|
||||||
new DefaultGlObjectsProvider(eglContext), inputTextureInfo, /* presentationTimeUs= */ 0);
|
new DefaultGlObjectsProvider(eglContext), inputTextureInfo, /* presentationTimeUs= */ 0);
|
||||||
@ -118,6 +108,37 @@ public class LanczosResampleTest {
|
|||||||
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
|
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queueInputFrame_with3xUpscale_matchesGoldenFile() throws Exception {
|
||||||
|
GlTextureInfo inputTextureInfo = setupInputTexture(SMALLER_JPG_ASSET_PATH);
|
||||||
|
float scale = 3;
|
||||||
|
Size outputSize =
|
||||||
|
new Size((int) (inputTextureInfo.width * scale), (int) (inputTextureInfo.height * scale));
|
||||||
|
lanczosShaderProgram =
|
||||||
|
LanczosResample.scaleToFit(outputSize.getWidth(), outputSize.getHeight())
|
||||||
|
.toGlShaderProgram(context, /* useHdr= */ false);
|
||||||
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
|
Bitmap expectedBitmap = readBitmap(UPSCALED_3X_PNG_ASSET_PATH);
|
||||||
|
|
||||||
|
lanczosShaderProgram.queueInputFrame(
|
||||||
|
new DefaultGlObjectsProvider(eglContext), inputTextureInfo, /* presentationTimeUs= */ 0);
|
||||||
|
Bitmap actualBitmap =
|
||||||
|
createArgb8888BitmapFromFocusedGlFramebuffer(outputSize.getWidth(), outputSize.getHeight());
|
||||||
|
|
||||||
|
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual", actualBitmap, /* path= */ null);
|
||||||
|
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlTextureInfo setupInputTexture(String path) throws Exception {
|
||||||
|
Bitmap inputBitmap = readBitmap(path);
|
||||||
|
return new GlTextureInfo(
|
||||||
|
createGlTextureFromBitmap(inputBitmap),
|
||||||
|
/* fboId= */ C.INDEX_UNSET,
|
||||||
|
/* rboId= */ C.INDEX_UNSET,
|
||||||
|
inputBitmap.getWidth(),
|
||||||
|
inputBitmap.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
private void setupOutputTexture(int outputWidth, int outputHeight) throws Exception {
|
private void setupOutputTexture(int outputWidth, int outputHeight) throws Exception {
|
||||||
int outputTexId =
|
int outputTexId =
|
||||||
GlUtil.createTexture(
|
GlUtil.createTexture(
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package androidx.media3.effect;
|
package androidx.media3.effect;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
import static java.lang.Math.min;
|
||||||
import static java.lang.Math.round;
|
import static java.lang.Math.round;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -92,7 +93,10 @@ public final class LanczosResample implements GlEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConvolutionFunction1D getConvolution(long presentationTimeUs) {
|
public ConvolutionFunction1D getConvolution(long presentationTimeUs) {
|
||||||
return new ScaledLanczosFunction(radius, scale);
|
// When scaling down (scale < 1), scale the kernel function to ensure we sample all input
|
||||||
|
// pixels inside the kernel radius.
|
||||||
|
// When scaling up (scale > 1), leave the kernel function unchanged.
|
||||||
|
return new ScaledLanczosFunction(radius, min(scale, 1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 286 KiB |
Loading…
x
Reference in New Issue
Block a user