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) - 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图像格式介绍 - 简书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版
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").
光栅模型的基本策略是 平面的细分,细分成像素, 下图中的每个像素(每个小方块)
解读BMP图片 - 李佳浩的博客 | LiJiaHao' Bloghttps://geocld.github.io/2021/03/02/bmp/ 浅谈图像格式 .bmp - 知乎
https://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/50272771
typedef struct tagBITMAP_FILE{
BITMAPFILEHEADER bitmapheader;
BITMAPINFOHEADER bitmapinfoheader;
PALETTEENTRY palette[256];
UCHAR *buffer; //UCHAR 大小1字节(同BYTE),在VC6下
} BITMAP_FILE;
①BMP文件头 (14字节):BITMAPFILEHEADER
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 |
说明从文件头开始到实际的图像数据之间的字节偏移量。这个参数是非常有用的,因为位图信息和调色板的长度会根据不同的情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。 |
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的分量值
这里每3个字节表示一个像素的颜色,注意:由于位图信息头中的图像高度是正数,所以位图数据在文件中的排列顺序是从左下角到右上角,以行为主序排列的。
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();
}
@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
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();
}
});
}
android/lib_support/src/main/java/org/tensorflow/lite/examples/classification/tflite/Classifier.java
/** Runs inference and returns the classification results. */
public List
/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
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