Extension to Gaussian Blur: support changing blur over time
PiperOrigin-RevId: 597809380
This commit is contained in:
parent
59afb4fb01
commit
78c419e566
@ -99,4 +99,28 @@ public class GaussianBlurTest {
|
||||
assertThat(actualPresentationTimesUs).containsExactly(32_000L);
|
||||
getAndAssertOutputBitmaps(textureBitmapReader, actualPresentationTimesUs, testId, ASSET_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresNonNull({"textureBitmapReader", "testId"})
|
||||
public void gaussianBlur_sigmaChangesWithTime_differentFramesHaveDifferentBlurs()
|
||||
throws Exception {
|
||||
ImmutableList<Long> frameTimesUs = ImmutableList.of(32_000L, 71_000L);
|
||||
ImmutableList<Long> actualPresentationTimesUs =
|
||||
generateAndProcessFrames(
|
||||
BLANK_FRAME_WIDTH,
|
||||
BLANK_FRAME_HEIGHT,
|
||||
frameTimesUs,
|
||||
new SeparableConvolution() {
|
||||
@Override
|
||||
public ConvolutionFunction1D getConvolution(long presentationTimeUs) {
|
||||
return new GaussianFunction(
|
||||
presentationTimeUs < 40_000L ? 5f : 20f, /* numStandardDeviations= */ 2.0f);
|
||||
}
|
||||
},
|
||||
textureBitmapReader,
|
||||
TEXT_SPAN_CONSUMER);
|
||||
|
||||
assertThat(actualPresentationTimesUs).containsExactly(32_000L, 71_000L);
|
||||
getAndAssertOutputBitmaps(textureBitmapReader, actualPresentationTimesUs, testId, ASSET_PATH);
|
||||
}
|
||||
}
|
||||
|
@ -51,11 +51,10 @@ public final class GaussianBlur extends SeparableConvolution {
|
||||
*/
|
||||
public GaussianBlur(float sigma) {
|
||||
this(sigma, /* numStandardDeviations= */ 2.0f);
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConvolutionFunction1D getConvolution() {
|
||||
public ConvolutionFunction1D getConvolution(long presentationTimeUs) {
|
||||
return new GaussianFunction(sigma, numStandardDeviations);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ import static java.lang.Math.exp;
|
||||
import static java.lang.Math.sqrt;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Implementation of a symmetric Gaussian function with a limited domain.
|
||||
@ -68,4 +70,21 @@ public final class GaussianFunction implements ConvolutionFunction1D {
|
||||
return (float)
|
||||
(exp(-samplePositionOverSigma * samplePositionOverSigma / 2) / sqrt(2 * PI) / sigma);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof GaussianFunction)) {
|
||||
return false;
|
||||
}
|
||||
GaussianFunction that = (GaussianFunction) o;
|
||||
return Float.compare(that.sigma, sigma) == 0 && Float.compare(that.numStdDev, numStdDev) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(sigma, numStdDev);
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,12 @@ public abstract class SeparableConvolution implements GlEffect {
|
||||
this.scaleFactor = scaleFactor;
|
||||
}
|
||||
|
||||
/** Returns a {@linkplain ConvolutionFunction1D 1D convolution function}. */
|
||||
public abstract ConvolutionFunction1D getConvolution();
|
||||
/**
|
||||
* Returns a {@linkplain ConvolutionFunction1D 1D convolution function}.
|
||||
*
|
||||
* @param presentationTimeUs The presentation timestamp of the input frame, in microseconds.
|
||||
*/
|
||||
public abstract ConvolutionFunction1D getConvolution(long presentationTimeUs);
|
||||
|
||||
@Override
|
||||
public GlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
|
@ -34,6 +34,7 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import java.io.IOException;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* A {@link GlShaderProgram} for performing separable convolutions.
|
||||
@ -87,6 +88,7 @@ import java.util.concurrent.Executor;
|
||||
private float functionLutCenterX;
|
||||
private float functionLutDomainStart;
|
||||
private float functionLutWidth;
|
||||
private @MonotonicNonNull ConvolutionFunction1D lastConvolutionFunction;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
@ -108,11 +110,12 @@ import java.util.concurrent.Executor;
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = (frameProcessingException) -> {};
|
||||
|
||||
errorListenerExecutor = MoreExecutors.directExecutor();
|
||||
lastInputSize = Size.ZERO;
|
||||
intermediateSize = Size.ZERO;
|
||||
outputSize = Size.ZERO;
|
||||
lastConvolutionFunction = null;
|
||||
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
sharpTransformGlProgram =
|
||||
@ -160,7 +163,7 @@ import java.util.concurrent.Executor;
|
||||
+ " first.");
|
||||
try {
|
||||
ensureTexturesAreConfigured(
|
||||
glObjectsProvider, new Size(inputTexture.width, inputTexture.height));
|
||||
glObjectsProvider, new Size(inputTexture.width, inputTexture.height), presentationTimeUs);
|
||||
outputTextureInUse = true;
|
||||
renderHorizontal(inputTexture);
|
||||
renderVertical();
|
||||
@ -269,10 +272,15 @@ import java.util.concurrent.Executor;
|
||||
renderOnePass(intermediateTexture.texId, /* isHorizontal= */ false);
|
||||
}
|
||||
|
||||
private void ensureTexturesAreConfigured(GlObjectsProvider glObjectsProvider, Size inputSize)
|
||||
private void ensureTexturesAreConfigured(
|
||||
GlObjectsProvider glObjectsProvider, Size inputSize, long presentationTimeUs)
|
||||
throws GlUtil.GlException {
|
||||
// Always update the function texture, as it could change on each render cycle.
|
||||
updateFunctionTexture(glObjectsProvider);
|
||||
ConvolutionFunction1D currentConvolutionFunction =
|
||||
convolution.getConvolution(presentationTimeUs);
|
||||
if (!currentConvolutionFunction.equals(lastConvolutionFunction)) {
|
||||
updateFunctionTexture(glObjectsProvider, currentConvolutionFunction);
|
||||
lastConvolutionFunction = currentConvolutionFunction;
|
||||
}
|
||||
|
||||
// Only update intermediate and output textures if the size changes.
|
||||
if (inputSize.equals(lastInputSize)) {
|
||||
@ -295,11 +303,10 @@ import java.util.concurrent.Executor;
|
||||
* Creates a function lookup table for the convolution, and stores it in a 16b floating point
|
||||
* texture for GPU access.
|
||||
*/
|
||||
private void updateFunctionTexture(GlObjectsProvider glObjectsProvider)
|
||||
private void updateFunctionTexture(
|
||||
GlObjectsProvider glObjectsProvider, ConvolutionFunction1D convolutionFunction)
|
||||
throws GlUtil.GlException {
|
||||
|
||||
ConvolutionFunction1D convolutionFunction = convolution.getConvolution();
|
||||
|
||||
int lutRasterSize =
|
||||
(int)
|
||||
Math.ceil(
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
Loading…
x
Reference in New Issue
Block a user