# Difference between revisions of "Photography exposure"

## Photography exposure

author Photography exposure Leif Roschier

## Theory and background

This example illustrates how exposure in photography depends on factors: latitude, time of day, day of year, weather, composition. It relates these to camera settings: film speed (e.g. ISO 100), aperture and shutter speed. The mathematical approach and model is taken from book written by V. Setälä.[1] This book illustrates the approach as nomographs but they are different as the one generetated here.

The basic equation in Setälä[1](pp.492-494) can be extracted and written as

$FS-L-A-W+C-T=0 \,$,

where

FS Film speed DIN value that equals $10 \log (S) +1 \,$,where S is ISO FILM speed
T shutter time $10 \log \left( \frac{t}{10}\right)$
A aperture $10 \log \left(\frac{N^2}{3.2^2}\right)$
L shadow length (in steps) two times (shadow length)/(person length) $= 2 \arctan ( \phi) \,$, where $\phi \,$ is solar zenith angle.
W weather
 Clear sky, Cumulus clouds 0 Clear sky 1 Sun through clouds 3 Sky light gray 6 Sky dark gray 9 Thunder-clouds cover sky 12
C composition
 Person under trees -6 Inside forest -4 Person in shadow of wall -1 Person at open place; alley under trees 2 Buildings; street 5 Landscape and front matter 7 Open landscape 9 Snow landscape and front matter; beach 11 Snow field; open sea 13 Clouds 15

It is to be noted that Setälä has stops ten times base-10 logarithmic. Today we think stops in base-2 logarithmic.

Calculation of shadow length as a function of day of year, time of day and latitude is according to [2]. Following equations are used: For fractional year (without time information)

$\gamma = (day-1+0.5)2\pi/365.0 \,$

for time offset (eqtime) we use equation (in minutes)

$TO = 229.18(0.000075+0.001868\cos(\gamma)-0.032077\sin(´\gamma)-0.014615\cos(2\gamma)-0.040849\sin(2\gamma)) \,$

to calculate that error is below 17 minutes for time axis. We assume that sun is at heightest point at noon and this is the error and approximation. We calculate stops in logarithmic scale and in this case we do not need very accurate equations for time. For declination we use equation

$D=0.006918-0.399912\cos(\gamma)+0.070257\sin(\gamma)-0.006758\cos(2\gamma)+0.000907\sin(2\gamma)-0.002697\cos(3\gamma)+0.00148\sin(3\gamma).\,$

Hour angle is

$ha=(60h+\overline{TO})/4-180 \,$.

Finally solar zenith angle ($\phi \,$), latitude (LAT), declination (D) and hour angle (ha) are connected with equation:

$\cos (\phi ) = \sin(LAT)sin(D)+\cos(LAT)\cos(D)\cos(ha) \,$

This is in our desired for as function of hour (h), day (day), latitude (LAT), solar zenith angle ($\phi \,$):

$\cos (\phi ) = \sin(LAT)sin(D(\gamma(day)))+\cos(LAT)\cos(D(\gamma(day)))\cos(ha(h)) \,$.

In practice illuminance of flat surface on earth depends on solar zenith angle as $\cos(\phi)\,$. Setälä[1] uses shadow length that is easily measurable, but scales incorrectly, as value is propotional to $\tan(\phi)$. Also Setälä[1] uses linear value as logarithmic as practical approximation. To correct this assumption, I assume that values 1 and 10 for Setälä are close to correct and find equation that scales correctly. It turns out, that equation

$L = 0.33766 - 13.656 \log10 (\cos(\phi)) \,$

gives

$L=1\,$

for

$\phi = 26.565 =\arctan(1/2)\,$

and

$L=10\,$

for

$\phi = 78.69 =\arctan(10/2).\,$

#### References

1. Vilho Setälä: "Valokuvaus", Otava 1940.
2. http://www.srrb.noaa.gov/highlights/sunrise/solareqns.PDF

## Generated nomograph

Photography exposure
Generated portable document file (pdf): File:Ex photo exposure.pdf

## Source code

"""
ex_photo_exposure.py

Photgraph exposure.

This program is free software: you can redistribute it and/or modify
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
from nomographer import *
"""
functions for solartime taken from solareqns.pdf from
http://www.srrb.noaa.gov/highlights/sunrise/solareqns.PDF
"""
# fractional year
def gamma(day):
return 2*pi/365.0*(day-1+0.5)
# equation of time
def eq_time(day):
gamma0=gamma(day)
return 229.18*(0.000075+0.001868*cos(gamma0)-0.032077*sin(gamma0)\
-0.014615*cos(2*gamma0)-0.040849*sin(2*gamma0))

# mean correction, with constant correction we make less than 1.5 minutes error
# in time axis
temp_a=arange(0,2*pi,0.001)
temp_b=eq_time(temp_a)
correction=mean(temp_b) # this is about four minutes

# declination
def eq_declination(day):
g0=gamma(day)
return 0.006918-0.399912*cos(g0)+0.070257*sin(g0)-0.006758*cos(2*g0)\
+0.000907*sin(2*g0)-0.002697*cos(3*g0)+0.00148*sin(3*g0)

def f1(dummy):
return 0.0
def g1(fii):
return cos(fii*pi/180.0)

def f2(lat,day):
dec=eq_declination(day)
return (cos(lat*pi/180.0)*cos(dec))/(1.0+(cos(lat*pi/180.0)*cos(dec)))
def g2(lat,day):
return (sin(lat*pi/180.0)*sin(dec))/(1.0+(cos(lat*pi/180.0)*cos(dec)))

def f3(dummy):
return 1
def g3(h):
hr=(h*60.0+correction)/4.0-180.0
return -1.0*cos(hr*pi/180.0)

days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)
times1=[]
for idx in range(0,12):
times1.append(sum(days_in_month[0:idx])+1)

#times=linspace(0,350,10)
times=arange(0.0,360.0,10.0,dtype=double).tolist()
time_titles=['January','February','March','April','May','June',
'July','August','September','October','November','December']

phi_params={
'u_min':0.0,
'u_max':90.0,
'u_min_trafo':0.0,
'u_max_trafo':90.0,
'f':f1,
'g':g1,
'h':lambda u:1.0,
'title':r'Solar zenith angle $\phi$',
'title_x_shift':0.0,
'title_y_shift':0.25,
'scale_type':'linear',
'tick_levels':2,
'tick_text_levels':1,
'tick_side':'right',
'tag':'phi',
'grid':False,
'extra_params':[{'u_min':20.0,
'u_max':90.0,
'tick_levels':4,
'tick_text_levels':2,
}]
}

time_params={
'u_min':0.0,
'u_max':23.0,
'u_min_trafo':0.0,
'u_max_trafo':12.0,
'f':f3,
'g':g3,
'h':lambda u:1.0,
'title':r'Hour (h)',
'title_x_shift':0.0,
'title_y_shift':0.25,
'scale_type':'linear',
'tick_levels':2,
'tick_text_levels':1,
'tick_side':'right',
'tag':'none',
'grid':False,
}

lat_day_params={
'ID':'none', # to identify the axis
'tag':'none', # for aligning block wrt others
'title':'Grid',
'title_x_shift':0.0,
'title_y_shift':0.25,
'title_distance_center':0.5,
'title_opposite_tick':True,
'u_min':20.0, # for alignment
'u_max':80.0,  # for alignment
'f_grid':f2,
'g_grid':g2,
'h_grid':lambda u,v:1.0,
'u_start':30.0,
'u_stop':80.0,
'v_start':times1[0], # day
'v_stop':times1[-1],
'u_values':[30.0,40.0,50.0,60.0,70.0,80.0],
'u_texts':['30','40','50','Latitude = 60','70','80'],
'v_values':times1,
'v_texts':time_titles,
'grid':True,
'text_prefix_u':r'',
'text_prefix_v':r'',
'text_distance':0.5,
'v_texts_u_start':False,
'v_texts_u_stop':True,
'u_texts_v_start':False,
'u_texts_v_stop':True,
}

block_params={
'block_type':'type_9',
'f1_params':phi_params,
'f2_params':lat_day_params,
'f3_params':time_params,
'transform_ini':True,
}

def limit_xx(x):
x1=x
if x1>1.0:
x1=1.0
if x1<-1.0:
x1=-1.0
return x1

def limit_x(x):
x1=x
if not x1>0.0:
x1=0.0001
return x1

const_A=0.33766
const_B=-13.656

block_params_weather={
'block_type':'type_5',
'u_func':lambda u:u,
'v_func':lambda x,v:const_A+const_B*log10(limit_x(x))+v,
#'u_values':[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,15.0,20,25],
'u_values':[1.0,25.0],
'u_manual_axis_data':{1.0:'',
25.0:''},
'v_values':[0.0,1.0,3.0,6.0,9.0,12.0],
'v_manual_axis_data': {0.0:['Clear sky, Cumulus clouds',{'x_corr':0.5,
'y_corr':0.0,
'draw_line':False}],
1.0:'Clear sky',
3.0:'Sun through clouds',
6.0:'Sky light gray',
9.0:'Sky dark gray',
12.0:'Thunder-clouds cover sky',
},
'v_text_distance':0.5,
'wd_tick_levels':0,
'wd_tick_text_levels':0,
'wd_tick_side':'right',
'wd_title':'',
'manual_x_scale':True,
'x_min':0.06,
'x_max':0.99,
'u_title':'',
'v_title':'',
'wd_title_opposite_tick':True,
'wd_title_distance_center':2.5,
'wd_align_func':lambda L:acos(limit_xx(10.0**((L-const_A)/const_B)))*180.0/pi, # phi as L
'wd_func':lambda L:10.0**((L-const_A)/const_B), # x as L
'wd_func_inv':lambda x:const_A+const_B*log10(x), # L as x
'wd_tag':'phi',
'mirror_y':True,
'mirror_x':False,
'width':10.0,
'height':10.0,
'u_scale_opposite':True,
'u_tag':'AA',
'horizontal_guides':True,
}

block_params_scene={
'block_type':'type_5',
'u_func':lambda u:u,
'v_func':lambda x,v:x+v,
'u_values':[1.0,25.0],
'u_manual_axis_data':{1.0:'',
25.0:''},
'u_tag':'AA',
'wd_tag':'EV',
'v_values':[-4.0,-1.0,2.0,5.0,7.0,9.0,11.0,13.0,15.0],
'v_manual_axis_data': {-6.0:'Person under trees',
-4.0:'Inside forest',
2.0:'Person at open place; alley under trees',
5.0:'Buildings; street',
7.0:'Landscape and front matter',
9.0:'Open landscape',
11.0:'Snow landscape and front matter; beach',
13.0:'Snow field; open sea',
15.0:'Clouds',
},
'wd_tick_levels':0,
'wd_tick_text_levels':0,
'wd_tick_side':'right',
'wd_title':'',
'u_title':'',
'v_title':'',
'wd_title_opposite_tick':True,
'wd_title_distance_center':2.5,
'mirror_x':True,
'horizontal_guides':True,
'u_align_y_offset':-0.9,
}

camera_params_1={
'u_min':-10.0,
'u_max':15.0,
'function':lambda u:u,
'title':r'',
'tick_levels':0,
'tick_text_levels':0,
'tag':'EV',
}
camera_params_2={
'u_min':10.0,
'u_max':12800.0,
'function':lambda S:-(10*log10(S)+1.0),
'title':r'Film speed',
'manual_axis_data': {10.0:'ISO 10',
20.0:'ISO 20',
#40.0:'ISO 40',
50.0:'ISO 50',
100.0:'ISO 100',
200.0:'ISO 200',
400.0:'ISO 400',
800.0:'ISO 800',
1600.0:'ISO 1600',
3200.0:'ISO 3200',
6400.0:'ISO 6400',
12800.0:'ISO 12800',
},
'scale_type':'manual line'
}
camera_params_3={
'u_min':0.1,
'u_max':10000.0,
'function':lambda t:-10*log10((1.0/t)/(1.0/10.0))-30,
'manual_axis_data': {1/10.0:'10',
1/7.0:'7',
1/5.0:'5',
1/3.0:'3',
1/2.0:'2',
1.0:'1',
2.0:'1/2',
3.0:'1/3',
5.0:'1/5',
7.0:'1/7',
10.0:'1/10',
20.0:'1/20',
30.0:'1/30',
50.0:'1/50',
70.0:'1/70',
100.0:'1/100',
200.0:'1/200',
300.0:'1/300',
500.0:'1/500',
700.0:'1/700',
1000.0:'1/1000',
2000.0:'1/2000',
3000.0:'1/3000',
5000.0:'1/5000',
7000.0:'1/7000',
10000.0:'1/10000',
},
'scale_type':'manual line',
'title':r't (s)',
'text_format':r"1/%3.0f s"
}
#          6.7     8     9.5     11     13     16     19     22
camera_params_4={
'u_min':1.0,
'u_max':22.0,
'function':lambda N:10*log10((N/3.2)**2)+30,
'manual_axis_data': {1.0:'f/1',
1.2:'f/1.2',
1.4:'f/1.4',
1.7:'f/1.7',
2.0:'f/2',
2.4:'f/2.4',
2.8:'f/2.8',
3.3:'f/3.3',
4.0:'f/4',
4.8:'f/4.8',
5.6:'f/5.6',
6.7:'f/6.7',
8.0:'f/8',
9.5:'f/9.5',
11.0:'f/11',
13.0:'f/13',
16.0:'f/16',
19.0:'f/19',
22.0:'f/22',
},
'scale_type':'manual line',
'title':r'Aperture',
}

block_params_camera={
'block_type':'type_3',
'width':10.0,
'height':10.0,
'f_params':[camera_params_1,camera_params_2,camera_params_3,
camera_params_4],
'mirror_x':True,
}

def old_EV(EV):
return (-EV+13.654)/0.3322

EV_para={
'tag':'EV',
'u_min':4.0,
'u_max':19.0,
'function':lambda u:old_EV(u),
'title':r'EV$_{100}$',
'tick_levels':1,
'tick_text_levels':1,
'align_func':old_EV,
'title_x_shift':0.5,
'tick_side':'right',
}
EV_block={
'block_type':'type_8',
'f_params':EV_para
}

main_params={
'filename':'ex_photo_exposure.pdf',
'paper_height':35.0,
'paper_width':35.0,
'block_params':[block_params,block_params_weather,block_params_scene,block_params_camera,EV_block],
#'block_params':[block_params_weather,block_params_scene,block_params_camera,EV_block],
'transformations':[('rotate',0.01),('scale paper',)],
'title_x': 7,
'title_y': 34,
'title_box_width': 10,
'title_str':r'Photography exposure (Setala 1940) \copyright Leif Roschier  2008 '
}
b=Nomographer(main_params)