For the ones interested in the gory details, that's how it works:
It's based on the previous script, but after shooting each frame it builds a (rough) histogram of the RAW data.
The histogram is made of 1024 short ints, each ranging from 0 to 1023.
The uBasic script checks this data and tries to determine if the shot is over-exposed.
A satisfactory way to do this (in frames mainly composed of sky) is to check that at most 10% of the pixels are in the luminance range 100-800.
The logic of this is that if there are artificial lights, with these long exposures they will result in over-exposed pixels (above 800). We will ignore them, as it's normal for them to be over-exposed.
for the rest of the scene, we want a rather dark sky, so at most 10% of the pixels will be allowed to be over luminance 100.
If there are more, the script determines that the frame was over-exposed and decreases the exposure for next shot.
The results are smoothed with a sort of FIFO buffer (with a rudimendary weighting system)
This allows for a certain number of over-exposed pixels (in case arti
THIS SCRIPT REQUIRES A CUSTOM CHDK BUILD (see below)
EVERYTHING IS STILL VERY EXPERIMENTAL, BUGGY AND INCOMPLETE
If interested in testing this script, please give me your camera model and I will compile the build for you.
Otherwise, see below for a summary of changes made to the core.
@title Sunset10
rem Sunset and dawn time-lapses
rem v. 10, Fbonomi may 20th 2008
rem Released under GPL
rem incorporates feedback from previous shot: if more than 10% of pixels
rem are in the 100-800 range, decrease exposure
@param a Delay (sec)
@default a 5
@param b Limit Tv
@default b -414
rem (-414=20 sec; -384=15 sec; -320=10 sec; -224=5 sec.)
@param c Default Sv
@default c 480
rem (480=160)
@param d Limit Sv
@default d 776
rem (776=1250 ISO; 960=5000)
@param e Guess mode limit
@default e -200
@param f Slope in guess mode
@default f 5
print_screen 1
print "Sunset Time Lapse"
rem initialize guess mode value
x=e
rem get start Tv
get_tv96 N
rem picture counter
p=1
rem exposure factor of previous shot
h=0
rem mark "FIFO BUFFER EMPTY"
Z=-1
:loop
rem measure luminance and aperture
press "shoot_half"
sleep 500
release "shoot_half"
get_bv96 B
get_av96 A
rem release "shoot_half"
print "Measured: ",B ,A
rem resulting Tv would be:
N=B-A
N=N+c
print "Calculated T: ", N
rem check Tv Values
if N>e then
rem normal mode, shoot with calculated Tv and default Sc
M=N
L=c
print "Mode: Standard"
else
rem Guess mode, every shot will be 5/96th steps longer
x=x-f
rem but if last exposure was over-exposed, don't decrease,
rem and instead increase
if h>10 then
print "Compensating ", h
x=x+f+f
endif
rem x has now the desired exposure, we will now attempt
rem to smooth its value (mobile average over 10 shots)
rem the last 10 values of x are stored in a FIFO buffer
rem made by variables Q to Z
rem The first time we are here, the FIFO buffer will be empty
rem let's initialize it
if Z=-1 then
Q=x
R=x
S=x
T=x
U=x
V=x
W=x
X=x
Y=x
Z=x
endif
rem feed the buffer
Z=Y
Y=X
X=W
W=V
V=U
U=T
T=S
S=R
R=Q
Q=x
rem calculate the average of values stored in FIFO buffer
P= Q+Q+Q+Q+Q
P=P +R+R+R+R
P=P +S+S+S
P=P +T+T
P=P +U
P=P/15
print "Smoothed ",x, " ", P
rem P now contains a smoothed value of what was in x
rem avoid overcompensating (guess mode climbing over guess mode limit)
if P>e then
P=e
endif
if P>b then
print "Mode: Guess ", P ,b
rem we are in dark, but exposure is not too long
rem normal mode, shoot with guessed Tv and default Sc
M=P
L=c
else
print "Mode: Guess with High ISO", P ,b
rem we are in very dark area, we want to avoid too long exposures
rem shoot with maximum allowed time (b) and
rem adjust Sv accordingly
M=b
L=c+b-P
rem BUT check we don't go in too high ISO!
if L>d then
L=d
endif
endif
endif
rem we can now shoot
sleep 100
set_sv96 L
sleep 100
set_tv96_direct M
sleep 100
shoot
sleep 100
rem let's read histogram of the shot that was just taken
exp_get_info 100 800 h
rem h contains the percentage of pixels that are between 100/1024 and 800/1024
print "SHOOT ",p,M,L,h
p=p+1
sleep a*1000
goto "loop"
Summary of changes made to the C code:
SHOT_HISTOGRAM.C
New file:
#include "platform.h"
#include "conf.h"
#include "shot_histogram.h"
#include "camera.h"
#include "raw.h"
#include "stdlib.h"
#define RAW_TARGET_DIRECTORY "A/DCIM/%03dCANON"
#define RAW_TARGET_FILENAME "%s_%04d.%s"
unsigned short shot_histogram[1024];
unsigned short shot_margin_left=0, shot_margin_top=0, shot_margin_right=0, shot_margin_bottom=0;
void build_shot_histogram()
{
// read samples fromRAW memory and build an histogram of its luminosity
// actually, it' just reading pixels, ignoring difference between R, G and B,
// we just need an estimate of luminance
// SHOT_HISTOGRAM_MARGIN defines a margin around the sensor that will be ignored
// (dead area)
int x, y, x0, x1, y0, y1;
int marginstep;
short p;
for (x = 0; x < 1024; x ++ )
{
shot_histogram[x]=0;
}
marginstep= (CAM_RAW_ROWPIX - 2 * SHOT_HISTOGRAM_MARGIN)/10;
// In future, support definition of a sort of "spot metering"
x0 = SHOT_HISTOGRAM_MARGIN + shot_margin_left * marginstep;
x1 = CAM_RAW_ROWPIX - SHOT_HISTOGRAM_MARGIN - shot_margin_right * marginstep;
y0 = SHOT_HISTOGRAM_MARGIN + shot_margin_top * marginstep;
y1 = CAM_RAW_ROWS - SHOT_HISTOGRAM_MARGIN - shot_margin_bottom * marginstep;
//x0 = SHOT_HISTOGRAM_MARGIN ;
//x1 = CAM_RAW_ROWPIX - SHOT_HISTOGRAM_MARGIN ;
//y0 = SHOT_HISTOGRAM_MARGIN;
//y1 = CAM_RAW_ROWS - SHOT_HISTOGRAM_MARGIN;
// just read one pixel out of SHOT_HISTOGRAM_STEP, one line out of SHOT_HISTOGRAM_STEP
for (y = y0 ; y < y1; y +=SHOT_HISTOGRAM_STEP )
for (x = x0 ; x < x1; x +=SHOT_HISTOGRAM_STEP )
{
p=get_raw_pixel(x,y);
shot_histogram[p]++;
}
// dump to file (just for debugging / logging purposes)
// for each shoot, creates a HSTnnnnn.DAT file containing 2*1024 bytes
char fn[64];
char dir[32];
sprintf(dir, RAW_TARGET_DIRECTORY, (conf.raw_in_dir)?get_target_dir_num():100);
sprintf(fn, "%s/", dir);
sprintf(fn+strlen(fn), RAW_TARGET_FILENAME, "HST", get_target_file_num(), "DAT");
char buf[64];
int fd = open(fn, O_WRONLY|O_CREAT, 0777);
if (fd>=0)
{
write(fd, shot_histogram, 2048);
close(fd);
}
}
int shot_histogram_get_range(int histo_from, int histo_to)
// Examines the histogram, and returns the percentage of pixels that
// have luminance between histo_from and histo_to
{
int x, tot, rng;
tot=0;
rng=0;
for (x = 0 ; x < 1024; x ++ )
{
tot += shot_histogram[x];
if (x>=histo_from && x <= histo_to)
{
rng += shot_histogram[x];
}
}
return (rng*100)/tot;
}
RAW.C
Line 4:
#include "shot_histogram.h"
Line 28:
// just after shooting, build shot histogram
build_shot_histogram();
UBASIC.C
Line 48:
#include "shot_histogram.h"
Line 1552:
// Interface function for uBasic
static void exp_get_info()
{
int var;
int histo_from;
int histo_to;
accept(TOKENIZER_EXP_GET_INFO);
histo_from=expr();
histo_to=expr();
var = tokenizer_variable_num();
accept(TOKENIZER_VARIABLE);
ubasic_set_variable(var, (unsigned short)shot_histogram_get_range(histo_from, histo_to));
accept_cr();
}
Line:1831
case TOKENIZER_EXP_GET_INFO:
exp_get_info();
break;
TOKENIZER.C
Line 175
{"exp_get_info", TOKENIZER_EXP_GET_INFO},