Image常见方法解析

android->media->Image.java

Image 类,它代表了一个完整的图像缓冲区,可以与诸如 MediaCodecCameraDevice 等媒体源一起使用。

主要特点和用途
  • 直接访问图像数据Image 类允许通过一个或多个 ByteBuffer 实现对像素数据的高效直接访问。每个缓冲区都由一个 Plane 封装,描述了该平面中像素数据的布局。这种直接访问使得图像不像 Bitmap 类那样直接可用于 UI 资源。
  • 图像资源管理:由于图像通常直接由硬件组件生产或消费,它们是系统中的有限资源,应在不再需要时立即关闭。
  • ImageReader 使用示例:例如,使用 ImageReader 类从各种媒体源中读取图像时,不关闭旧的 Image 对象将阻止新的图像的可用性,一旦达到了最大未完成图像数量,获取新图像的函数通常会抛出 IllegalStateException 异常。
public abstract int getFormat();

getFormat() 方法,用于获取图像的格式。图像的格式决定了表示图像所需的 ByteBuffer 数量,以及每个 ByteBuffer 中像素数据的一般布局。

图像格式及其对应的平面数和布局细节

  • JPEG:压缩数据,只有一个平面。行和像素步长为0。要解压缩,请使用 BitmapFactory.decodeByteArray
  • YUV_420_888:有3个平面。首先是亮度平面,然后是Cb和Cr色度平面。色度平面的宽度和高度是亮度平面的一半(4:2:0抽样)。每个平面的像素采样为8位。
  • YUV_422_888:有3个平面。首先是亮度平面,然后是Cb和Cr色度平面。色度平面的宽度是亮度平面的一半,高度与亮度平面相同(4:2:2抽样)。每个平面的像素采样为8位。
  • YUV_444_888:有3个平面。首先是亮度平面,然后是Cb和Cr色度平面。色度平面的宽度和高度与亮度平面相同(4:4:4抽样)。每个平面的像素采样为8位。
  • FLEX_RGB_888:有3个平面。首先是红色平面,然后是绿色和蓝色平面。所有平面的宽度和高度相同,每个平面的像素采样为8位。
  • FLEX_RGBA_8888:有4个平面。首先是红色平面,然后是绿色、蓝色和alpha平面。所有平面的宽度和高度相同,每个平面的像素采样为8位。
  • RAW_SENSOR:只有一个平面。包含原始传感器图像数据,每个颜色采样为16位。布局的详细信息需要从原始传感器数据源(例如 CameraDevice)查询。
  • RAW_PRIVATE:只有一个平面,是私有布局的原始传感器图像数据。布局的详细信息是实现特定的。对于此格式,行步长和像素步长未定义。对 RAW_PRIVATE 图像调用 Plane.getRowStride()Plane.getPixelStride() 将引发 UnsupportedOperationException 异常。
  • HEIC:压缩数据,只有一个平面。行和像素步长为0。要解压缩,请使用 BitmapFactory.decodeByteArray
  • YCBCR_P010:有3个平面。P010是4:2:0 YCbCr半平面格式,由一个WxH的Y平面,后面是Wx(H/2)的Cb和Cr平面组成。每个样本由16位小端值表示,低6位设置为零。由于这是半平面格式,Cb平面也可以视为交错的Cb/Cr平面。

方法返回值

  • 返回图像的格式,其值是 ImageFormatPixelFormatHardwareBuffer 中的一个。
getWidth() | getHeight()

getWidth()getHeight() 方法,用于获取图像的宽度和高度。

  • getWidth() 方法
    • 获取图像的宽度,单位为像素。
    • 对于某些颜色通道进行子采样的格式,此宽度是具有最大分辨率平面的宽度。
  • getHeight() 方法
    • 获取图像的高度,单位为像素。
    • 对于某些颜色通道进行子采样的格式,此高度是具有最大分辨率平面的高度。

这些方法可用于确定图像的尺寸,以便在处理图像数据时准确地解释图像的布局和像素信息。

getTimestamp()

getTimestamp() 方法,用于获取与该帧关联的时间戳。

  • getTimestamp() 方法:

    • 获取图像帧的时间戳,以纳秒为单位。

    • 时间戳通常是单调递增的。

    • 不同来源的图像可能具有不同的时间戳基准,因此它们可能不可比较。

    • 时间戳的具体含义和基准取决于提供图像的源。可以查看 {@link android.hardware.Camera Camera}、{@link android.hardware.camera2.CameraDevice CameraDevice}、{@link MediaPlayer} 和 {@link MediaCodec} 获取更多详细信息。

getHardwareBuffer()

getHardwareBuffer() 方法,用于获取输入图像的 HardwareBuffer 句柄,以便进行 GPU 和/或硬件访问。

  • getHardwareBuffer() 方法:
    • 获取与此图像关联的 HardwareBuffer
    • 返回与该图像关联的 HardwareBuffer,如果该图像不支持此功能,则返回 null。
    • 调用此方法前需确保图像对象有效,否则将抛出 IllegalStateException

该方法是用于获取与图像关联的硬件缓冲区句柄,以便进行直接的 GPU 或硬件访问。

setTimestamp()

这个方法允许你为图像设置时间戳。时间戳是以纳秒为单位的,通常是单调递增的。不同来源的图像可能具有不同的时间基准,因此它们可能无法进行直接比较。时间戳的具体含义和时间基准取决于提供图像的源

// 假设从 ImageReader 中获取了一个图像对象 image

if (image != null) {
    long timestamp = System.nanoTime();  // 获取当前时间作为时间戳(以纳秒为单位)
    
    // 设置图像的时间戳
    image.setTimestamp(timestamp);

    // 可以继续处理图像或将其传递给其他模块进行进一步处理
    // ...

    // 处理完成后记得关闭图像,释放资源
    image.close();
} else {
    // 处理图像对象为 null 的情况
}
getCropRect()

裁剪矩形指定图像中有效像素的区域,使用最大分辨率平面的坐标。

具体来说:

  • 如果图像没有指定裁剪矩形(mCropRect为null),则返回一个包含整个图像尺寸的矩形(Rect),即从左上角(0, 0)到右下角(getWidth(), getHeight())。
  • 如果图像有指定裁剪矩形(mCropRect不为null),则返回裁剪矩形的副本(copy),避免直接返回引用,以确保图像的裁剪矩形不会被外部修改。

这个方法主要用于确定图像中有效像素的范围,对于处理图像的算法或操作来说是一个重要的参考,可以确保只处理图像中指定的有效区域,提高效率和准确性。

public Rect getCropRect() {
    throwISEIfImageIsInvalid();

    if (mCropRect == null) {
        return new Rect(0, 0, getWidth(), getHeight());
    } else {
        return new Rect(mCropRect); // return a copy
    }
}


Image.Plane[] planes = image.getPlanes();
Rect cropRect = image.getCropRect();

// 获取裁剪矩形的左上角和右下角坐标
int left = cropRect.left;
int top = cropRect.top;
int right = cropRect.right;
int bottom = cropRect.bottom;

// 计算裁剪矩形的宽度和高度
int width = right - left;
int height = bottom - top;

// 遍历图像的每个平面并处理有效像素区域
for (Image.Plane plane : planes) {
    ByteBuffer buffer = plane.getBuffer();
    int pixelStride = plane.getPixelStride();
    int rowStride = plane.getRowStride();

    // 计算有效像素数据的起始偏移量
    int offset = top * rowStride + left * pixelStride;

    // 根据偏移量和宽高处理图像数据
    // 这里只是一个示例,实际处理方式取决于图像格式和处理需求
    buffer.position(offset);
    // 在这里处理有效像素数据
}
getPlanes()

getPlanes() 方法用于获取图像的像素平面数组 Plane[]。每个图像都由一个或多个像素平面组成,具体平面数量取决于图像的格式。

Image.Plane[] planes = image.getPlanes();

// 遍历图像的每个像素平面
for (Image.Plane plane : planes) {
    ByteBuffer buffer = plane.getBuffer(); // 获取像素数据的 ByteBuffer
    int pixelStride = plane.getPixelStride(); // 获取像素步幅
    int rowStride = plane.getRowStride(); // 获取行步幅

    // 处理图像数据
    // 注意:这里只是一个示例,实际处理方式取决于图像格式和应用需求
    int bufferSize = buffer.remaining(); // 获取缓冲区大小
    byte[] pixels = new byte[bufferSize]; // 创建用于存储像素数据的数组
    buffer.get(pixels); // 将像素数据读取到数组中

    // 在这里可以对像素数据进行进一步处理,比如解码、渲染等
    // ...

    // 释放资源(注意:这里的示例未显示对资源的释放,实际应用中应该适时调用 image.close() 来释放资源)
}

// 在处理完所有像素平面后,记得调用 image.close() 来释放图像资源
image.close();

getPlanes() 获取图像的像素平面数组 planes,然后遍历每个像素平面 plane。对于每个像素平面,我们可以通过 plane.getBuffer() 获取像素数据的 ByteBuffer,并使用 plane.getPixelStride()plane.getRowStride() 获取像素步幅和行步幅等信息。接下来,我们可以根据需要对像素数据进行处理,比如解码、渲染等操作。最后,需要在适当的时候调用 image.close() 来释放图像资源,确保资源得到正确的释放和管理。

close()

close() 方法用于释放图像资源,使图像可以重新使用。调用该方法后,对图像的任何操作都将导致 IllegalStateException 异常,并且尝试读取或写入之前通过 Plane#getBuffer 调用返回的 ByteBuffer 将具有未定义的行为。如果图像是通过 ImageWriter#dequeueInputImage() 获取的,那么调用此方法后,应用程序填充的任何图像数据将丢失,并且图像将返回给 ImageWriter 以便重新使用。通过 ImageWriter#queueInputImage 提供的图像将自动关闭。

Image.Plane
/**
 * <p>A single color plane of image data.</p>
 *
 * <p>The number and meaning of the planes in an Image are determined by the
 * format of the Image.</p>
 *
 * <p>Once the Image has been closed, any access to the plane's
 * ByteBuffer will fail.</p>
 *
 * @see #getFormat
 */
public static abstract class Plane {
    /**
     * @hide
     */
    @UnsupportedAppUsage
    @TestApi
    protected Plane() {
    }

    /**
     * <p>The row stride for this color plane, in bytes.</p>
     *
     * <p>This is the distance between the start of two consecutive rows of
     * pixels in the image. Note that row stride is undefined for some formats
     * such as
     * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
     * and calling getRowStride on images of these formats will
     * cause an UnsupportedOperationException being thrown.
     * For formats where row stride is well defined, the row stride
     * is always greater than 0.</p>
     */
    public abstract int getRowStride();
    /**
     * <p>The distance between adjacent pixel samples, in bytes.</p>
     *
     * <p>This is the distance between two consecutive pixel values in a row
     * of pixels. It may be larger than the size of a single pixel to
     * account for interleaved image data or padded formats.
     * Note that pixel stride is undefined for some formats such as
     * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
     * and calling getPixelStride on images of these formats will
     * cause an UnsupportedOperationException being thrown.
     * For formats where pixel stride is well defined, the pixel stride
     * is always greater than 0.</p>
     */
    public abstract int getPixelStride();
    /**
     * <p>Get a direct {@link java.nio.ByteBuffer ByteBuffer}
     * containing the frame data.</p>
     *
     * <p>In particular, the buffer returned will always have
     * {@link java.nio.ByteBuffer#isDirect isDirect} return {@code true}, so
     * the underlying data could be mapped as a pointer in JNI without doing
     * any copies with {@code GetDirectBufferAddress}.</p>
     *
     * <p>For raw formats, each plane is only guaranteed to contain data
     * up to the last pixel in the last row. In other words, the stride
     * after the last row may not be mapped into the buffer. This is a
     * necessary requirement for any interleaved format.</p>
     *
     * @return the byte buffer containing the image data for this plane.
     */
    public abstract ByteBuffer getBuffer();
}

Plane 类表示图像数据中的单个颜色平面。图像中的平面数量和含义取决于图像的格式。

以下是 Plane 类的重要方法:

  1. getRowStride() 方法
    • 返回颜色平面的行跨度,以字节为单位。
    • 行跨度是指图像中两个连续像素行的起始点之间的距离。
    • 对于某些格式(如 RAW_PRIVATE),行跨度未定义,对这些格式的图像调用 getRowStride() 将导致 UnsupportedOperationException 异常。
  2. getPixelStride() 方法
    • 返回相邻像素样本之间的距离,以字节为单位。
    • 像素跨度是指一行像素中两个连续像素值之间的距离。
    • 对于某些格式(如 RAW_PRIVATE),像素跨度未定义,对这些格式的图像调用 getPixelStride() 将导致 UnsupportedOperationException 异常。
  3. getBuffer() 方法
    • 返回包含图像数据的直接 ByteBuffer
    • 返回的 ByteBuffer 总是直接缓冲区,可以在 JNI 中作为指针映射,无需使用 GetDirectBufferAddress 进行任何拷贝。
    • 对于原始格式,每个平面只保证包含数据直到最后一行的最后一个像素。换句话说,最后一行后的跨度可能未映射到缓冲区中。这是任何交错格式的必要条件。

这些方法允许您获取有关图像颜色平面的重要信息,例如行跨度、像素跨度和包含图像数据的 ByteBuffer

示例

     ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
     ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
     int yStride = image.getPlanes()[0].getRowStride();
     int uvStride = image.getPlanes()[1].getRowStride();
	 int yuvSize = yStride * height + uvStride * height / 2;
     int width = image.getWidth();
     int height = image.getHeight();

     nativeSetInputImage(mHandle, yBuffer, uBuffer, vBuffer,
             index, yStride, uvStride, width, height, evValue);


    public static boolean imageToNV12(Image image, byte[] dst) {
        if (image == null) {
            LogUtil.e("NULL Image", "image is null");
            return false;
        }
        final int imageWidth = image.getWidth();
        final int imageHeight = image.getHeight();
        final Image.Plane[] planes = image.getPlanes();
        int offset = 0;
        for (int channel = 0; channel < planes.length - 1; channel++) {
            Image.Plane plane = planes[channel];
            final ByteBuffer buffer = plane.getBuffer();
            if (buffer == null) {
                return false;
            }
            final int rowStride = plane.getRowStride();
            // plane.getPixelStride()==2, 说明UV交错存储在buffer上
            // Experimentally, U and V planes have |pixelStride| = 2, which
            // essentially means they are packed. That's silly, because we are
            // forced to unpack here.
            final int pixelStride = plane.getPixelStride();
            // YUV420: UV数据是Y的1/2
            int rows = imageHeight / pixelStride;
            if (rowStride == imageWidth) {
                // Copy whole plane from buffer into |data| at once.
                int length = rowStride * rows;
                // UV plane的buffer比实际图像UV小1byte,读buffer实际大小
                buffer.get(dst, offset, buffer.remaining());
                offset += length;
            } else {
                //图像每行有填充额外数据做字节对齐
                for (int i = 0; i < rows - 1; i++) {
                    buffer.get(dst, offset, imageWidth);
                    buffer.position(i * rowStride);
                    offset += imageWidth;
                }
                // UV plane的buffer比实际图像UV小1byte,最后一行读buffer实际大小
                // Last row is special in some devices and may not contain the full
                // |rowStride| bytes of data. See  http://crbug.com/458701  and
                // http://developer.android.com/reference/android/media/Image.Plane.html#getBuffer()
                int lastRowLength = Math.min(imageWidth, buffer.remaining());
                buffer.get(dst, offset, lastRowLength);
                offset += imageWidth;
            }

            buffer.rewind();
        }

        return true;
    }

总结:

Image 类提供了访问图像数据的方法,允许应用程序直接处理图像数据。以下是 Image 类中的重要方法和功能总结:

  1. 图像格式和属性

    • getFormat(): 获取图像的格式,如 JPEG、YUV 等。
    • getWidth(): 获取图像的宽度。
    • getHeight(): 获取图像的高度。
    • getTimestamp(): 获取图像的时间戳。
    • getCropRect(): 获取图像的裁剪矩形。
  2. 图像数据访问

    • getPlanes(): 获取图像的颜色平面数组,用于访问图像数据的每个平面。
    • getHardwareBuffer(): 获取图像关联的 HardwareBuffer,用于直接访问 GPU 和硬件。
  3. 图像数据处理

    • close(): 关闭图像,释放图像资源,防止访问已关闭的图像。
  4. 其他功能

    • setTimestamp(long timestamp): 设置图像的时间戳。
    • setFence(SyncFence fence): 设置图像的同步屏障。
    • getTransform(): 获取图像的转换。
    • getScalingMode(): 获取图像的缩放模式。
  5. Image.Plane

    Image.Plane 类是 Image 类中的一个内部静态抽象类,用于表示图像数据的单个颜色平面。每个 Image 对象可以包含一个或多个 Plane 对象,具体取决于图像的格式。

    以下是 Image.Plane 类中的重要方法和功能总结:

    1. 颜色平面属性

      • getRowStride(): 获取颜色平面的行跨距,即相邻两行像素数据之间的字节距离。
      • getPixelStride(): 获取颜色平面的像素跨距,即相同颜色分量的两个相邻像素之间的字节距离。
      • getBuffer(): 获取包含颜色平面数据的 ByteBuffer 对象,用于直接访问图像数据。
    2. 颜色平面数据访问

      • getRowStride()getPixelStride() 方法用于了解颜色平面的布局和像素间距。
      • getBuffer() 方法返回一个 ByteBuffer 对象,用于访问颜色平面的像素数据。

      Image.Plane 类提供了对图像数据的底层访问,允许应用程序直接操作图像数据的每个颜色平面。通过 getBuffer() 方法返回的 ByteBuffer 对象可以用于读取或写入图像数据。注意,颜色平面的行跨距和像素跨距可能因图像格式而异,对于某些特定格式,这些属性可能未定义。

      在使用完图像数据后,应及时释放对 Image 对象的引用,并确保不再访问已关闭的图像或颜色平面,以避免出现未定义的行为。

这些方法使您能够获取图像的基本属性(格式、宽度、高度、时间戳等),访问图像的像素数据(颜色平面、缓冲区等),并执行一些图像数据处理操作(设置时间戳、同步屏障等)。务必在使用完图像后调用 close() 方法以释放资源。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值