版权声明:本文使用https://creativecommons.org/licenses/by-nc-nd/4.0/规定的《署名-非商业性使用-禁止演绎 4.0 国际》协议 https://blog.csdn.net/bluewindtalker/article/details/79999172
本篇文章即为通过一系列的方法获得当前摄像头所处环境的亮度,最相近的就是微信的扫一扫提示“轻触照亮”打开手电筒的功能,首先我们看一个方法setPreviewFormat方法,在注释中我们可以清楚的看到默认使用NV21格式,
/**
* Sets the image format for preview pictures.
* <p>If this is never called, the default format will be
* {@link android.graphics.ImageFormat#NV21}, which
* uses the NV21 encoding format.</p>
*
* 此处省略部分内容
*
* @param pixel_format the desired preview picture format, defined by
* one of the {@link android.graphics.ImageFormat} constants. (E.g.,
* <var>ImageFormat.NV21</var> (default), or
* <var>ImageFormat.YV12</var>)
*
* @see android.graphics.ImageFormat
* @see android.hardware.Camera.Parameters#getSupportedPreviewFormats
*/
public void setPreviewFormat(int pixel_format) {
String s = cameraFormatForPixelFormat(pixel_format);
if (s == null) {
throw new IllegalArgumentException(
"Invalid pixel_format=" + pixel_format);
}
set(KEY_PREVIEW_FORMAT, s);
}
NV21是什么格式呢?NV21是YUV格式的一种,YUV主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽(RGB要求三个独立的视频信号同时传输)。其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。其中转换公式为(RGB取值范围均为0-255)︰
其实他们的转化矩阵是
那NV21的具体编码格式是什么样呢,NV21格式就是YUV420SP格式,
NV21的存储格式是YYYYYYYY VUVU
而 NV12存储格式是 YYYYYYYY UVUV,
下图为I420格式: YYYYYYYY UU VV
同过上图我们得知,如果拿到亮度即为拿到Y值即可,而Y值是数组中前N个(N为图像宽乘以高得到的像素点总数)。
https://blog.csdn.net/bluewindtalker/article/details/79999172
我们先看下摄像头识别的代码通过调用setPreviewCallback方法,在回调的方法中获得NV21格式的byte数组,这个数组即为上述格式,通过对NV21的data数组进行亮度数值采集获得图片的总亮度,然后除当前采集的像素点个数即为当前图片的平均值。下面看具体实现:
//上次记录的时间戳
long lastRecordTime = System.currentTimeMillis();
//上次记录的索引
int darkIndex = 0;
//一个历史记录的数组,255是代表亮度最大值
long[] darkList = new long[]{255, 255, 255, 255};
//扫描间隔
int waitScanTime = 300;
//亮度低的阀值
int darkValue = 60;
private void setPreviewLight() {
//不需要的时候直接清空
// if(noNeed){
// camera.setPreviewCallback(null);
// return;
// }
camera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastRecordTime < waitScanTime) {
return;
}
lastRecordTime = currentTime;
int width = camera.getParameters().getPreviewSize().width;
int height = camera.getParameters().getPreviewSize().height;
//像素点的总亮度
long pixelLightCount = 0L;
//像素点的总数
long pixeCount = width * height;
//采集步长,因为没有必要每个像素点都采集,可以跨一段采集一个,减少计算负担,必须大于等于1。
int step = 10;
//data.length - allCount * 1.5f的目的是判断图像格式是不是YUV420格式,只有是这种格式才相等
//因为int整形与float浮点直接比较会出问题,所以这么比
if (Math.abs(data.length - pixeCount * 1.5f) < 0.00001f) {
for (int i = 0; i < pixeCount; i += step) {
//如果直接加是不行的,因为data[i]记录的是色值并不是数值,byte的范围是+127到—128,
// 而亮度FFFFFF是11111111是-127,所以这里需要先转为无符号unsigned long参考Byte.toUnsignedLong()
pixelLightCount += ((long) data[i]) & 0xffL;
}
//平均亮度
long cameraLight = pixelLightCount / (pixeCount / step);
//更新历史记录
int lightSize = darkList.length;
darkList[darkIndex = darkIndex % lightSize] = cameraLight;
darkIndex++;
boolean isDarkEnv = true;
//判断在时间范围waitScanTime * lightSize内是不是亮度过暗
for (int i = 0; i < lightSize; i++) {
if (darkList[i] > darkValue) {
isDarkEnv = false;
}
}
Log.e(TAG, "摄像头环境亮度为 : " + cameraLight);
if (!isFinishing()) {
//亮度过暗就提醒
if (isDarkEnv) {
lightTV.setVisibility(View.VISIBLE);
} else {
lightTV.setVisibility(View.GONE);
}
}
}
}
});
}
实现效果我们可以看到
代码的git地址为:https://github.com/bluewindtalker/camerademo
来源:CSDN
作者:bluewindtalker
链接:https://blog.csdn.net/bluewindtalker/article/details/79999172