ImageProcessor.java
/*
* 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.camera.core;
import android.graphics.PixelFormat;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* Post-processing effect for images.
*
* <p>This interface is for post-processing images. The input is an image from the camera, with the
* instructions on how to process it; the output is a processed image. CameraX forwards images to
* the implementation, and delivers the processed images to back the app.
*
* <p>Currently, it can only be used with the {@link ImageCapture} by targeting
* {@link CameraEffect#IMAGE_CAPTURE}.
*
* <p>If the implementation fails to process the input, it should throw
* {@link ProcessingException}. The error will be caught by CameraX and propagated to the app via
* error callbacks such as {@link ImageCapture.OnImageSavedCallback#onError} or
* {@link ImageCapture.OnImageCapturedCallback#onError}.
*
* <p>Code sample:
* <pre><code>
* class ImageProcessorImpl implements ImageProcessor {
* Response process(Request request) throws ProcessingException {
* try {
* ImageProxy image = request.getInputImages().get(0);
* ByteBuffer byteBuffer = image.getPlanes()[0];
* // Process the content of byteBuffer and create a Response object.
* } catch(Exception e) {
* throws new ProcessingException(e);
* }
* }
* }
* </code></pre>
*
* @see CameraEffect
*/
public interface ImageProcessor {
/**
* Accepts original image from CameraX and returns processed image.
*
* <p>CameraX invokes this method for each new incoming image. It's invoked on the
* {@link Executor} provided in {@link CameraEffect}'s constructor. It might be called in
* parallel, should the {@link Executor} allow multi-threading. The implementation must block
* the current calling thread until the output image is returned.
*
* <p>The implementation must follow the instruction in the {@link Request} to process the
* input image. For example, it must produce an output image with the format following the
* JavaDoc of {@link Request#getInputImage()}. Failing to do so might cause the processing to
* fail. For example, for {@link ImageCapture}, it will cause the
* {@link ImageCapture#takePicture} call to fail.
*
* <p>The implementation must throw a {@link ProcessingException} if it fails to process the
* {@link Request}. CameraX will catch the error and deliver it to the app via error
* callbacks. For {@link ImageCapture}, the error callbacks are
* {@link ImageCapture.OnImageCapturedCallback#onError} or
* {@link ImageCapture.OnImageSavedCallback#onError}.
*
* @param request a {@link Request} that contains the original image.
* @return a {@link Response} that contains the processed image.
* @throws ProcessingException if the implementation fails to process the {@link Request}.
*/
@NonNull
Response process(@NonNull Request request) throws ProcessingException;
/**
* Valid output formats.
*
* <p>{@link Request#getOutputFormat()} can only return the formats defined by this annotation.
*/
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef(value = {PixelFormat.RGBA_8888})
@interface OutputFormats {
}
/**
* A request for processing one or multiple {@link ImageProxy}.
*/
interface Request {
/**
* Gets the input images.
*
* <p>Return a single image captured by the camera. The implementation should check the
* format of the image before processing it. For example, checking the value of
* {@link ImageProxy#getFormat()}, {@link ImageProxy.PlaneProxy#getRowStride()} and/or
* {@link ImageProxy.PlaneProxy#getPixelStride()}.
*
* <p>Currently, the image format is always {@link PixelFormat#RGBA_8888} with pixel
* stride equals to 4 and row stride equals to width * 4.
*/
@NonNull
ImageProxy getInputImage();
/**
* Gets the output image format.
*
* <p>The return value will one of the values in the table. The implementation must
* create the {@link Response} {@link ImageProxy} following the corresponding
* instruction, or the processing may fail.
*
* <table>
* <tr>
* <th>Value</th>
* <th>Instruction</th>
* </tr>
* <tr>
* <td>{@link PixelFormat#RGBA_8888}</td>
* <td>The output image must contain a single plane with a pixel stride of 4 and a
* row stride of width * 4. e.g. each pixel is stored on 4 bytes and each RGBA
* channel is stored with 8 bits of precision. For more details, see the JavaDoc of
* {@code Bitmap.Config#ARGB_8888}.</td>
* </tr>
* </table>
*/
@OutputFormats
int getOutputFormat();
}
/**
* A response for returning a processed {@link ImageProxy} to CameraX.
*/
interface Response {
/**
* Gets the output image returned by the {@link ImageProcessor}.
*
* <p>{@link ImageProcessor} should implement the {@link ImageProxy} and
* {@link ImageProxy.PlaneProxy} interfaces to create the return value. Once
* return, CameraX will inject the image back to the processing pipeline.
*
* <p>The {@link ImageProxy} must follow the instruction in the request, or CameraX may
* throw error. For example, the image format must match the description of the
* {@link Request#getOutputFormat()}.
*
* @return the output image.
*/
@NonNull
ImageProxy getOutputImage();
}
}