Allerdings in gdl. Das ist ein OS Klon von IDL, was wiederum eine mathematisch naturwissenschaftliche Programmiersprache/Auswertesoftware ist. Ist etwa vergleichbar mit Matlab und wie dieses lizenzpflichtig. GDL dagegen ist kostenlos. Ich hab mit IDL beruflich viel zu tun, daher fällt es mir leichter das in dieser Sprache zu machen. Um das Program zum laufen zu bringen braucht man ein Unix/Linux mit installiertem exiftool (ist wohl hier bekannt) und gdl (das ist zumindest bei Fedora in den Repos (vielleicht RPM fusion), glaub inzwischen auch bei Ubuntu.
Das Program geht einfach stumpf die Bilder in der Reihenfolge der Aufname durch und entscheidet dann ob ein Objetivwechsel stattfand. Dabei ist eine Relative Abweichung (momentan 20%) in der Brennweite erlaubt. Ein Wechsel von 50 auf 65 mm wird also nicht gewertet. Ausserdem wird ab einem Abstand zweier Fotos von 3 Stunden ebenfalls kein Objektivwechsel mehr gezählt. Beide Werte lassen sich im 'config' Bereich zu Beginn des Hauptprogramms anpassen.
; Short manual
; 0. Make sure exiftool and gdl are installed
; 1. copy this file somewhere
; 2. Open terminal and chdir to directory containing pictures. All *.jpg and *.JPG files
; will be examined in directory an all subdirs.
; 3. Call gdl shell (> gdl)
; 4. compile this program:
; GDL> .r /path/to/file/exif_stat_export.pro
; 5. run program:
; GDL> exif_stat
; 6. output file exif_stat.txt will be in same folder as
; pictures. Make sure there is no file with this name there, for it
; will be overwritten.
;===================================================================================================
;===================================================================================================
; Subroutines
function int_test, string, flag_float = flag_float, flag_nochar=flag_nochar, flag_nolchar=flag_nolchar, flag_nosign=flag_nosign, last_pos=last_pos
compile_opt idl2
p_name = 'int_test'
if ( ~keyword_set(flag_float) ) then flag_float = 0
if ( ~keyword_set(flag_nochar) ) then flag_nochar = 0
if ( ~keyword_set(flag_nolchar) ) then flag_nolchar = 0
if ( ~keyword_set(flag_nosign) ) then flag_nosign = 0
if ( keyword_set(last_pos) ) then flag_last_pos = 1 else flag_last_pos = 0
; prueft ob string in int (von idl) konvertierbar ist
; erlaubt sind: '9', ' 9 ', ' 9 hammel ', ' 9hammel ', ' -8', '+2'
; nicht erlaubt sind: 'hammel 9',' - 9'
; Problem: bei flag_float werden auch mehrere Punkte akzeptiert, wenn
; einmal ein Punkt vorkam sollte beim zweiten aber abgebrochen werden!
; der rueckgabewert ist 1, wenn es konvertierbar ist, sonst null
; optional:
; in
; flag_float :: integer, der anzeigt, ob nicht auf die konvertierbasrkeit in int, sondern
; in float geprueft werden soll
; flag_nochar :: no symbols other than '-+0123456789' are allowed (-+
; only at beginning)
; flag_nolchar :: no leading characters allowed
; flag_nosign :: no leading +- allowed
; out
; last_pos :: returns last valid pos of string which can be
; converted to integer. -1, if string cannot be converted.
; Example: string='1233 hallo' >> last_pos==3
str_i = string
last_pos = -1
if ( (flag_nolchar eq 1) and (flag_nochar eq 1) ) then begin
print,'*** Error in '+p_name+'! ***'
print,'Contradicting options flag_nochar and flag_nolchar both set.'
return, -1
endif
if (flag_nolchar eq 1) then begin
i_beg = 0
if (flag_nosign eq 0) then begin
a = strmid(str_i,i_beg,1)
if ( (a eq '-') or (a eq '+') ) then i_beg = i_beg + 1
endif
a = strmid(str_i,i_beg,1)
if ( (a eq '0') or (a eq '1') or (a eq '2') or (a eq '3') or (a eq '4') or (a eq '5') or $
(a eq '6') or (a eq '7') or (a eq '8') or (a eq '9') ) then begin
erg = 1
endif else if ( (flag_float eq 1) and (a eq '.')) then begin
erg = 1
endif else begin
erg = 0
endelse
endif else if (flag_nochar eq 1) then begin
i_beg = 0
if (flag_nosign eq 0) then begin
a = strmid(str_i,i_beg,1)
if ( (a eq '-') or (a eq '+') ) then i_beg = i_beg + 1
endif
for ii=i_beg,strlen(str_i)-1 do begin
a = strmid(str_i,ii,1)
if ( ~( (a eq '0') or (a eq '1') or (a eq '2') or (a eq '3') or (a eq '4') or $
(a eq '5') or (a eq '6') or (a eq '7') or (a eq '8') or (a eq '9') ) $
) then begin
if ( ~( (flag_float eq 1) and (a eq '.') ) ) then begin
erg = 0
return, erg
endif
endif
endfor
erg = 1
endif else begin
i_beg = strlen(str_i) - strlen( strtrim(str_i,1))
;str_i = strtrim(str_i,2)
if (flag_nosign eq 0) then begin
a = strmid(str_i,i_beg,1)
if ( (a eq '-') or (a eq '+') ) then i_beg = i_beg + 1
endif
a = strmid(str_i,i_beg,1)
if ( (a eq '0') or (a eq '1') or (a eq '2') or (a eq '3') or (a eq '4') or (a eq '5') or $
(a eq '6') or (a eq '7') or (a eq '8') or (a eq '9') ) then begin
erg = 1
endif else if ( (flag_float eq 1) and (a eq '.')) then begin
erg = 1
endif else begin
erg = 0
endelse
endelse
if ( (erg eq 1) and (flag_last_pos eq 1) ) then begin
last_pos = strlen(strtrim(str_i))-1 ; always, because flag_nochar
if (flag_nochar eq 0) then begin
for ii=i_beg+1,strlen(strtrim(str_i))-1 do begin
a = strmid(str_i,ii,1)
if ( ~( (a eq '0') or (a eq '1') or (a eq '2') or (a eq '3') or (a eq '4') or $
(a eq '5') or (a eq '6') or (a eq '7') or (a eq '8') or (a eq '9') ) $
) then begin
if ( ~( (flag_float eq 1) and (a eq '.') ) ) then begin
last_pos = ii-1
break
endif
endif
endfor
endif
endif
return, erg
end
;===================================================================================================
pro any_time2jul, timestr, template, jul, erg=erg
compile_opt idl2
p_name = 'any_time2jul'
p_vers = '1.0.0'
;converts any time string to jul
; version history
; 1.0.0 14 Aug 2013 first unnamed version
; 1.2.0 15 May 2014 include mmm: for abr of Month TODO
; template of form yyyy-mm-dd hh-mm-ss
; Restrictions:
; - the following substrings must be present in template exactly once:
; yyyy, mm(2x), dd, hh, ss
; - there must be a 'mm' between ss and hh.
; - There can only be one 'mm' between ss and hh. This is assumed to be the minutes,
; the other mm is for the month
; arguments
; in
; timestr string :: string with times to be converted, can be an array
; template string :: template for timestr, restrictions see above
; out
; jul long :: converted times
; erg long :: return status, 0 ok, 1 error
; config
check_lev = 1 ; 0: no check, 1: first array element is being checked, 2 all is checked
;month_abbr = ['Jan']
erg = 1
;----------------------------------------------------------------------------
; find index of substrings in template
key='yyyy'
y_pos=strpos(template, key)
if (y_pos eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
nix=strpos(template, key,y_pos+1)
if (nix ne -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' twice in template'
return
endif
key='dd'
d_pos=strpos(template, key)
if (d_pos eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
nix=strpos(template, key,d_pos+1)
if (nix ne -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' twice in template'
return
endif
key='hh'
h_pos=strpos(template, key)
if (h_pos eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
nix=strpos(template, key,h_pos+1)
if (nix ne -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' twice in template'
return
endif
key='ss'
s_pos=strpos(template, key)
if (s_pos eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
nix=strpos(template, key,s_pos+1)
if (nix ne -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' twice in template'
return
endif
key='mm'
m_pos1=strpos(template, key)
if (m_pos1 eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
m_pos2=strpos(template, key, m_pos1+1)
if (m_pos2 eq -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' missing in template'
return
endif
nix=strpos(template, key,m_pos2+1)
if (nix ne -1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Substring '+key+' three times in template'
return
endif
if ( (m_pos1 gt h_pos) and (m_pos1 lt s_pos) ) then begin
if ( (m_pos2 gt h_pos) and (m_pos2 lt s_pos) ) then begin
print,'*** Error in '+p_name+'! ***'
print,'String "mm" must be between "ss" and "hh" only once.'
return
endif else begin
min_pos = m_pos1
mon_pos = m_pos2
endelse
endif else if ( (m_pos2 gt h_pos) and (m_pos2 lt s_pos) ) then begin
min_pos = m_pos2
mon_pos = m_pos1
endif else begin
print,'*** Error in '+p_name+'! ***'
print,'String "mm" for minutes must be between "ss" and "hh".'
return
endelse
;----------------------------------------------------------------------------
; convert to integers
nn=n_elements(timestr)
case check_lev of
0: n_check = 0
1: n_check = 1
2: n_check = nn
endcase
for ii=0,n_check-1 do begin
tempstr=strmid(timestr[ii],y_pos,4)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
tempstr=strmid(timestr[ii],mon_pos,2)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
tempstr=strmid(timestr[ii],d_pos,2)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
tempstr=strmid(timestr[ii],h_pos,2)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
tempstr=strmid(timestr[ii],min_pos,2)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
tempstr=strmid(timestr[ii],s_pos,2)
if (int_test(tempstr) ne 1) then begin
print,'*** Error in '+p_name+'! ***'
print,'Integer conversion of '+tempstr+' at '+timestr[ii]+' failed.'
return
endif
endfor
year=long(strmid(timestr,y_pos,4))
month=long(strmid(timestr,mon_pos,2))
day=long(strmid(timestr,d_pos,2))
hour=long(strmid(timestr,h_pos,2))
min=long(strmid(timestr,min_pos,2))
sec=long(strmid(timestr,s_pos,2))
jul = julday( month, day, year, hour, min, sec)
erg = 0
end
;===================================================================================================
;===================================================================================================
; Main program
pro exif_stat
; reads exifs and calculates statisics
p_name='exif_stat'
p_vers='1.0.0'
;=========================
; config
delta_t_max = 3. ; hours, time between two pics, for which a lens change is counted
delta_f_max = .2 ; relative difference between two f values, which will still be counted as same
log=1 ; 0,1,2 log grade (higher means more output)
;=========================
delta_t_max_d = delta_t_max / 24.
spawn,'find . -iname "*.jpg"', files
if (files[0] eq '') then begin
print,'*** Error in '+p_name+'_'+p_vers+'! ***'
print,'No files found. Aborting.'
return
endif
n_pic=n_elements(files)
ff=fltarr(n_pic)-1
tt=dblarr(n_pic)-1
for ii=0,n_pic-1 do begin
if (log gt 0) then print,files[ii],' '+strtrim(string(ii+1),2)+' / '+strtrim(string(n_pic),2)
spawn,'exiftool -FocalLength '+files[ii],str
if (str eq '') then begin
print,'*** Warning in '+p_name+'_'+p_vers+'! ***'
print,'Exif could not be read in '+files[ii]
continue
endif
temp=strsplit(str,/extract)
ff[ii]=float(temp[3])
spawn,'exiftool -DateTimeOriginal '+files[ii],str
if (str eq '') then begin
print,'*** Warning in '+p_name+'_'+p_vers+'! ***'
print,'Exif datecould not be read in '+files[ii]
continue
endif
temp=strsplit(str,/extract) ; 4/5
any_time2jul, temp[3]+' '+temp[4], 'yyyy:mm:dd hh:mm:ss', jul, erg=status
if (status ne 0) then begin
print,'*** Error in '+p_name+'_'+p_vers+'! ***'
print,'Could not convert str to jultime of '+files[ii]
print, temp[3]+' '+temp[4]
return
endif
tt[ii]=jul
endfor
valdex = where(ff ne -1, count)
if (count eq 0) then begin
print,'*** Error in '+p_name+'_'+p_vers+'! ***'
print,'Exif could not be read in any file. Aborting.'
return
endif
files=files[valdex]
ff=ff[valdex]
tt=tt[valdex]
n_all=count
; get f histogram
sortdex=sort(ff)
udex=uniq(ff[sortdex])
n_uni=n_elements(udex)
histo_f=ff[sortdex[udex]]
histo_n=lonarr(n_uni)
for ii=0,n_uni-1 do begin
nix=where(ff[sortdex] eq ff[sortdex[udex[ii]]], count)
histo_n[ii] = count
;print, histo_f[ii], histo_n[ii]
endfor
; get amount of lens changes
sortdex=sort(tt)
n_change=0
total_sum_t=0.
n_in_mean=1
f_ref=ff[sortdex[0]]
t_beg=tt[sortdex[0]]
for ii=1,n_all-1 do begin
if ( tt[sortdex[ii]] - tt[sortdex[ii-1]] lt delta_t_max_d ) then begin
if ( abs(ff[sortdex[ii]] - f_ref)/f_ref lt delta_f_max) then begin
; no change
if (log gt 1) then print,'No lens change at '+files[sortdex[ii]]+' from '+strtrim(string(f_ref),2)+' to '+$
strtrim(string(ff[sortdex[ii]]),2)
f_ref = ( f_ref * n_in_mean + ff[sortdex[ii]] ) / (n_in_mean + 1)
n_in_mean = n_in_mean + 1
endif else begin
; lens change
if (log gt 0) then print,'Lens change at '+files[sortdex[ii]]+' from '+strtrim(string(f_ref),2)+' to '+$
strtrim(string(ff[sortdex[ii]]),2)
n_change=n_change+1
f_ref = ff[sortdex[ii]]
n_in_mean = 1
endelse
endif else begin
; out of time range, start new patch
if (log gt 1) then print,'New time range at '+files[sortdex[ii]]
total_sum_t = total_sum_t + tt[sortdex[ii-1]] - t_beg
t_beg=tt[sortdex[ii]]
f_ref = ff[sortdex[ii]]
n_in_mean = 1
endelse
endfor
total_sum_t = total_sum_t + tt[sortdex[n_all-1]] - t_beg
total_sum_t = total_sum_t * 24.
; write to output
file=p_name+'.txt'
openw, iou, file, /get_lun
printf,iou,'# Exif histogram, created by '+p_name+' '+p_vers
printf,iou,'# n pictures: ', n_all
printf,iou,'# n lens changes: ', n_change
printf,iou,'# total hours: ', total_sum_t
printf,iou,'# changes / h: ', n_change/total_sum_t
printf,iou,'# changes / pic: ', float(n_change)/n_all
printf,iou,'#',n_uni
for ii=0,n_uni-1 do printf,iou,histo_f[ii], histo_n[ii]
free_lun,iou
end
Alles anzeigen
Man sollte wirklich noch betonen, dass nur Objektivwechsel aufgrund der Brennweite berücksichtigt sind, also alle anderen Gründe (zB Naheinstellgrenze/Makro, grössere Offenblende) für einen Objektivwechsel fehlen.
Bei mir kamen übrigens ca 4,2 Objektivwechsel pro Stunde und 0,4 pro Bild heraus. Das bestätigt mich aber nur in meiner Wertschätzung für mein 18-105.