CV: camera数据预处理

Camera 常见的数据输出格式有:Rawdata 格式、YUV 格式、RGB 格式。

RGB 格式:采用这种编码方法,每种颜色都可用三个变量来表示红色、绿色以及蓝色的强度。每一个像素有三原色 R 红色、G 绿色、B 蓝色组成。

YUV 格式:其中“Y”表示明亮度(Luminance 或 Luma),就是灰阶值;而“U”和“V”表示色度(Chrominance 或 Chroma),是描述影像色彩及饱和度,用于指定像素的颜色。

RAW DATA 格式:是 CCD 或 CMOS 在将光信号转换为电信号时的电平高低的原始记录,单纯地将没有进行任何处理的图像数据,即摄像元件直接得到的电信号进行数字化处理而得到的。

支持 YUV/RGB 格式的模组,一般会在模组上集成 ISP(Image Single Processor),经过A/D 转换过的原始数据经过 ISP 处理生成 YUV 标准格式传到 BB。一般来说,这种设计适用于低像素 Camera 的要求,会在主板上省去一个 DSP,可降低成本。在调试过程中,YUV/RGB 格式的摄像头,其所有参数都可在 kernel 层通过寄存器来控制。调试一般由 sensor的原厂支持。

支持 RawData 格式的模组,由于感光区域的需求,不会再模组内集成 ISP 以最大程度的增大感光区域的面积,提高照片质量。模组把原始的数字信号传给 BB 上的 DSP 进行处理,MTK 自带的 DSP 一般包含 ISP、JPEG encoder、和 DSP 控制芯片。在调试的时候图像的效果需要 MTK 在 HAL 层的参数进行支持。


Android: Image类浅析(结合YUV_420_888)

Android: Image类浅析(结合YUV_420_888) - Penguin简介Image类在API 19中引入,但真正开始发挥作用还是在API 21引入CameraDevice和MediaCodec的增强后。API 21引入了Camera2,deprecated掉了C...https://www.polarxiong.com/archives/Android-Image%E7%B1%BB%E6%B5%85%E6%9E%90-%E7%BB%93%E5%90%88YUV_420_888.html

Android相机开发系列 - PenguinAndroid Camera Develop Series简介Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做...https://www.polarxiong.com/archives/Android%E7%9B%B8%E6%9C%BA%E5%BC%80%E5%8F%91%E7%B3%BB%E5%88%97.html

RGB、YUV图像格式介绍

图像实战 - RGB、YUV图像格式介绍 - 简书ArcFace 2.0 API目前支持多种图像格式:BGR24、NV21、NV12、I420、YUYV(Android、IOS只支持其中的部分)。以下介绍这几种图像格式的内存...https://www.jianshu.com/p/a8ae092bb8b8

ImageFormat.YUV_420_888(4个Y对应一组UV,即平均1个像素占1.5个byte,12位) 

Android使用Camera2获取预览数据 - 简书一、Camera2简介 Camera2是Google在Android 5.0后推出的一个全新的相机API,Camera2和Camera没有继承关系,是完全重新设计的,且Cam...https://www.jianshu.com/p/7bb94bf7e34d

为什么推荐使用CameraX?_allisonchen的专栏-CSDN博客本文可能是当下最新最全的CameraX解读,篇幅很长,你准备好了吗?我们的生活已经越来越离不开相机,从自拍到直播,扫码再到VR等等。相机的优劣自然就成为了厂商竞相追逐的赛场。对于app开发者来说,如何快速驱动相机,提供优秀的拍摄体验,优化相机的使用功耗,是一直以来依赖追求的目标。前言Android 5.0 时期Camera接口便已弃用,所以一般的做法是使用其替代者Camera2接口。但随着CameraX的出现,这个选择变得不再唯一。我们先来回顾下图像预览这一简单的需求,使用Camera2接口是..https://blog.csdn.net/allisonchen/article/details/113483498 cameraX api 当前还是beta版 

Camera 数据格式 

什么是raster graphic

In computer graphics and digital photography, a raster graphic is a mechanism that represents a two-dimensional image  as a rectangular matrix or grid of square pixels, viewable via a computer display, paper, or other display medium. 
A raster is technically characterized by the width and height of the image in pixels and by the number of bits per pixel.  Raster images are stored in image files with varying dissemination, production, generation, and acquisition formats.

The fundamental strategy underlying the raster model is the tessellation of a plane, into a two dimensional array of squares, each called a cell or pixel (from "picture element").

光栅模型的基本策略是 平面的细分,细分成像素, 下图中的每个像素(每个小方块)

CV: camera数据预处理_第1张图片

 BMP 数据格式

解读BMP图片 - 李佳浩的博客 | LiJiaHao' Blogicon-default.png?t=LA92https://geocld.github.io/2021/03/02/bmp/ 浅谈图像格式 .bmp - 知乎icon-default.png?t=LA92https://zhuanlan.zhihu.com/p/25119530

BMP格式详解_Tut-CSDN博客_bmp格式BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Wihttps://blog.csdn.net/u012877472/article/details/50272771CV: camera数据预处理_第2张图片

数据结构表示

typedef struct tagBITMAP_FILE{

        BITMAPFILEHEADER  bitmapheader;

        BITMAPINFOHEADER  bitmapinfoheader;

        PALETTEENTRY  palette[256];

        UCHAR  *buffer;        //UCHAR  大小1字节(同BYTE),在VC6下

}  BITMAP_FILE;

①BMP文件头 (14字节):BITMAPFILEHEADER

typedef struct tagBITMAPFILEHEADER   { 

        WORD bfType;//位图文件的类型,必须为BM(1-2字节)

        DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)

        WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)

        WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)

        DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)

        //文件头的偏移量表示,以字节为单位

}BITMAPFILEHEADER;

bfType

说明文件的类型,该值必须是0x4D42,也就是字符'BM',否则表示根本不是BMP

bfSize

说明该位图文件的大小,用字节为单位

bfReserved1

保留,必须设置为0

bfReserved2

保留,必须设置为0

bfOffBits

说明从文件头开始到实际的图像数据之间的字节偏移量。这个参数是非常有用的,因为位图信息和调色板的长度会根据不同的情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。

②位图信息段 (40字节):BITMAPINFOHEADER

typedef  struct  tagBITMAPINFOHEADER  {

        DWORD  biSize; //本结构所占用字节数(15-18字节)

        LONG  biWidth; //位图的宽度,以像素为单位(19-22字节)

        LONG  biHeight; //位图的高度,以像素为单位(23-26字节)

        WORD  biPlanes; //目标设备的级别,必须为1(27-28字节)

        WORD  biBitCount; 

        DWORD  biCompression; //位图压缩类型,必须是0(不压缩),(31-34字节)

        //1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一

        DWORD  biSizeImage; //位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)

        LONG  biXPelsPerMeter; //位图水平分辨率,每米像素数(39-42字节)

        LONG  biYPelsPerMeter; //位图垂直分辨率,每米像素数(43-46字节)

        DWORD  biClrUsed; //位图实际使用的颜色表中的颜色数(47-50字节)

        DWORD  biClrImportant; //位图显示过程中重要的颜色数(51-54字节)

}  BITMAPINFOHEADER;

biSize

说明BITMAPINFOHEADER结构所需要的字节数

biWidth

说明图像的宽度,以像素为单位

biHeight

说明图像的高度,以像素位单位。注:这个值除了用于描述图像的高度之外,它还有另外一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,即数据的第一行其实是图像的最后一行,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是高度值是一个正数。

biPlanes

表示bmp图片的平面属性,显然显示器只有一个平面,所以恒等于1

biBitCount

说明比特数/像素,其值为1、4、8、16、24或32.

biCompression

说明图像数据压缩的类型,其中:

BI_RGB:没有压缩

BI_RLE8:每个像素8比特的RLE压缩编码,压缩格式由2字节组成(重复像素计数和颜色索引)

BI_RLE4:每个像素4比特的RLE压缩编码,压缩格式由2字节组成

BI_BITFIELDS:每个像素的比特由指定的掩码决定

BI_JPEG:JPEG格式

biSizeImage

说明图像的大小,以字节为单位。当用BI_RGB格式时,可设置为0

biXPelsPerMeter

说明水平分辨率,用像素/米表示

biYPelsPerMeter

说明垂直分辨率,用像素/米表示

biClrUsed

说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有的调色板项)

biClrImportant

说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要

③调色板

typedef struct tagRGBQUAD { 

        BYTE    rgbBlue; 

        BYTE    rgbGreen; 

        BYTE    rgbRed; 

        BYTE    rgbReserved;

 } RGBQUAD;

字段名称

大小(单位:字节)

描述

rgbBlue

1

蓝色值

rgbGreen

1

绿色值

rgbRed

1

红色值

rgbReserved

1

保留,总为0

④位图数据

24位BMP图,每三个字节表示一个像素,三个字节分别代表R、G、B的分量值

CV: camera数据预处理_第3张图片

这里每3个字节表示一个像素的颜色,注意:由于位图信息头中的图像高度是正数,所以位图数据在文件中的排列顺序是从左下角到右上角,以行为主序排列的。

TFLite example image 处理 

  private Runnable imageConverter;
  private byte[][] yuvBytes = new byte[3][];
  private int[] rgbBytes = null;

  /** Callback for Camera2 API */
  @Override
  public void onImageAvailable(final ImageReader reader) {

    if (rgbBytes == null) {
      rgbBytes = new int[previewWidth * previewHeight];
    }
    try {
      final Image image = reader.acquireLatestImage();

      if (image == null) {
        return;
      }

      if (isProcessingFrame) {
        image.close();
        return;
      }
      isProcessingFrame = true;
      Trace.beginSection("imageAvailable");
      final Plane[] planes = image.getPlanes();
      fillBytes(planes, yuvBytes);
      yRowStride = planes[0].getRowStride();
      final int uvRowStride = planes[1].getRowStride();
      final int uvPixelStride = planes[1].getPixelStride();

      // getRgbBytes()
      imageConverter = 何时调用?
          new Runnable() {
            @Override
            public void run() {
              ImageUtils.convertYUV420ToARGB8888(
                  yuvBytes[0],
                  yuvBytes[1],
                  yuvBytes[2],
                  previewWidth,
                  previewHeight,
                  yRowStride,
                  uvRowStride,
                  uvPixelStride,
                  rgbBytes);
            }
          };

      // readyForNextImage()
      postInferenceCallback = 何时调用?
          new Runnable() {
            @Override
            public void run() {
              image.close();
              isProcessingFrame = false;
            }
          };

      processImage();
    } catch (final Exception e) {
      LOGGER.e(e, "Exception!");
      Trace.endSection();
      return;
    }
    Trace.endSection();
  }

processImage()

  @Override
  protected void processImage() {
    rgbFrameBitmap.setPixels(getRgbBytes(), 0, previewWidth, 0, 0, previewWidth, previewHeight);
    final int cropSize = Math.min(previewWidth, previewHeight);

    runInBackground(
        new Runnable() {
          @Override
          public void run() {
            if (classifier != null) { // 创建classifier对象
              final long startTime = SystemClock.uptimeMillis();
              final List results =
                  classifier.recognizeImage(rgbFrameBitmap, sensorOrientation);
              lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;
              LOGGER.v("Detect: %s", results);

              runOnUiThread(
                  new Runnable() {
                    @Override
                    public void run() {
                      showResultsInBottomSheet(results);
                      showFrameInfo(previewWidth + "x" + previewHeight);
                      showCropInfo(imageSizeX + "x" + imageSizeY);
                      showCameraResolution(cropSize + "x" + cropSize);
                      showRotationInfo(String.valueOf(sensorOrientation));
                      showInference(lastProcessingTimeMs + "ms");
                    }   
                  }); 
            }   
            readyForNextImage();
          } 
        }); 
  }
 

recognizeImage

 android/lib_support/src/main/java/org/tensorflow/lite/examples/classification/tflite/Classifier.java
  /** Runs inference and returns the classification results. */
  public List recognizeImage(final Bitmap bitmap, int sensorOrientation) {
    /inputImageBuffer/ Logs this method so that it can be analyzed with systrace.
    Trace.beginSection("recognizeImage");

    Trace.beginSection("loadImage");
    long startTimeForLoadImage = SystemClock.uptimeMillis();
    inputImageBuffer = loadImage(bitmap, sensorOrientation);
    long endTimeForLoadImage = SystemClock.uptimeMillis();
    Trace.endSection();
    Log.v(TAG, "Timecost to load the image: " + (endTimeForLoadImage - startTimeForLoadImage));

    // Runs the inference call.
    Trace.beginSection("runInference");
    long startTimeForReference = SystemClock.uptimeMillis();
    tflite.run(inputImageBuffer.getBuffer(), outputProbabilityBuffer.getBuffer().rewind());
    long endTimeForReference = SystemClock.uptimeMillis();
    Trace.endSection();
    Log.v(TAG, "Timecost to run model inference: " + (endTimeForReference - startTimeForReference));

    // Gets the map of label and probability.
    Map labeledProbability =
        new TensorLabel(labels, probabilityProcessor.process(outputProbabilityBuffer))
            .getMapWithFloatValue();
    Trace.endSection();

    // Gets top-k results.
    return getTopKProbability(labeledProbability);
  }

数据预处理返回TensorImage

  /** Loads input image, and applies preprocessing. */
  private TensorImage loadImage(final Bitmap bitmap, int sensorOrientation)      {                                                                                                                                      
    // Loads bitmap into a TensorImage.
    inputImageBuffer.load(bitmap);

    // Creates processor for the TensorImage.
    int cropSize = min(bitmap.getWidth(), bitmap.getHeight());
    int numRotation = sensorOrientation / 90;
    // TODO(b/143564309): Fuse ops inside ImageProcessor.
    ImageProcessor imageProcessor =
        new ImageProcessor.Builder()
            .add(new ResizeWithCropOrPadOp(cropSize, cropSize))
            // TODO(b/169379396): investigate the impact of the resize algorithm on accuracy.
            // To get the same inference results as lib_task_api, which is built on top of the Task
            // Library, use ResizeMethod.BILINEAR.
            .add(new ResizeOp(imageSizeX, imageSizeY, ResizeMethod.NEAREST_NEIGHBOR))
            .add(new Rot90Op(numRotation))
            .add(getPreprocessNormalizeOp())
            .build();
    return imageProcessor.process(inputImageBuffer);
  }

lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/ImageProcessor.java

你可能感兴趣的:(人工智能,opencv,计算机视觉,visual,studio)