鱼一直对单反的自动测光原理很感兴趣,可没学过光学和光电传感器相关知识的我一直都不知道怎样实现它。
直到我看到了一个 老外做的测光表
其实很简单,只需要一个普通的光强传感器(精度越高越好,动态范围越大越好)
难的部分(也是困扰我的部分)是计算的算法,好在查wiki可以查到,还以为是什么相机厂商的黑科技呢(23333
本文目录
EV-Exposure Value
曝光值代表能够给出同样曝光的所有相机光圈和快门的组合。这一概念是在一九五零年代在德国发展起来的,被试图用以简化在等价的拍摄参数之间进行选择的过程。曝光值同样也可以表示曝光刻度上的一个级差,1EV对应于两倍的曝光比例并通常被称为“一挡”
曝光值最早是使用符号Ev来表示,ISO标准中(不是感光的那个ISO,是国际标准化组织)延续了这一标记法,但在其他地方EV这个缩写更常见~
Define
曝光值是一个以2为底的对数刻度系统:
E_v = \log_2 {N^2 \over t}
其中N是光圈(F value),t是曝光时间(快门),单位秒(s).
EV & Light(Lux)
在给定照明条件下所采用的F值与曝光时间由下式给出:
{N^2 \over t} = {LS \over K}
其中:
- N是相对光圈(F值)
- t是曝光时间,单位秒(s)
- L是场景平均辉度(就是平均亮度)
- S是ISO指数
- K是反射式测光表校正常数
代入等式右边,得到曝光值:
E_v = \log_2{LS \over K}
同样也可以由入射式测光结果计算拍摄参数:
{N^2 \over t} = {ES \over C}
其中:
- E是照度
- C是入射式测光表校正常数
当然也有:
E_v = log_2{ES \over C}
用EV作为照度与亮度的量度
好绕口的标题.jpg
在给定ISO和测光表校正常数(K或C)之后,EV与亮度(或照度)之间就建立了直接的关联。
严格地说,曝光值并不是亮度或照度的量度:事实上,一个对应于某一亮度(或照度)的曝光值是用以指示照相机(已知其感光度)来获取所谓的正确曝光用的。然而,摄影器材生产商在实践中常常用感光度100时的曝光值来表达亮度,如在说明测光范围或对焦灵敏度时。这一做法由来已久。标准的做法是同时声明所用测光表校正常数和对应的感光度,但很少有厂商会这么做。
不同厂商所选用的反射式测光表校正常数之间有微小的差别。一个普遍的选择是12.5(佳能,尼康和世光)。在感光度100时,曝光值与亮度的关系为:
L = 2^{EV-3}
该式的推导如下:
已知
EV = log_2{N^2 \over t}
{N^2 \over t} = {LS \over K}
\Longrightarrow
L = 2^{EV}*{K \over S}
while K=12.5,S=100,
\Longrightarrow L = 2^{EV}*0.125=2^{EV-3}
入射式测光表的情况比反射式测光表要复杂得多,因为校正常数C与传感器类型有关。有两种传感器很常见:平面型(余弦响应)和半球型(心形响应)。用平面型传感器测量照度时,C 的典型值是250,而照度的单位是勒克斯(lux)。在 C = 250时,照度与感光度100下的曝光值的关系为:
E = 2.5*2^{EV}
推导如下:
已知
EV = log_2{N^2 \over t}
{N^2 \over t} = {ES \over C}
\Longrightarrow
E = 2^{EV}*{C \over S}
while C=250,S=100,
\Longrightarrow L = 2.5*2^{EV}
尽管(用平面型传感器)测量得到的照度可以用以指示对于一个平面物体的曝光,这一办法对于通常的场景却不是那么有用,因为这些场景中的许多元素都不是平的,其相对于相机的朝向也各不相同。半球型传感器在测量影像曝光时更为实用。对于半球型传感器,C值通常在320(美能达)和340(世光)之间。如果对“照度”的定义不那么严格的话,半球型传感器所测得的结果可以说是指示了“场景照度”。
下表显示了该关系式下不同曝光值所对应的亮度值.用这个式子,一个以曝光值为示数的反射式测光表可以用来测量亮度
Table
曝光值与亮度(感光度=100,K=12.5)和照度(感光度=100,C=250)
EV | 亮度(cd/m^2) | 照度(lx) | w分割行w | EV | 亮度(cd/m^2) | 照度(lx) |
---|---|---|---|---|---|---|
-4 | 0.008 | 0.156 | www | 7 | 16 | 320 |
-3 | 0.016 | 0.313 | www | 8 | 32 | 640 |
-2 | 0.031 | 0.625 | www | 9 | 64 | 1280 |
-1 | 0.063 | 1.25 | www | 10 | 128 | 2560 |
0 | 0.125 | 2.5 | www | 11 | 256 | 5120 |
1 | 0.250 | 5 | www | 12 | 512 | 10240 |
2 | 0.5 | 10 | www | 13 | 1024 | 20480 |
3 | 1 | 20 | www | 14 | 2048 | 40960 |
4 | 2 | 40 | www | 15 | 4096 | 81920 |
5 | 4 | 80 | www | 16 | 8192 | 163840 |
6 | 8 | 160 | www | of! | of! | overflow! |
对于不同的ISO(这次是感光度了)的修正
以上给出的所有数据都是在ISO=100的条件下测得的。
如果你了解过ISO和曝光补偿,那么你应该知道该怎么做了~
对于某一感光度S,应当以这一感光度高出(低于)ISO100的曝光挡数进行增减,用公式表示为:
EV_S = EV_{100} + log_2{S \over 100}.
比如,ISO400比ISO100高出2档:
EV_{400} = EV_{100} + log_2{400 \over 100}
= EV_{100} + 2.
对于更低的感光度,以这一感光度低于ISO100的曝光挡数降低曝光值(增加曝光)。比如ISO50比ISO100低了一挡。
EV_{50} = EV_{100} + log_2{50 \over 100}
= EV_{100} - 1.
使用EV表示的曝光补偿
许多现代照相机都允许设置曝光补偿,并通常用EV这个词来表示。在这种情况下, EV指的是相机测光数据减去实际曝光值的差。 比如+1EV的曝光补偿意味着增加一档曝光,不管是通过延长曝光时间还是更小的f值。
在这里,EV的含义和先前刻度系统中的EV(曝光值)恰好是相反的。增加曝光对应于减少曝光值(-EV),因此+1EV的曝光补偿意味着更小的EV(曝光值);反过来,-1EV的曝光补偿的结果是更大的EV。例如,测得某一比中性灰更亮的物体的EV为16,并设定+1EV的曝光补偿以修正曝光,那么最终的拍摄参数对应于EV15(而不是17)。
再进一步!
我们使用相机M档进行拍摄的操作一般是——
- 指定ISO、光圈、快门
- 看一下曝光补偿的提示,是欠曝了还是过曝了,适当调整拍摄参数
- Shoot!
这个流程应该适用于现在主流的数码相机。。
那么我们也可以根据这个流程来设计测光表的算法:
- 输入3个参数
- 根据传感器获取的光照度和指定的ISO来计算被拍摄场景的EV值,记作EVr(Real)
- 根据3个参数算出EV值,记作EVt(theoretical)
- 两者做差,得到曝光补偿
当然,也可以更直接一些:
- 输入2个参数
- 根据公式直接定出第三个参数,此时的EVr与EVt理论上来说完全相等
- 但是由于参数刻度非线性,不可能完全相等。
或者把这个流程任意调换顺序,反正是4个参数,知3求1.都没问题的~
由此,我们整理公式可以得到:
给定ISO、光圈值,计算快门时间:
t = {N^2 \over S*E} * C
给定光圈值、快门时间,计算ISO:
S = { { N^2 * E } \over t} * {1 \over C}
给定快门时间、ISO,计算光圈F值:
N = \sqrt {S*E*t \over C}
式中,各参数含义如下:
- t-快门时间,单位秒(s)
- N-光圈F值,典型的如f2.8、f4、f5.6……
- S-ISO值,典型的如100、200、320、400、800、1600……
- E-入射式测光传感器得到的照度值,单位lux(勒克斯),由传感器给出
- C-平面型传感器测量照度时的校正常数,默认250,需要根据硬件结构等不定因素适当调整
至此,我们的理论部分就算作结束了~接下来就是激动人心的画板子写代码time了!(orz)
Codeworks
今天我调了半天,写出了初代曝光参数计算算法,数据结构和C没学好(我太菜了),所以并不会做优化。。。在x86平台上使用vscode编写,g++编译调试,测试初步通过。
测试数据
第一步我们需要获取标准的ISO/曝光时间/光圈数据表,很简单,打开相机装上AF镜头,开始一个一个调参数…
uint16_t ISO[24] = {
50, 100, 125, 160, 200, 250, 320, 400,
500, 640, 800, 1000, 1250, 1600, 2000, 2500,
3200, 4000, 5000, 6400, 8000, 10000, 12800,25600
};
double APERTURE[21] = {
1.4, 1.8, 2.0, 2.8, 3.5, 4.0, 4.5, 5.0,
5.6, 6.3, 7.1, 8.0, 9.0, 10.0, 11.0, 13.0,
14.0, 16.0, 18.0, 20.0, 22.0
};
uint16_t SHUTTER[] = {
/* values at the beginning should be used as 1/x */
4000, 3200, 2500, 2000, 1600, 1250, 1000,
800, 640, 500, 400, 320, 250, 200, 160, 125,
100, 80, 60, 50, 40, 30, 25, 20, 15, 13, 10, 8, 6, 5, 4,
/* values below represents : 0.3s, 0.4s, 0.5s, 0.6s, 0.8s, 1s */
3, 4, 5, 6, 8, 1
};
double SHUTTER_F[] = {
0.00025, 0.0003125, 0.0004, 0.0005, 0.000625, 0.0008, 0.001,
0.00125, 0.0015625, 0.002, 0.0025, 0.003125, 0.004, 0.005, 0.00625, 0.008,
0.01, 0.0125, 0.0166667, 0.02, 0.025, 0.0333333, 0.04, 0.05, 0.0666667, 0.0769231, 0.1, 0.125, 0.1666667, 0.2, 0.25,
/* values below represents : 0.3s, 0.4s, 0.5s, 0.6s, 0.8s, 1s */
0.3, 0.4, 0.5, 0.6 ,0.8, 1.0
double lx_test[11] = {0.156, 0.625, 2.5, 10, 40, 160, 640, 2560, 5120, 20480, 81920};
#define SHUTTER_F_SIZE sizeof(SHUTTER_F)/sizeof(SHUTTER_F[0])
#define APERTURE_SIZE sizeof(APERTURE)/sizeof(APERTURE[0])
#define ISO_SIZE sizeof(ISO)/sizeof(ISO[0])
为了简化计算过程,我直接根据分数时间计算出了小数,单独列了一个表。
最后的lx_test是我们后面测试代码的时候要用的。
根据ISO/Aperture/Lux计算时间
我们从这个功能入手,来分析一下这个简单的算法。。
整个算法的核心是我们上一章里推出的公式:
t = {N^2 \over S*E} * C
接下来构造一个函数,留好数据接口就ok啦~
uint16_t calcTime(uint16_t iso, double aperture, float lux)
{
double time;
uint8_t i = 0, index = 0;
double previous_t, next_t;
/* Calculation */
time = (aperture * aperture) * C_CONSTANT / (lux * (double)iso);
/* Skip the value below, and prevent from overflow */
while (time > SHUTTER_F[i++])
if (i >= SHUTTER_F_SIZE-1)
return SHUTTER[SHUTTER_F_SIZE-1];
i -= 1;
/* avoid overflow */
if (i == 0)
return SHUTTER[0];
previous_t = SHUTTER_F[i-1];
next_t = SHUTTER_F[i];
/* Get the Closest Standard value */
index = (fabs(time-previous_t) > fabs(time-next_t)) ? i : i-1;
return SHUTTER[index];
}
注意这里返回的是16位长无符号整数,因为我们最后要显示的是1/x秒,而不是长达5、6位的小数。。。
分析完这个,那么其他的算法对我们来说应该都是小case了。。。除后面有一个需要开根号取对数的操作有点绕,别的都ok
要注意的是,在计算光圈的最近标准值时,index那里的逻辑需要反过来,因为iso和shutter都是从小到大排序建表的, 而光圈数值是这样建的,可物理意义是反过来的
Test!
以下是几个测试结果,从结果初步来看,效果不错~
算ISO
算快门时间
算光圈
算曝光补偿
下一站!
当然是激动人心的实践环节~
请看下集~