Android 8.1 DisplayPowerController(五) 自动调节亮度(2)——算法

本文详细分析了Android 8.1中自动背光亮度调节的算法,包括光强缓冲区的处理、Lux值计算、样条插值和Lux阀值的计算。通过对AmbientLightRingBuffer类的构造方法、数据存储、Lux值计算方法的探讨,揭示了自动亮度调节的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章中,对自动背光的流程做了总结,在本篇中,将对自动背光涉及到的一些算法进行分析总结。

1.采集光强缓冲区

AmbientLightRingBuffer类是一个用于存储采集到的光照强度和对应时间点的数据结构。在自动背光控制器中,实例化了两个AmbientLightRingBuffer对象:

//包含所有光照样例的AmbientLightRingBuffer对象
mAmbientLightRingBuffer =
    new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
//只包含在初始时间范围周期的光照样例的AmbientLightRingBuffer对象
mInitialHorizonAmbientLightRingBuffer =
    new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);

mAmbientLightRingBuffer是包含所有光照样例的AmbientLightRingBuffer对象,mInitialHorizonAmbientLightRingBuffer只包含在初始采光时间范围周期的光照样例的AmbientLightRingBuffer对象,这个初始采光时间范围周期是指当LSensor启用开始计时,到一个采光时间周期,采光时间周期mAmbientLightHorizon则在配置文件中读取:

<!--一个采光周期为10s-->
<integer name="config_autoBrightnessAmbientLightHorizon">10000</integer>

下面我们从它的构造方法开始,来分析缓冲区的实现原理和其中包括的算法。

1.1.构造方法

AmbientLightRingBuffer的构造方法如下:

    private static final class AmbientLightRingBuffer {
   
        private static final float BUFFER_SLACK = 1.5f;
        private float[] mRingLux;
        private long[] mRingTime;
        private int mCapacity;

	//缓冲区中第一个数据位置
        private int mStart;
        //缓冲区中下一个槽口位置
        private int mEnd;
        //缓冲区中光照样例的数量
        private int mCount;

        public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
   
	    //缓冲区容量
            mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
            //存储Lux值的数组
            mRingLux = new float[mCapacity];
	    //存储时间的数组
            mRingTime = new long[mCapacity];
        }

在构造方法中,初始化了两个数组分别用来存储时间和Lux值,而这两个数组的长度,是根据采集光照样例的时间范围周期 * BUFFER_SLACK/LSensor事件率得到的。其实也可以这样理解:总时间 / 每次上报数据的时间 = 时间周期内上报事件的个数 = 缓冲区大小。

1.2.向缓冲区中push光照样例

将上报的LSensor数据记录到缓冲区时,使用push()方法,该方法如下:

        public void push(long time, float lux) {
   
            //标记要存储的位置
            int next = mEnd;
            if (mCount == mCapacity) {
   
                //如果缓冲区已满,则将进行扩容
                int newSize = mCapacity * 2;
                //初始化两个新的数组,大小为原来数组大小的2倍
                float[] newRingLux = new float[newSize];
                long[] newRingTime = new long[newSize];
                //数组长度减去第一个元素位置=原数组的长度
                int length = mCapacity - mStart;
                //将旧数组中内容拷贝到新数组中
                //将mRingLux数组从mStart位起,拷贝到newRingLux中,放在0位置起,拷贝length个数据
                System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
                System.arraycopy(mRingTime, mStart, newRingTime, 0, length);

                //如果原数组中第一个数据的索引不为0
                if (mStart != 0) {
   
                    //将mRingLux数组从0位起,拷贝到newRingLux中,放在length位置起,拷贝mStart个数据
                    //即如果mStart不为0,将mStart前的数据放在了最后
                    System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
                    System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
                }
                mRingLux = newRingLux;
                mRingTime = newRingTime;

                next = mCapacity;
                mCapacity = newSize;
                mStart = 0;
            }
            //记录时间和Lux
            mRingTime[next] = time;
            mRingLux[next] = lux;
            mEnd = next + 1;//下一个槽口
            //如果到达数组尾端,则将mEnd置为0,在下一次进入后,由于mCount == mCapacity,因此又会将mEnd置为next+1
            if (mEnd == mCapacity) {
   
                mEnd = 0;
            }
            //记录数+1
            mCount++;
        }

在这个方法中,会将LSensor上报的值和时间点记录到缓冲区中,此处有两个地方的计算方式如下:

  • 1.如果当前缓冲区已满,则对其进行扩容,新的容量是原来容量的2倍,实际上是重新初始化了两个数组,并将旧数组中的元素从保存的第一个元素起,拷贝到了新数组中,从0索引开始存放数据。
  • 2.如果mStart不为0,说明缓冲区数组中存储元素的起始位置不为0(被prune()方法操作过),所以0-mStart之间的记录已被标记为删除,则在拷贝时,将旧数组中0-mStart之间的元素拷贝到新数组中,从length索引开始存放。

根据以上两个计算方式,从而形成了类似环的环形缓冲区。

1.3.从缓冲区中移除某个时间前的数据

每当LSensor收到上报数据后,会通过push()方法记录到缓冲区,而对据此次上报时间一个采光时间周期前的数据,则进行删除,该方法如下:


        public void prune(long horizon) {
   
            //如果当前缓冲区中无数据,直接返回
            if (mCount == 0) {
   
                return;
            }

            while (mCount > 1) {
   
                //表示第二个有效元素位置
                int next = mStart + 1;
                //如果第二个元素置位值大于等于容量值,说明整个缓冲区中只有一个元素,索引为mStart.
                if (next >= mCapacity) {
   
                    next -= mCapacity;
                }
                //如果第二个有效元素的时间点大于传入的时间,则停止
                if (mRingTime[next] > horizon) {
   
                    break;
                }
                //重置第一个有效元素索引
                mStart = next;
                //数量值-1
                mCount -= 1;
            }

            //如果第一个有效元素时间小于传入的时间,则重置第一个有效元素的时间值
            if (mRingTime[mStart] < horizon) {
   
                mRingTime[mStart] = horizon;
            }
        }

下图简单表示了push()prune()方法调用时缓冲区状态示例:

1.4.缓冲区获取索引值

缓冲区提供了getTime(int index)getLux(int index)方法,根据参数index得到缓冲区中对应的数据,然而,由于缓冲区中数据的存储是根据缓冲区内部的变量mStart标记存储的起始位置,因此,需要将index进行转换,使用offsetOf(int index)转换,从而得到正确的位置:

        private int offsetOf(int index) {
   
            if (index >= mCount || index < 0) {
   
                throw new ArrayIndexOutOfBoundsException(index);
            }
            //mStart表示第一个元素的位置
            index += mStart;
            //如果index >= mCapacity,直接index = index -mCapacity
            if (index >= mCapacity) {
   
                index -= mCapacity;
            }
            return index;
        }
    }

1.5.缓冲区中其他方法

其他方法比较简单,如下:

        //获取Lux值
        public float getLux(int index) {
   
            return mRingLux[offsetOf(index)];
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值