ImageReader常用方法讲解

ImageReader常用方法讲解

ImageReader类允许应用程序直接访问渲染到Surface中的图像数据
几个Android媒体API类接受Surface对象作为渲染目标,包括MediaPlayer, MediaCodec, Android .hardware.camera2CameraDevice, ImageWriter和RenderScript分配。可用于每个源的图像大小和格式各不相同,应在特定API的文档中进行检查。

图像数据被封装在image对象中,并且可以同时访问多个这样的对象,最多可以访问由maxImages构造函数参数指定的数量。通过Surface发送到ImageReader的新图像被排队,直到通过acquireLatestImage或acquireNextImage调用访问。由于内存限制,如果ImageReader不能以与生成速率相等的速率获取和释放图像,那么图像源最终会在试图渲染到Surface时停止或丢弃图像

newInstance(width,height,format,maxImages)

ImageReader newInstance(@IntRange(from = 1) int width,@IntRange(from = 1) int height,@Format int format,@IntRange(from = 1) int maxImages)

文档注释解释

  1. 创建图像阅读器
    • 此方法用于创建一个新的图像阅读器 (ImageReader) 实例,用于获取指定大小和格式的图像数据。
  2. maxImages 参数
    • maxImages 参数确定可以同时从 ImageReader 中获取的最大 Image 对象数量。请求更多缓冲区会占用更多内存,因此应根据使用情况仅使用必要的最小数量。
  3. 图像大小和格式
    • 可用的大小和格式取决于图像数据的来源。
  4. 私有格式 (ImageFormat#PRIVATE)
    • 如果指定的 formatImageFormat#PRIVATE,则创建的 ImageReader 将生成应用程序无法直接访问的图像。应用程序仍然可以从该 ImageReader 获取图像,并通过 ImageWriter 接口将其发送到相机 (CameraDevice) 进行重新处理。
    • 对于 ImageFormat#PRIVATE 格式的图像,调用 Image#getPlanes() 将返回一个空数组。应用程序可以通过调用 getImageFormat() 来检查现有阅读器的格式。android.graphics.ImageFormat.java
  5. 私有格式的效率
    • 使用 ImageFormat#PRIVATE 格式的 ImageReader 在应用程序不需要访问图像数据时更高效,相比使用其他格式(例如 ImageFormat#YUV_420_888)的 ImageReader 更加高效。

方法实现解释

该方法实现了 newInstance,根据传入的参数创建一个新的 ImageReader 实例,并返回该实例。

  • 参数说明
    • widthheight:生成图像的默认宽度和高度(以像素为单位)。
    • format:生成图像的格式,必须是 android.graphics.ImageFormatandroid.graphics.PixelFormat 常量之一。
    • maxImages:用户同时想要访问的最大图像数量,应尽可能小以限制内存使用。
  • 创建实例
    • 根据 format 的值判断是否为 ImageFormat#PRIVATE,如果是,则创建 ImageReader 实例时不指定 HardwareBuffer.USAGE_CPU_READ_OFTEN
    • 最终创建并返回一个新的 ImageReader 实例。

注意事项

  • 调用该方法前,应确保传入的参数合法且符合预期的图像大小和格式。
  • 需要根据应用程序的需求和设备支持情况选择合适的图像格式和大小。
  • 注意 maxImages 参数的设置,应根据实际需求尽量保持较小的值,以限制内存使用。
    public static @NonNull ImageReader newInstance(
            @IntRange(from = 1) int width,
            @IntRange(from = 1) int height,
            @Format             int format,
            @IntRange(from = 1) int maxImages) {
        // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
        // work, and is inscrutable anyway
        return new ImageReader(width, height, format, maxImages,
                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, null);
    }
newInstance(width,height,format,maxImages,usage)

ImageReader newInstance(@IntRange(from = 1) int width,@IntRange(from = 1) int height,@Formatint format,@IntRange(from = 1) int maxImages,@Usage long usage)

文档注释解释

  1. 创建图像阅读器
    • 此方法用于创建一个新的图像阅读器 (ImageReader) 实例,用于获取指定大小、格式和使用标志的图像数据。
  2. maxImages 参数
    • maxImages 参数确定可以同时从 ImageReader 中获取的最大 Image 对象数量。请求更多缓冲区会占用更多内存,因此应根据使用情况仅使用必要的最小数量。
  3. 图像大小和格式
    • 可用的大小和格式取决于图像数据的来源。
  4. 格式和使用标志组合
    • formatusage 标志组合描述了缓冲区将被消费端点如何使用的方式。例如,如果应用程序打算将图像发送到 MediaCodecMediaRecorder 进行硬件视频编码,则格式和使用标志组合需要是 ImageFormat#PRIVATEHardwareBuffer#USAGE_VIDEO_ENCODE
    • 当使用有效大小和此类格式/使用标志组合创建 ImageReader 对象时,应用程序可以将图像发送到通过 MediaCodecMediaRecorder 提供的输入 SurfaceImageWriter
  5. 私有格式 (ImageFormat#PRIVATE)
    • 如果指定的 formatImageFormat#PRIVATE,则创建的 ImageReader 将生成应用程序无法直接访问的图像。应用程序仍然可以从该 ImageReader 获取图像,并将其发送到 CameraDevice 进行重新处理,或者通过 ImageWriter 接口将其发送到 MediaCodec / MediaRecorder 进行硬件视频编码。
  6. 有效性和效率
    • 使用 ImageFormat#PRIVATE 格式的 ImageReader 在应用程序不需要访问图像数据时更高效,相比使用其他格式(如 ImageFormat#YUV_420_888)的 ImageReader 更加高效。
  7. 支持的格式和使用标志组合
    • 文档中列出了 ImageReader 支持的格式和使用标志组合列表,如果使用不支持的组合可能会导致 IllegalArgumentException 异常。
  • 参数说明
    • widthheight:生成图像的默认宽度和高度(以像素为单位)。
    • format:生成图像的格式,必须是 android.graphics.ImageFormatandroid.graphics.PixelFormat 常量之一。
    • maxImages:用户同时想要访问的最大图像数量,应尽可能小以限制内存使用。
    • usage:用于指定图像使用方式的标志,应遵循 HardwareBuffer 类的使用位。
    public static @NonNull ImageReader newInstance(
            @IntRange(from = 1) int width,
            @IntRange(from = 1) int height,
            @Format             int format,
            @IntRange(from = 1) int maxImages,
            @Usage              long usage) {
        // TODO: Check this - can't do it just yet because format support is different
        // Unify formats! The only reliable way to validate usage is to just try it and see.

//        if (!HardwareBuffer.isSupported(width, height, format, 1, usage)) {
//            throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
//                + " & usage=" + Long.toHexString(usage) + " is not supported");
//        }
        return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null);
    }
@Usage 注解
  • @Retention(RetentionPolicy.SOURCE): 指定注解仅在源代码级别保留,不会包含在编译后的字节码中。
  • @LongDef(flag = true, value = { ... }): 指定该注解用于标记 long 类型的标志常量,其值是给定数组中列出的常量之一。

常量定义

  1. USAGE_CPU_READ_RARELY(2):
    • 表示缓冲区偶尔会被 CPU 读取。
  2. USAGE_CPU_READ_OFTEN(3):
    • 表示缓冲区经常会被 CPU 读取。
  3. USAGE_CPU_WRITE_RARELY(32):
    • 表示缓冲区偶尔会被 CPU 写入。
  4. USAGE_CPU_WRITE_OFTEN(48):
    • 表示缓冲区经常会被 CPU 写入。
  5. USAGE_GPU_SAMPLED_IMAGE(256):
    • 表示缓冲区将被 GPU 用作采样图像。
  6. USAGE_GPU_COLOR_OUTPUT(512):
    • 表示缓冲区将被 GPU 用作颜色输出。
  7. USAGE_COMPOSER_OVERLAY(2048):
    • 表示缓冲区将作为硬件合成器叠加层使用,通过 SurfaceControl 显示。
  8. USAGE_PROTECTED_CONTENT(16384):
    • 表示缓冲区包含受保护内容,不允许在受保护的硬件路径之外使用。
  9. USAGE_VIDEO_ENCODE(65536):
    • 表示缓冲区将被硬件视频编码器读取。
  10. USAGE_SENSOR_DIRECT_DATA(8388608):
    • 表示缓冲区将用于传感器直接数据。
  11. USAGE_GPU_DATA_BUFFER(16777216):
    • 表示缓冲区将用作着色器存储或统一缓冲对象。
  12. USAGE_GPU_CUBE_MAP(33554432):
    • 表示缓冲区将用作立方体贴图。
  13. USAGE_GPU_MIPMAP_COMPLETE(67108864):
    • 表示缓冲区包含完整的 mipmap 层次结构。
  14. USAGE_FRONT_BUFFER(4294967296):
    • 表示缓冲区将用于前缓冲区渲染,用于指定前缓冲区渲染时的行为。
    @Retention(RetentionPolicy.SOURCE)
    @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
            USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
            USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT,
            USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA,
            USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER})
    public @interface Usage {};

    @Usage
    /** Usage: The buffer will sometimes be read by the CPU */
    public static final long USAGE_CPU_READ_RARELY       = 2;
    /** Usage: The buffer will often be read by the CPU */
    public static final long USAGE_CPU_READ_OFTEN        = 3;

    /** Usage: The buffer will sometimes be written to by the CPU */
    public static final long USAGE_CPU_WRITE_RARELY      = 2 << 4;
    /** Usage: The buffer will often be written to by the CPU */
    public static final long USAGE_CPU_WRITE_OFTEN       = 3 << 4;

    /** Usage: The buffer will be read from by the GPU */
    public static final long USAGE_GPU_SAMPLED_IMAGE      = 1 << 8;
    /** Usage: The buffer will be written to by the GPU */
    public static final long USAGE_GPU_COLOR_OUTPUT       = 1 << 9;
    /**
     * The buffer will be used as a hardware composer overlay layer. That is, it will be displayed
     * using the system compositor via {@link SurfaceControl}
     *
     * This flag is currently only needed when using
     * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
     * to set a buffer. In all other cases, the framework adds this flag
     * internally to buffers that could be presented in a composer overlay.
     */
    public static final long USAGE_COMPOSER_OVERLAY = 1 << 11;
    /** Usage: The buffer must not be used outside of a protected hardware path */
    public static final long USAGE_PROTECTED_CONTENT      = 1 << 14;
    /** Usage: The buffer will be read by a hardware video encoder */
    public static final long USAGE_VIDEO_ENCODE           = 1 << 16;
    /** Usage: The buffer will be used for sensor direct data */
    public static final long USAGE_SENSOR_DIRECT_DATA     = 1 << 23;
    /** Usage: The buffer will be used as a shader storage or uniform buffer object */
    public static final long USAGE_GPU_DATA_BUFFER        = 1 << 24;
    /** Usage: The buffer will be used as a cube map texture */
    public static final long USAGE_GPU_CUBE_MAP           = 1 << 25;
    /** Usage: The buffer contains a complete mipmap hierarchy */
    public static final long USAGE_GPU_MIPMAP_COMPLETE    = 1 << 26;
    /** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is
     * specified, different usages may adjust their behavior as a result. For example, when
     * used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window.
     * When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer
     * receiving an overlay plane & avoid caching it in intermediate composition buffers. */
    public static final long USAGE_FRONT_BUFFER           = 1L << 32;

注意事项

  • 这些常量用于描述图像缓冲区的使用方式,包括 CPU 和 GPU 的读写操作,以及特定用途(如硬件视频编码、传感器数据等)。
  • 使用 @Usage 注解标记这些常量,便于在代码中理解和指定图像缓冲区的预期使用方式。
  • 不同的使用标志组合对应不同的图像缓冲区行为和性能特征。
  • 在使用这些常量时,需要根据实际需求和设备支持情况选择合适的使用标志组合,以获得最佳的图像处理效果和性能表现。
getWidth()
/**
 * The default width of {@link Image Images}, in pixels.
 *
 * <p>The width may be overridden by the producer sending buffers to this
 * ImageReader's Surface. If so, the actual width of the images can be
 * found using {@link Image#getWidth}.</p>
 *
 * @return the expected width of an Image
 */
public int getWidth() {
    return mWidth;
}
  • 默认宽度
    • 这个方法返回的宽度是 ImageReader 对象预期接收的图像宽度。
    • 如果图像生成者向 ImageReaderSurface 发送的缓冲区覆盖了宽度,实际图像的宽度可以通过 Image 对象的 getWidth() 方法获得。
  • 注意事项
    • 方法返回的宽度值是预期值,实际图像的宽度可能会因为生产者发送的图像缓冲区而有所不同。
    • 对于实际获取的图像,应通过 Image 对象的 getWidth() 方法获取准确的宽度值。
getHeight()
    /**
     * The default height of {@link Image Images}, in pixels.
     *
     * <p>The height may be overridden by the producer sending buffers to this
     * ImageReader's Surface. If so, the actual height of the images can be
     * found using {@link Image#getHeight}.</p>
     *
     * @return the expected height of an Image
     */
    public int getHeight() {
        return mHeight;
    }
  • 作用
    • getHeight() 方法用于获取图像 (Image) 的预期高度,即 ImageReader 期望接收的图像高度。
  • 返回值
    • 返回 int 类型的预期图像高度值。
getImageFormat()
/**
 * The default {@link ImageFormat image format} of {@link Image Images}.
 *
 * <p>Some color formats may be overridden by the producer sending buffers to
 * this ImageReader's Surface if the default color format allows. ImageReader
 * guarantees that all {@link Image Images} acquired from ImageReader
 * (for example, with {@link #acquireNextImage}) will have a "compatible"
 * format to what was specified in {@link #newInstance}.
 * As of now, each format is only compatible to itself.
 * The actual format of the images can be found using {@link Image#getFormat}.</p>
 *
 * <p>Use this function if the ImageReader instance is created by factory method
 * {@code newInstance} function or by builder pattern {@code ImageReader.Builder} and using
 * {@link Builder#setImageFormat}.</p>
 *
 * @return the expected format of an Image
 *
 * @see ImageFormat
 */
public int getImageFormat() {
    return mFormat;
}

方法说明:

  • 作用
    • getImageFormat() 方法用于获取图像 (Image) 的预期格式,即 ImageReader 期望接收的图像格式。
  • 返回值
    • 返回 int 类型的预期图像格式,该格式是通过 ImageFormat 类中定义的常量来表示的。

注释解释:

  • 默认图像格式
    • 这个方法返回的格式是 ImageReader 对象预期接收的图像格式。
    • 图像生成者向 ImageReaderSurface 发送的缓冲区可能会覆盖默认的颜色格式,如果默认颜色格式允许的话。
    • ImageReader 保证从其获取的所有图像 (Image)(例如使用 acquireNextImage 方法获取的图像)将具有与在 newInstance 方法或 ImageReader.BuildersetImageFormat 方法中指定的“兼容”格式相同。
    • 实际图像的格式可以通过 Image 对象的 getFormat() 方法获得。
getHardwareBufferFormat()

方法说明:

  • 作用
    • getHardwareBufferFormat() 方法用于获取图像 (Image) 的预期硬件缓冲区 (HardwareBuffer) 格式,即 ImageReader 期望接收的图像硬件缓冲区格式。
  • 返回值
    • 返回 int 类型的预期硬件缓冲区格式,该格式是通过 HardwareBuffer.Format 注解中定义的常量来表示的。

注释解释:

  • 默认硬件缓冲区格式
    • 这个方法返回的格式是 ImageReader 对象预期接收的图像硬件缓冲区格式。
    • 如果使用 ImageReader.BuildersetDefaultHardwareBufferFormatsetDefaultDataSpace 方法创建 ImageReader 实例,则可以使用此方法来获取预期的硬件缓冲区格式。
/**
 * The default {@link HardwareBuffer} format of {@link Image Images}.
 *
 * <p>Use this function if the ImageReader instance is created by builder pattern
 * {@code ImageReader.Builder} and using {@link Builder#setDefaultHardwareBufferFormat} and
 * {@link Builder#setDefaultDataSpace}.</p>
 *
 * @return the expected {@link HardwareBuffer} format of an Image.
 */
public @HardwareBuffer.Format int getHardwareBufferFormat() {
    return mHardwareBufferFormat;
}


ImageReader imageReader = ...; // 创建或获取 ImageReader 对象

// 获取预期的硬件缓冲区格式
int expectedFormat = imageReader.getHardwareBufferFormat();

// 处理从 ImageReader 获取的图像
Image image = imageReader.acquireNextImage();
if (image != null) {
    // 获取实际图像的硬件缓冲区格式
    HardwareBuffer hardwareBuffer = image.getHardwareBuffer();
    int actualFormat = hardwareBuffer.getFormat();
    // 使用图像数据进行处理
    // ...
    // 释放图像资源
    image.close();
}
getDataSpace()

方法说明:

  • 作用
    • getDataSpace() 方法用于获取图像 (Image) 的预期数据空间 (dataspace),即 ImageReader 期望接收的图像数据空间。
  • 返回值
    • 返回 int 类型的预期数据空间值,该值通过 NamedDataSpace 注解中定义的常量来表示。

注释解释:

  • 默认数据空间
    • 这个方法返回的数据空间是 ImageReader 对象预期接收的图像数据空间。
    • 如果使用 ImageReader.BuildersetDefaultDataSpace 方法创建 ImageReader 实例,则可以使用此方法来获取预期的数据空间。
/**
 * The default dataspace of {@link Image Images}.
 *
 * <p>Use this function if the ImageReader instance is created by builder pattern
 * {@code ImageReader.Builder} and {@link Builder#setDefaultDataSpace}.</p>
 *
 * @return the expected dataspace of an Image.
 */
@SuppressLint("MethodNameUnits")
public @NamedDataSpace int getDataSpace() {
    return mDataSpace;
}


ImageReader imageReader = ...; // 创建或获取 ImageReader 对象

// 获取预期的数据空间
int expectedDataSpace = imageReader.getDataSpace();

// 处理从 ImageReader 获取的图像
Image image = imageReader.acquireNextImage();
if (image != null) {
    // 获取实际图像的数据空间
    int actualDataSpace = image.getDataSpace();
    // 使用图像数据进行处理
    // ...
    // 释放图像资源
    image.close();
}
getMaxImages()

方法说明:

  • 作用
    • getMaxImages() 方法用于获取 ImageReader 可以同时获取的最大图像数量。这个数量由 ImageReader 在实例化时指定的 maxImages 参数确定。
  • 返回值
    • 返回一个 int 值,表示 ImageReader 可以同时获取的最大图像数量。

注释解释:

  • 最大图像数量:
    • 这个方法返回的值代表了 ImageReader 可以同时获取的最大图像数量。一旦获取的图像数量达到这个最大值,后续尝试获取图像会导致 acquire 方法抛出 IllegalStateException 异常。
  • 图像获取和释放:
    • 图像在被 ImageReader 获取后,直到调用 Image#close() 方法将图像释放回 ImageReader,期间被视为已获取状态。
  • 并发限制:
    • 如果尝试同时获取超过 maxImages 个图像,ImageReader 的获取方法会抛出 IllegalStateException 异常。
    • ImageReader 用户已获取的图像达到最大数量时,生产者可能会因为无法再将图像入队而处于阻塞状态,直到至少释放了一个图像。
/**
 * Maximum number of images that can be acquired from the ImageReader by any time (for example,
 * with {@link #acquireNextImage}).
 *
 * <p>An image is considered acquired after it's returned by a function from ImageReader, and
 * until the Image is {@link Image#close closed} to release the image back to the ImageReader.
 * </p>
 *
 * <p>Attempting to acquire more than {@code maxImages} concurrently will result in the
 * acquire function throwing a {@link IllegalStateException}. Furthermore,
 * while the max number of images have been acquired by the ImageReader user, the producer
 * enqueueing additional images may stall until at least one image has been released. </p>
 *
 * @return Maximum number of images for this ImageReader.
 *
 * @see Image#close
 */
public int getMaxImages() {
    return mMaxImages;
}


ImageReader imageReader = ...; // 创建或获取 ImageReader 对象

// 获取 ImageReader 可以同时获取的最大图像数量
int maxImages = imageReader.getMaxImages();

// 尝试获取图像
try {
    Image image = imageReader.acquireNextImage();
    // 处理获取的图像
    // ...
    // 释放图像资源
    image.close();
} catch (IllegalStateException e) {
    // 处理无法获取图像的异常情况
    e.printStackTrace();
}
getUsage()

方法说明:

  • 作用
    • getUsage() 方法用于获取 ImageReader 可以生成的图像的使用标志 (usage flag)。这个标志由 ImageReader 在实例化时指定的 usage 参数确定。
  • 返回值
    • 返回一个 long 值,表示 ImageReader 可以生成的图像的使用标志。

注释解释:

  • 图像使用标志:
    • 这个方法返回的值代表了 ImageReader 可以生成的图像的使用标志,指示图像可以用于何种用途。
/**
 * The usage flag of images that can be produced by the ImageReader.
 *
 * @return The usage flag of the images for this ImageReader.
 */
public @Usage long getUsage() {
    return mUsage;
}
getSurface()

方法说明:

  • 作用
    • getSurface() 方法用于获取一个 Surface 对象,该对象可用于生成图像供 ImageReader 使用。
  • 返回值
    • 返回一个 Surface 对象,用作生成图像的绘制目标。

注释解释:

  • 图像生成的 Surface
    • 这个方法返回的 Surface 对象是用于生成图像的绘制目标。在向这个 Surface 渲染有效的图像数据之前,调用 acquireNextImage 方法将返回 null
  • 单一数据源
    • 同一时间只能有一个数据源向这个 Surface 中产生数据。但是,一旦第一个数据源与 Surface 断开连接,同一个 Surface 可以重新用于不同的 API。
  • Surface 的持有
    • 需要注意的是,仅持有由此方法返回的 Surface 对象并不能阻止其父 ImageReader 被回收。从这个意义上说,Surface 的行为类似于对提供它的 ImageReader 的弱引用 (WeakReference)。
/**
 * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this
 * {@code ImageReader}.</p>
 *
 * <p>Until valid image data is rendered into this {@link Surface}, the
 * {@link #acquireNextImage} method will return {@code null}. Only one source
 * can be producing data into this Surface at the same time, although the
 * same {@link Surface} can be reused with a different API once the first source is
 * disconnected from the {@link Surface}.</p>
 *
 * <p>Please note that holding on to the Surface object returned by this method is not enough
 * to keep its parent ImageReader from being reclaimed. In that sense, a Surface acts like a
 * {@link java.lang.ref.WeakReference weak reference} to the ImageReader that provides it.</p>
 *
 * @return A {@link Surface} to use for a drawing target for various APIs.
 */
public Surface getSurface() {
    return mSurface;
}


ImageReader imageReader = ...; // 创建或获取 ImageReader 对象

// 获取用于生成图像的 Surface
Surface surface = imageReader.getSurface();

// 使用 Surface 进行图像渲染等操作(需要其他API来渲染有效图像数据)

acquireLatestImage()
  • 获取最新图像
    • acquireLatestImage() 方法会获取 ImageReader 队列中的最新图像,丢弃队列中较旧的图像。如果没有新的图像可用,则返回 null
  • 适用性
    • 对于大多数实时处理的用例,推荐使用 acquireLatestImage() 方法,因为它更适合获取最新的图像数据。
  • 丢弃旧图像
    • 要使用 acquireLatestImage() 方法,maxImages 应至少为 2,以便丢弃除最新图像之外的所有图像。这是因为临时同时获取两个图像来实现丢弃旧图像的操作。
  • 异常情况
    • 如果已经通过 acquireLatestImage()acquireNextImage() 获取了 maxImages 个图像,并且没有及时释放图像资源 (Image#close),则会抛出 IllegalStateException 异常。
/**
 * <p>
 * Acquire the latest {@link Image} from the ImageReader's queue, dropping older
 * {@link Image images}. Returns {@code null} if no new image is available.
 * </p>
 * <p>
 * This operation will acquire all the images possible from the ImageReader,
 * but {@link #close} all images that aren't the latest. This function is
 * recommended to use over {@link #acquireNextImage} for most use-cases, as it's
 * more suited for real-time processing.
 * </p>
 * <p>
 * Note that {@link #getMaxImages maxImages} should be at least 2 for
 * {@link #acquireLatestImage} to be any different than {@link #acquireNextImage} -
 * discarding all-but-the-newest {@link Image} requires temporarily acquiring two
 * {@link Image Images} at once. Or more generally, calling {@link #acquireLatestImage}
 * with less than two images of margin, that is
 * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected.
 * </p>
 * <p>
 * This operation will fail by throwing an {@link IllegalStateException} if
 * {@code maxImages} have been acquired with {@link #acquireLatestImage} or
 * {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage}
 * calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between
 * will exhaust the underlying queue. At such a time, {@link IllegalStateException}
 * will be thrown until more images are
 * released with {@link Image#close}.
 * </p>
 *
 * @return latest frame of image data, or {@code null} if no image data is available.
 * @throws IllegalStateException if too many images are currently acquired
 */
public Image acquireLatestImage() {
    Image image = acquireNextImage();
    if (image == null) {
        return null;
    }
    try {
        for (;;) {
            Image next = acquireNextImageNoThrowISE();
            if (next == null) {
                Image result = image;
                image = null;
                return result;
            }
            image.close();
            image = next;
        }
    } finally {
        if (image != null) {
            image.close();
        }
        if (mParent != null) {
            mParent.flushOther(this);
        }
    }
}



ImageReader imageReader = ...; // 创建或获取 ImageReader 对象

try {
    Image latestImage = imageReader.acquireLatestImage();
    if (latestImage != null) {
        // 处理最新的图像数据
        // ...
        // 使用完毕后记得释放图像资源
        latestImage.close();
    } else {
        // 没有可用的最新图像数据
    }
} catch (IllegalStateException e) {
    // 处理图像获取过多的异常情况
    e.printStackTrace();
}

方法说明:

  • 作用
    • acquireNextImage() 方法用于从 ImageReader 的队列中获取下一个图像,如果成功获取到图像则返回相应的 Image 对象,否则返回 null
  • 返回值
    • 如果成功获取到图像,则返回对应的 Image 对象;如果没有可用的图像数据,则返回 null

注释解释:

  • 推荐用法
    • 在注释中提到,建议使用 acquireLatestImage() 方法而不是直接使用 acquireNextImage()acquireLatestImage() 方法会自动释放旧图像,允许处理速度较慢的处理程序跟上最新的帧。直接使用 acquireNextImage() 可能会导致图像显示出现越来越大的延迟,最终完全停滞,看不到新的图像。
  • 异常处理
    • 如果已经获取了与 maxImages 相同数量的图像,调用 acquireNextImage() 将抛出 IllegalStateException 异常。这是为了防止超出可同时获取图像的最大限制。
acquireNextImage()
  • 功能
    • acquireNextImage() 用于从 ImageReader 的图像队列中获取下一个可用的图像。
  • 返回值
    • 如果成功获取到图像,则返回对应的 Image 对象;如果没有可用的图像数据,则返回 null
  • 使用场景
    • 适合需要按顺序处理图像的情况,每次调用都会返回图像队列中最旧的未处理图像。
  • 注意事项
    • 如果图像队列已满(即达到了 maxImages 的限制),调用此方法会抛出 IllegalStateException 异常。
/**
 * <p>
 * Acquire the next Image from the ImageReader's queue. Returns {@code null} if
 * no new image is available.
 * </p>
 *
 * <p><i>Warning:</i> Consider using {@link #acquireLatestImage()} instead, as it will
 * automatically release older images, and allow slower-running processing routines to catch
 * up to the newest frame. Usage of {@link #acquireNextImage} is recommended for
 * batch/background processing. Incorrectly using this function can cause images to appear
 * with an ever-increasing delay, followed by a complete stall where no new images seem to
 * appear.
 * </p>
 *
 * <p>
 * This operation will fail by throwing an {@link IllegalStateException} if
 * {@code maxImages} have been acquired with {@link #acquireNextImage} or
 * {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or
 * {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without
 * calling {@link Image#close} in-between will exhaust the underlying queue. At such a time,
 * {@link IllegalStateException} will be thrown until more images are released with
 * {@link Image#close}.
 * </p>
 *
 * @return a new frame of image data, or {@code null} if no image data is available.
 * @throws IllegalStateException if {@code maxImages} images are currently acquired
 * @see #acquireLatestImage
 */
public Image acquireNextImage() {
    // Initialize with reader format, but can be overwritten by native if the image
    // format is different from the reader format.
    SurfaceImage si;
    si = new SurfaceImage(mFormat);
    int status = acquireNextSurfaceImage(si);

    switch (status) {
        case ACQUIRE_SUCCESS:
            return si;
        case ACQUIRE_NO_BUFS:
            return null;
        case ACQUIRE_MAX_IMAGES:
            throw new IllegalStateException(
                    String.format(
                            "maxImages (%d) has already been acquired, " +
                            "call #close before acquiring more.", mMaxImages));
        default:
            throw new AssertionError("Unknown nativeImageSetup return code " + status);
    }
}
区别
  • 使用 acquireNextImage() 适合需要顺序处理图像数据的情况,对于每次调用,会返回图像队列中最旧的图像。
  • 使用 acquireLatestImage() 适合需要实时处理最新图像数据的情况,它会自动丢弃旧图像,始终返回最新的图像。但要注意合理设置 maxImages,以免导致图像队列耗尽或性能问题。
setOnImageAvailableListener(OnImageAvailableListener, Handler)
  • 功能
    • setOnImageAvailableListener 用于注册一个监听器,在 ImageReader 接收到新图像时触发回调。
  • 参数
    • listener:要注册的 OnImageAvailableListener 监听器对象。
    • handler:指定监听器回调所在的 Handler,如果为 null,则回调将在调用线程的 looper 上执行。
  • 异常
    • 如果未指定 handler 且调用线程没有 looper,则抛出 IllegalArgumentException 异常。
  • 实现步骤
    1. 在同步块中对监听器进行处理,确保线程安全。
    2. 检查传入的 listener 对象是否为 null,如果不为 null,则继续注册监听器。
    3. 根据传入的 handler 获取对应的 looper 对象。
    4. 如果 handler 不为 null,则使用其所属的 looper;如果为 null,则使用当前线程的 looper。
    5. 检查当前线程是否有 looper,如果没有且 handlernull,则抛出异常。
    6. 如果当前监听器处理的 Handlernull 或者其 looper 与新的 looper 不同,重新创建 ListenerHandler 对象和 HandlerExecutor 对象。
    7. 否则,如果传入的 listenernull,则清空 mListenerHandlermListenerExecutor
    8. 最后将传入的 listener 对象赋值给 mListener
/**
 * Register a listener to be invoked when a new image becomes available
 * from the ImageReader.
 *
 * @param listener
 *            The listener that will be run.
 * @param handler
 *            The handler on which the listener should be invoked, or null
 *            if the listener should be invoked on the calling thread's looper.
 * @throws IllegalArgumentException
 *            If no handler specified and the calling thread has no looper.
 */
public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
    synchronized (mListenerLock) {
        if (listener != null) {
            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
            if (looper == null) {
                throw new IllegalArgumentException(
                        "handler is null but the current thread is not a looper");
            }
            if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
                mListenerHandler = new ListenerHandler(looper);
                mListenerExecutor = new HandlerExecutor(mListenerHandler);
            }
        } else {
            mListenerHandler = null;
            mListenerExecutor = null;
        }
        mListener = listener;
    }
}
OnImageAvailableListener
  • 接口功能

    • OnImageAvailableListener 是一个回调接口,用于通知应用程序当 ImageReader 接收到新图像时。
  • 方法

    • onImageAvailable(ImageReader reader)
      

      :当

      ImageReader
      

      接收到新图像时,将调用此方法。

      • reader 参数表示与回调相关联的 ImageReader 对象,即接收到新图像的 ImageReader 实例。
  • 回调频率

    • onImageAvailable 方法是基于每个图像的基础调用的,也就是说,每当 ImageReader 接收到新帧时都会触发此回调
  • 使用方法

    • 开发者需要实现 OnImageAvailableListener 接口,并将其传递给 ImageReader 对象的 setOnImageAvailableListener 方法,以便在新图像可用时接收通知。
    • ImageReader 接收到新图像时,将调用开发者实现的 onImageAvailable 方法,并将 ImageReader 实例作为参数传递给此方法,以便开发者可以处理接收到的新图像。
    public interface OnImageAvailableListener {
        /**
         * Callback that is called when a new image is available from ImageReader.
         *
         * @param reader the ImageReader the callback is associated with.
         * @see ImageReader
         * @see Image
         */
        void onImageAvailable(ImageReader reader);
    }
    
close()
  • 方法功能
    • close() 方法用于释放 ImageReader 实例占用的所有资源,包括释放图像流(Image 对象)、释放关联的 Surface 等。
  • 操作效果
    • 调用 close() 方法后,将不再能够使用该 ImageReader 实例。对已释放的 ImageReader 调用任何方法,包括 acquireNextImageacquireLatestImage,都将导致抛出 IllegalStateException 异常。
    • 对之前通过 acquireNextImageacquireLatestImage 获取的 Image 对象调用方法,例如从 Image.Plane#getBuffer 获取的 ByteBuffer,将会导致行为未定义。
  • 具体操作
    • 首先,通过 setOnImageAvailableListener(null, null) 方法移除任何注册的图像可用监听器,确保不再接收图像可用的通知。
    • 然后,释放关联的 Surface 对象,调用 mSurface.release() 方法释放 Surface 资源。
    • 接下来,遍历并关闭所有已获取但尚未释放的 Image 对象。这是通过迭代 mAcquiredImages 列表中的每个 Image 对象,并调用其 close() 方法来实现的。
    • 最后,调用 nativeClose() 方法关闭与 ImageReader 相关的本地资源。
  • 同步操作
    • 在操作过程中使用了同步块 synchronized (mCloseLock),确保关闭操作的线程安全性。在同步块中,会将 mIsReaderValid 标志设置为 false,表示 ImageReader 已无效。
    • 对于已获取但尚未释放的 Image 对象,使用了同步块来确保在关闭 ImageReader 时可以安全地关闭这些图像,避免并发访问问题。
/**
 * Free up all the resources associated with this ImageReader.
 *
 * <p>
 * After calling this method, this ImageReader can not be used. Calling
 * any methods on this ImageReader and Images previously provided by
 * {@link #acquireNextImage} or {@link #acquireLatestImage}
 * will result in an {@link IllegalStateException}, and attempting to read from
 * {@link ByteBuffer ByteBuffers} returned by an earlier
 * {@link Image.Plane#getBuffer Plane#getBuffer} call will
 * have undefined behavior.
 * </p>
 */
@Override
public void close() {
    setOnImageAvailableListener(null, null);
    if (mSurface != null) mSurface.release();

    /**
     * Close all outstanding acquired images before closing the ImageReader. It is a good
     * practice to close all the images as soon as it is not used to reduce system instantaneous
     * memory pressure. CopyOnWrite list will use a copy of current list content. For the images
     * being closed by other thread (e.g., GC thread), doubling the close call is harmless. For
     * the image being acquired by other threads, mCloseLock is used to synchronize close and
     * acquire operations.
     */
    synchronized (mCloseLock) {
        mIsReaderValid = false;
        for (Image image : mAcquiredImages) {
            image.close();
        }
        mAcquiredImages.clear();

        nativeClose();

        if (mEstimatedNativeAllocBytes > 0) {
            VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
            mEstimatedNativeAllocBytes = 0;
        }
    }
}
discardFreeBuffers()
  • 方法功能
    • discardFreeBuffers() 方法用于丢弃 ImageReader 持有的所有空闲缓冲区。
    • 通常情况下,ImageReader 会缓存已分配的缓冲区,以便在需要时重用,以实现最佳性能。但有时可能需要释放所有缓存的未使用缓冲区,以节省内存。
  • 作用范围
    • 调用该方法将丢弃所有空闲的缓冲区,但不包括以下内容:
      1. 已从 ImageReader 获取的图像关联的任何缓冲区。
      2. 等待获取的任何已填充缓冲区。
      3. 当前由源渲染到 ImageReaderSurface 中的缓冲区。
  • 方法影响
    • 调用此方法后,ImageReader 仍然可用,但可能需要在需要更多缓冲区进行渲染时重新分配缓冲区。
    • 丢弃空闲缓冲区可以在某些情况下释放内存,但需要注意,这可能会导致在后续使用中重新分配缓冲区,可能会影响性能。
/**
 * Discard any free buffers owned by this ImageReader.
 *
 * <p>
 * Generally, the ImageReader caches buffers for reuse once they have been
 * allocated, for best performance. However, sometimes it may be important to
 * release all the cached, unused buffers to save on memory.
 * </p>
 * <p>
 * Calling this method will discard all free cached buffers. This does not include any buffers
 * associated with Images acquired from the ImageReader, any filled buffers waiting to be
 * acquired, and any buffers currently in use by the source rendering buffers into the
 * ImageReader's Surface.
 * <p>
 * The ImageReader continues to be usable after this call, but may need to reallocate buffers
 * when more buffers are needed for rendering.
 * </p>
 */
public void discardFreeBuffers() {
    synchronized (mCloseLock) {
        nativeDiscardFreeBuffers();
    }
}
new ImageReader.Builder(width, height)
  • 构造方法
    • Builder(int width, int height): 构造一个新的 Builder 对象,设置默认的图像宽度和高度。
  • 配置方法
    • setMaxImages(int maxImages): 设置用户希望同时访问的最大图像数量。默认值为 1。
    • setUsage(long usage): 设置图像使用标志,指定被 ImageReader 使用的图像的预期使用方式。
    • setImageFormat(int imageFormat): 设置图像的默认格式,可能会被生产者覆盖。使用此方法会将 setDefaultHardwareBufferFormatsetDefaultDataSpace 的组合替换为新的方式。
    • setDefaultHardwareBufferFormat(int hardwareBufferFormat): 设置默认的 HardwareBuffer 格式,可能会被生产者覆盖。与 setDefaultDataSpace 配合使用。
    • setDefaultDataSpace(int dataSpace): 设置默认的数据空间,可能会被生产者覆盖。与 setDefaultHardwareBufferFormat 配合使用。
  • 构建方法
    • build(): 根据 Builder 对象的配置,创建一个新的 ImageReader 对象。

在使用 Builder 类时,可以根据需求调用上述方法来配置 ImageReader 对象的属性,最后调用 build() 方法创建 ImageReader 对象。这种模式可以使得 ImageReader 对象的创建过程更加灵活和可配置化。

/**
 * Builder class for {@link ImageReader} objects.
 */
public static final class Builder {
    private int mWidth;
    private int mHeight;
    private int mMaxImages = 1;
    private int mImageFormat = ImageFormat.UNKNOWN;
    private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
    private int mDataSpace = DataSpace.DATASPACE_UNKNOWN;
    private long mUsage = HardwareBuffer.USAGE_CPU_READ_OFTEN;
    private boolean mUseLegacyImageFormat = false;

    /**
     * Constructs a new builder for {@link ImageReader}.
     *
     * @param width The default width in pixels that will be passed to the producer.
     *              May be overridden by the producer.
     * @param height The default height in pixels that will be passed to the producer.
     *              May be overridden by the producer.
     * @see Image
     */
    public Builder(@IntRange(from = 1) int width, @IntRange(from = 1) int height) {
        mWidth = width;
        mHeight = height;
    }

    /**
     * Set the maximal number of images.
     *
     * @param maxImages The maximum number of images the user will want to
     *            access simultaneously. This should be as small as possible to
     *            limit memory use. Default value is 1.
     * @return the Builder instance with customized usage value.
     */
    public @NonNull Builder setMaxImages(int maxImages) {
        mMaxImages = maxImages;
        return this;
    }

    /**
     * Set the consumer usage flag.
     *
     * @param usage The intended usage of the images consumed by this ImageReader.
     *              See the usages on {@link HardwareBuffer} for a list of valid usage bits.
     *              Default value is {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}.
     * @return the Builder instance with customized usage value.
     *
     * @see HardwareBuffer
     */
    public @NonNull Builder setUsage(@Usage long usage) {
        mUsage = usage;
        return this;
    }

    /**
     * Set the default image format passed by the producer. May be overridden by the producer.
     *
     * <p>{@link #setImageFormat} function replaces the combination of
     * {@link #setDefaultHardwareBufferFormat} and {@link #setDefaultDataSpace} functions.
     * Either this or these two functions must be called to initialize an {@code ImageReader}
     * instance.</p>
     *
     * @param imageFormat The format of the image that this reader will produce. This
     *                    must be one of the {@link android.graphics.ImageFormat} or
     *                   {@link android.graphics.PixelFormat} constants. Note that not
     *                   all formats are supported, like ImageFormat.NV21. The default value is
     *                   {@link ImageFormat#UNKNOWN}.
     * @return the builder instance with customized image format value.
     *
     * @see #setDefaultHardwareBufferFormat
     * @see #setDefaultDataSpace
     */
    public @NonNull Builder setImageFormat(@Format int imageFormat) {
        mImageFormat = imageFormat;
        mUseLegacyImageFormat = true;
        mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
        mDataSpace = DataSpace.DATASPACE_UNKNOWN;
        return this;
    }

    /**
     * Set the default hardwareBuffer format passed by the producer.
     * May be overridden by the producer.
     *
     * <p>This function works together with {@link #setDefaultDataSpace} for an
     * {@link ImageReader} instance. Setting at least one of these two replaces
     * {@link #setImageFormat} function.</p>
     *
     * <p>The format of the Image can be overridden after {@link #setImageFormat} by calling
     * this function and then {@link #setDefaultDataSpace} functions.
     * <i>Warning:</i> Missing one of callings for initializing or overriding the format may
     * involve undefined behaviors.</p>
     *
     * @param hardwareBufferFormat The HardwareBuffer format of the image that this reader
     *                             will produce. The default value is
     *                             {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
     * @return the builder instance with customized hardwareBuffer value.
     *
     * @see #setDefaultDataSpace
     * @see #setImageFormat
     */
    @SuppressLint("MissingGetterMatchingBuilder")
    public @NonNull Builder setDefaultHardwareBufferFormat(
            @HardwareBuffer.Format int hardwareBufferFormat) {
        mHardwareBufferFormat = hardwareBufferFormat;
        mUseLegacyImageFormat = false;
        mImageFormat = ImageFormat.UNKNOWN;
        return this;
    }

    /**
     * Set the default dataspace passed by the producer.
     * May be overridden by the producer.
     *
     * <p>This function works together with {@link #setDefaultHardwareBufferFormat} for an
     * {@link ImageReader} instance. Setting at least one of these two replaces
     * {@link #setImageFormat} function.</p>
     *
     * @param dataSpace The dataspace of the image that this reader will produce.
     *                  The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
     * @return the builder instance with customized dataspace value.
     *
     * @see #setDefaultHardwareBufferFormat
     */
    @SuppressLint("MissingGetterMatchingBuilder")
    public @NonNull Builder setDefaultDataSpace(@NamedDataSpace int dataSpace) {
        mDataSpace = dataSpace;
        mUseLegacyImageFormat = false;
        mImageFormat = ImageFormat.UNKNOWN;
        return this;
    }

    /**
     * Builds a new ImageReader object.
     *
     * @return The new ImageReader object.
     */
    public @NonNull ImageReader build() {
        if (mUseLegacyImageFormat) {
            return new ImageReader(mWidth, mHeight, mImageFormat, mMaxImages, mUsage, null);
        } else {
            return new ImageReader(mWidth, mHeight, mMaxImages, mUsage, null,
                mHardwareBufferFormat, mDataSpace);
        }
    }
}


import android.media.ImageFormat;
import android.media.ImageReader;

public class ImageReaderExample {

    public static void main(String[] args) {
        // 设置图像的默认宽度和高度
        int width = 640;
        int height = 480;

        // 设置最大图像数量
        int maxImages = 2;

        // 设置图像使用标志
        long usage = HardwareBuffer.USAGE_GPU_COLOR_OUTPUT;

        // 使用ImageReader.Builder构建ImageReader对象
        ImageReader imageReader = new ImageReader.Builder(width, height)
                .setMaxImages(maxImages)
                .setUsage(usage)
                .setImageFormat(ImageFormat.YUV_420_888) // 设置图像格式为YUV_420_888
                .build();

        // 现在可以使用imageReader对象进行图像捕获和处理等操作
        // ...
    }
}
ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer)

ImagePlane 类是一个继承自 android.media.Image.Plane 抽象类的具体实现。它表示与 Image 对象关联的图像数据的单个平面。在使用 YUV_420_888 等格式时,每个图像可能包含多个平面。

类的主要内容:

  • 构造方法 ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer) 初始化了 ImagePlane 对象,设置了行跨距、像素跨距和缓冲区。
    • rowStride:相邻两行像素数据之间的字节距离。
    • pixelStride:相同颜色分量的两个相邻像素之间的字节距离。
    • buffer:包含该平面像素数据的 ByteBuffer
  • getBuffer()getPixelStride()getRowStride() 方法重写了 android.media.Image.Plane 中的抽象方法。
    • getBuffer():返回包含该平面像素数据的 ByteBuffer
    • getPixelStride():返回该平面的像素跨距。
    • getRowStride():返回该平面的行跨距。

该类用于表示图像数据的特定平面(如颜色通道),并提供了访问其属性和像素数据的方法。通常与 Image 对象一起使用,以提取和处理图像数据的各个平面,特别是在涉及 YUV 或类似格式的场景中,图像数据被分割到多个平面中。

public static class ImagePlane extends android.media.Image.Plane {
    private ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
        mRowStride = rowStride;
        mPixelStride = pixelStride;
        mBuffer = buffer;
        /**
         * Set the byteBuffer order according to host endianness (native
         * order), otherwise, the byteBuffer order defaults to
         * ByteOrder.BIG_ENDIAN.
         */
        mBuffer.order(ByteOrder.nativeOrder());
    }

    @Override
    public ByteBuffer getBuffer() {
        return mBuffer;
    }

    @Override
    public int getPixelStride() {
        return mPixelStride;
    }

    @Override
    public int getRowStride() {
        return mRowStride;
    }

    final private int mPixelStride;
    final private int mRowStride;

    private ByteBuffer mBuffer;
}
小结:
  1. ImageReader 类和相关方法
    • ImageReader 是 Android 中用于从某些生产者(如摄像头)接收图像数据的类。
    • acquireNextImage() 方法用于从 ImageReader 的队列中获取下一个可用的图像。
    • acquireLatestImage() 方法用于获取最新可用的图像,丢弃旧图像,适合实时处理。
    • discardFreeBuffers() 方法用于丢弃 ImageReader 持有的空闲缓冲区,节省内存。
    • setOnImageAvailableListener() 方法用于注册当 ImageReader 有新图像可用时触发的监听器。
  2. Image 类和相关方法
    • Image 类代表从 ImageReader 中获取的图像对象。
    • getWidth()getHeight() 方法用于获取图像的宽度和高度。
    • getFormat() 方法用于获取图像的格式。
    • close() 方法用于释放图像对象,必须在使用完图像后调用。
  3. Builder 模式和图像处理
    • Builder 类是用于构建 ImageReader 对象的构建器模式。
    • 通过 Builder 类可以设置 ImageReader 的属性,如宽度、高度、图像格式、最大图像数量等。
    • ImageReader.Builder 提供了 build() 方法来构建最终的 ImageReader 对象。
  • 36
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常抱歉,我之前给出的示例代码中的`pylon.ImageEventType.ImageReady`在Pylon的Python绑定中是无效的。对于Basler相机的图像采集回调,可以使用`GrabResult`对象的`GrabSucceeded`属性来判断图像是否准备好。以下是修改后的示例代码: ```python from pypylon import pylon # 定义回调函数 def image_callback(camera, grab_result): if grab_result.GrabSucceeded(): # 处理图像数据 image = grab_result.Array # 在这里添加你的图像处理代码 # ... # 显示图像 cv2.imshow('Image', image) cv2.waitKey(1) # 等待1毫秒,以便图像显示更新 # 创建Pylon相机对象 camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice()) # 打开相机 camera.Open() # 注册回调函数 camera.RegisterImageEventHandler(image_callback) # 开始采集图像 camera.StartGrabbing() # 持续采集图像,直到按下键盘中断 while camera.IsGrabbing(): camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException) # 停止采集图像 camera.StopGrabbing() # 关闭相机 camera.Close() # 关闭OpenCV窗口 cv2.destroyAllWindows() ``` 在修改后的代码中,我们定义了一个名为`image_callback`的回调函数,该函数接受相机对象和采集结果(`grab_result`)作为参数。在回调函数中,我们首先使用`GrabSucceeded`方法检查采集是否成功,然后处理图像数据并进行图像处理。在这个例子中,我们使用了OpenCV来显示图像,确保在运行之前安装了OpenCV模块。 请注意,这只是一个简单的示例代码,你可能还需要根据实际需求进行相应的处理和配置。更详细的用法和参数设置,请参考Basler相机的文档或Pylon SDK的文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值