Log in

View Full Version : GamMac v1.10 - 15 June 2018


Pages : [1] 2 3 4 5 6 7

StainlessS
16th July 2016, 11:16
GamMac, Gamma Machine. Original idea, see here:- http://forum.doom9.org/showthread.php?t=173683


GamMac(), [Gamma Machine] An extraordinary Idea by VideoFred (the gent from Gent). Coded by StainlessS.
Requires CPP runtimes from VS 2008.

Home Thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281
Idea:- http://forum.doom9.org/showthread.php?t=173683

RGB Only.

Useful to correct color cast on old 8mm films.
Alters channel pixel average to match LockChan using Gamma correction. (By default alters Red and Blue channels to match Green).
Additional tweaking via RedMul, GrnMul and BluMul multipliers.

What it does(roughly):-
Firstly, RAW input channel Ranges are measured for all three channels (see RngLim).
If ALL THREE raw input ranges are less than RngLim (single color frame), then for current frame,
there is no scaling nor gamma estimation, and only linear rendering is done to output range omin -> omax.
[Channels multipliers RedMul, GrnMul and BluMul NOT applied either.]
OtherWise,
If ANY ONE channel input range is less than RngLim and Scale==2, then Scale is (for current frame) knocked down to Scale=1.
Get Channel averages, minimums, and maximums (using loTh for minimums and hiTh for maximums).
if(Scale==0 OR (loTh<0.0 AND hiTh<0.0)) then
No rescaling.
if(Scale == 1 AND (loTh>=0.0 OR hiTh>=0.0)) then
rescales averages using combined dynamic range of r,g,b ie 0.0 -> (max(redMax,grnMax,bluMax) - min(redMin,grnMin,bluMin)).
else if(Scale == 2 AND (loTh>=0.0 OR hiTh>=0.0)) then
rescales averages using separate dynamic ranges ie 0.0->(redMax-redMin), 0.0->(grnMax-grnMin), 0.0->(bluMax-bluMin).
For each channel, estimate gamma function that will remap (scaled channel average * channel multiplier) to match a particular
LockVal (chosen via LockChan) when rendered to the chosen output range specified by omin and omax.
Then renders frame using the output averages from estimated gamma with output channel minimums at omin, and maximums at omax.

GamMac(Clip c,int "LockChan"=1,int "Scale"=2,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "Th"= 0.0,Float "loTh"=Th,Float "hiTh"=Th,
\ Float "LockVal"=128.0,int "RngLim"=11,Float "GamMax"=10.0,
\ Clip "dc",
\ int "x"=20,int "y"=20,int "w"=-20,int "h"=-20,
\ int "omin"=0, int "omax"=255,
\ Bool "Show"=True,int "Verbosity"=2,Bool "Coords"=false,
\ Bool "Dither=False"
\ )

LockChan Default 1(Grn). Channel for lock to Average. [range -3 -> 2]
0 ] LockVal = Scaled(RedAve)
1 ] LockVal = Scaled(GrnAve)
2 ] LockVal = Scaled(BluAve)
-1] LockVal = Use explicit LockVal arg (see below).
-2] LockVal = (Scaled(RedAve)+Scaled(GrnAve)+Scaled(BluAve))/3.0. [Mean]
-3] LockVal = Median(Scaled(RedAve),Scaled(GrnAve),Scaled(BluAve))
Where Scaled(Channel Average) depends upon RngLim, Scale, and loTh, and hiTh.

Scale, default 1 Range 0 -> 1.
There is NO SCALING DONE if ALL THREE channels range is less than RngLim, see RngLim, linear render only.
If ANY ONE channel input range is less than RngLim and Scale==2, then Scale is (for current frame) knocked down to Scale=1.

where some described for Red Channel only:-
redMin = RedChanMin(ignorePerc=loTh) # Pixel minimum for red channel, ignoring up to loTh%, ie noise.
redMax = RedChanMax(ignorePerc=hiTh) # Pixel maximum for red channel, ignoring up to hiTh%, ie noise.
redAve = RedChanAve() # Pixel average for red Channel.
redRng = redMax - redMin
inMin = min(redMin,grnMin,bluMin) # Min of minimums
inMax = max(redMax,grnMax,bluMax) # Max of maximums

0 (Scale==0 || (loTh==-1.0 && hiTh==-1.0)) # No Effect on scale.
scaledAveR = redAve
scaledAveG = grnAve
scaledAveB = bluAve
1) Scales input channel average maximum dynamic range of R,G,B, to 0.0->(ChanAve-inMin)*255.0/(inMax-inMin)
scaler = 255.0 / (inMax - inMin)
scaledAveR = min(max((RedAve - inMin) * scaler,0.0),255.0)
scaledAveG = min(max((GrnAve - inMin) * scaler,0.0),255.0)
scaledAveB = min(max((BluAve - inMin) * scaler,0.0),255.0)
2) Scales input channel average dynamic range of R & G & B, Individually, to 0.0->(ChanAve-Chan_min)*255.0/(ChanMax-ChanMin)
scalerR = 255.0 / (redMax-redMin)
scalerG = 255.0 / (grnMax-grnMin)
scalerB = 255.0 / (bluMax-bluMin)
scaledAveR = min(max((redAve - redMin) * scalerR,0.0),255.0)
scaledAveG = min(max((grnAve - grnMin) * scalerG,0.0),255.0)
scaledAveB = min(max((bluAve - bluMid) * scalerB,0.0),255.0)

RedMul, default 1.0 Red channel multiplier adjustment. [0.1 <= RedMul <= 10.0]
GrnMul, default 1.0 Green channel multiplier adjustment. [0.1 <= GrnMul <= 10.0]
BluMul, default 1.0 Blue channel multiplier adjustment. [0.1 <= BluMul <= 10.0]
Scaled averages are multiplied by their multiplier then given as args to the gamma estimator.
Allow tweaking of R,G,B channels.
Above Multipliers only shown in metrics when at least one is != 1.0 (Always shown when Verbosity=3=FULL).

Th, Default 0.00 Sets Default for loTh and hiTh. Suggest Default, 0.00(percent). [-1.0(OFF) , or 0.0 -> 1.0]
loTh, Default Th As for Ignore_low in AutoLevels, or Threshold in YPlaneMin. [-1.0, or 0.0 -> 1.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding minimum R, G or B channel values.
-1.0 is OFF, input channel minimum is set to 0 as for levels(0,gamma,input_max, ... ).
If loTh >=0.0, then will scan frame looking for lowest pixel value whose cumulative sum
[including all pixels counts of lower value pixels] is greater than loTh%.
loTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).
hiTh, Default Th As for Ignore_high in AutoLevels, or Threshold in YPlaneMax. [-1.0, or 0.0 -> 1.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding maximum R, G or B channel values.
-1.0 is OFF, input channel maximum set to 255, as in levels(input_min,gamma,255, ... ).
If hiTh >=0.0, then will scan frame looking for highest pixel value whose cumulative sum
[including all pixels counts of higher value pixels] is greater than hiTh%.
hiTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).

LockVal, default 128.0 Only used if LockChan = -1. [0.0 < LockVal < 255.0] (set via LockChan if LockChan != -1)
There is no restricted range on this (other than 0.0 < LockVal < 255.0), so if you set a stupid value,
you will likely get stupid results.

RngLim, default 11 [1 <= RngLim <= 32]
If ALL THREE RAW input channel ranges ie (ChannelMax(max(hiTh,0.0))-ChannelMin(max(loTh,0.0))) are less than RngLim then
all scaling is disabled, and remapping is linear without gamma estimation, to range omin -> omax,
ie avoid remapping of Black, White frames, or single color frames.

GamMax, default 10.0 Upper value for guess gamma [1.0 < GamMax <= 10.0]
Starting guess upper range and limit for gamma estimator (probably best left alone).
The lower guess range and limit will be set to 1.0 / GamMax, by default 0.1.
Now allowing lower limit of GamMax to go as low as almost 1.0, GamMax now usable as
a gamma correction limiting device, where correction not allowed to exceed GamMax or go lower than
its reciprocal ie 1.0/GamMax. 'G' limited flag now added to flags line in metrics, hi-lited if Gamma
limited by GamMax (limiting includes any Red,Grn,BluMul, multiplier result).

dc, default clip c. Detection clip, Must be same ColorSpace and FrameCount as source clip, no other similarities enforced.
(can be different size, denoised etc).

x,y, Both default 20. Area of dc Detect clip frame to sample when getting averages and estimating Gamma function, allows to ignore rubbish at frame edges.
w,h, Both default -20. Specified as for crop eg x=10,y=20,w=-30,h=-40, as in crop(10,20,-30,-40).

omin, default 0. Output limits for all three R, and G, and B channels. [Range 0 -> 16]
omax, default 255. [Range 235 -> 255] (extremes 16->235 allow for Studio RGB output).
May want to give yourself a little head/foot room by setting eg omin=5, omax=250, so that you leave a little room for
further manual color tweaking.

Show, default true True, show metrics info on frame.
Verbosity, default 2 0 = Only upper frame metrics Flags line only
1 = Upper frame metrics
2 = Upper + important ones. (default)
3 = Nearly Full metrics.
4 = Full Metrics except version info
5 = Full Metrics including version info
Upper frame metrics shown as eg:- (when Verbosity=5=FULL)

nnnnn] Flags:- 1SRG
R G B
RAW: 10,253 10,253 10,253
IN: 10,253 10,253 10,253
IN_AVE: 78.466 88.552 78.767
SCALED: 71.847 82.431 72.162
GAMMA: 1.135 1.000 1.123
OUTAVE: 82.431 82.422 82.451

where,
nnnnn, is the frame number.
Flags:- (Specific to current frame, can change frame to frame)
'1' = LockChan, as above, channel '1'[ScaleAveG].
Can be, '0', '1', '2' [ScaleAve Channel number LockChan=-3(median) assigns '0', '1' or '2' as appropriate]
'A'[LockChan=-2, (ScaleAveR+ScaleAveG+ScaleAveB)/3.0]
'V'[LockChan=-1, Explicit LockVal]
'S' = Scale, mode signified by color.
Greyed out. Scale = 0(No Effect). May be Greyed out if all channels Min/Max are 0,255.
White. Scale = 1[Scales input channel average maximum dynamic range of R,G,B]
Orange. Scale = 2[Scales input channel average dynamic range of R and G and B, Individually]
'R' = Limited by RngLim, mode signfied by color.
Greyed out. Not Range Limited.
Red, at least 1 channel has remapping disabled.
'G' = Correction limited by GamMax, mode signfied by color.
Greyed out. Not Range Limited.
Orange hi-lite, at least 1 channel has GamMax limited gamma correction.
RAW: Shows RAW comma separated channel minimum and maximum, eg ChannelMin(max(loTh,0.0)) and (ChannelMax(max(hiTh,0.0)),
only shown if Verbosity>=3 or, if any RAW input range is less than RngLim AND any of the RAW inputs are different
to the equivalent standard input.
IN: Shows comma separated channel minimum and maximum (dependent upon Scale, loTh, hiTh).
IN_AVE: Input channel averages.
SCALED: Scaled input averages, (dependent upon Scale, loTh, hiTh, channel minimums and maximums).
GAMMA: Estimated gamma to achieve lockval for channel. (dependent upon pretty much everything).
OUTAVE: Output channel average ie rendered result.

ALL metrics derived from the detection clip dc (Including OutAve's).

Coords, default False. If True, then shows DC clip with dotted lines showing the x,y,w,h coords plotted on frame. (All other functionality disabled).

Dither, default False. If true, then dithers output, hopefully reducing banding (will be quite a lot slower, no ASM).


Produces Below. Top Left source. Top Right default settings. Bot Left BluMul 1.1 (too much Blue). Bot right BluMul=0.95.
https://s20.postimg.cc/kvgdhxxfh/Gam_Mac_1_zps4knvzil7.png (https://postimg.cc/image/w7syzq649/)

Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)
https://s20.postimg.cc/8i3jb17r1/Gam_Mac_2_zpsax99sdji.png (https://postimg.cc/image/q857w2lbt/)

Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)
https://s20.postimg.cc/jj39tspdp/Gam_Mac_3_zpskzc2dmu0.png (https://postimg.cc/image/5pex4qws9/)

See MediaFire or SendSpace in sig below.

EDIT: Zip approx 1MB, incl 3 png files, avs v2.58 x86, avs+ x86 and x64 dll's + source + VS2008 project files.

EDIT: Some images and JohnMeyer Parade clip often used in this thread:- https://forum.doom9.org/showthread.php?p=1825394#post1825394

FranceBB
16th July 2016, 15:04
Tested and it works perfectly on very old sources!
Thank you very much indeed; it's a way easier to adjust colours this way!

StainlessS
16th July 2016, 15:14
Glad you like it France, dont forget to say thanx to VideoFred for his weird & whacky idea :)

StainlessS
16th July 2016, 16:24
And here Lenna Sjööblom

Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)
https://s20.postimg.cc/vyzznjipp/Lenna_zpsb3ccnvcu.png (https://postimg.cc/image/p8jie3vjt/)

Bernardd
16th July 2016, 17:17
Hello StainlessS

Just a proposal for lockval : (In_Ave_0 + In_Ave_1 +In_Ave_2)/3

When i have written my script based on RGBAdapt, i have found the average of channel averages give often better result than 128.

Bernard

StainlessS
16th July 2016, 17:58
GamMac v1.01, Update as per Bernardd post #5.

Thanks Berni, could well be useful. :)

Lenna again with LockChan = -2 for TopRHS pic (which turns out to be about LockVal=128.0)

https://s20.postimg.cc/dkpgjk6f1/Lenna2_zpsi30spmpe.png (https://postimg.cc/image/b3dpcamih/)

EDIT: Fred, take a look at Bernardd LockChan= -2, should we change to -2 as default ?
Don't take decision based purely on Lenna, think that was deliberately manually screwed with, for whatever reason.
The version of Lenna originally published in a well known magazine, the feather scarf I think is nearly black,
think published version must have been altered for publication, and the above 'red' version must have come from
source picture (not altered for publication) from the photographer, and then deliberately screwed with to make red.
Think maybe above LockChan=1 or LockChan=2 is more like original shot.

EDIT: Pub, more like this:
https://s20.postimg.cc/7xt3m33wd/Lenna_Org_zpstoi1s1pl.png (https://postimg.cc/image/u9qwfh309/)

videoFred
17th July 2016, 11:22
EDIT: Fred, take a look at Bernardd LockChan= -2, should we change to -2 as default ?

Working on it.... Looks all very promising!
Dll version runs at real time :)

Fred.

StainlessS
17th July 2016, 19:19
GamMac() v1.02, update. See 1st post.

Added LockChan= -3, and Verbosity args. Appearance changed a little. Probably a bit faster.

EDIT:
Dll version runs at real time :)
Plays the 4x stacked image of parade at about double speed on my crappy core duo, so pretty good speed.

Motenai Yoda
18th July 2016, 01:36
@StainlessS it can be made faster using a downscaled (with a gamma-aware resizer) clip?
also as it can find a minimum and maximum value for each channel, will be possible to make a level adjustment right before the average gamma-aligning?

StainlessS
18th July 2016, 01:53
Yo, Yoda,
Faster aint a problem, not implemented at all like in script.
So far as I'm concerned, not possible to make much faster (all done on pixel count arrays[histograms], not clips).
(dont know how gamma aware that might be, dont see how that would be a problem except for final render,
and I dont see that as my immediate problem, if someone else wants to take this further, then be my guest please.).

Can you expand upon this please
can find a minimum and maximum value for each channel, will be possible to make a level adjustment right before the average gamma-aligning?

for your amusement, here is most of the source, excluding setup stuff.

void GamMac::CountRGB(int n,unsigned int *cntR,unsigned int *cntG,unsigned int *cntB,IScriptEnvironment* env) {
n = (n<0) ? 0 : (n>= vi.num_frames) ? vi.num_frames - 1 : n;
PVideoFrame src = child->GetFrame(n, env);
int rowsize = src->GetRowSize();
int height = src->GetHeight();
int pitch = src->GetPitch();
const BYTE *srcp= src->GetReadPtr();
memset(cntR,0,sizeof(cntR[0])*256); memset(cntG,0,sizeof(cntG[0])*256); memset(cntB,0,sizeof(cntB[0])*256);
// We process from bottom to top (weird RGB order)
if(vi.IsRGB32()) {
for(int y=height;--y>=0;) {
for(int x=rowsize;(x-=4)>=0;) {
++cntB[srcp[x+0]];
++cntG[srcp[x+1]];
++cntR[srcp[x+2]];
}
srcp += pitch;
}
} else {
for(int y=height;--y>=0;) {
for(int x=rowsize;(x-=3)>=0;) {
++cntB[srcp[x+0]];
++cntG[srcp[x+1]];
++cntR[srcp[x+2]];
}
srcp += pitch;
}
}
}


double GamMac::GuessGamma(unsigned int *cnt,double reqAve) {
double result=-1.0;
double PrevAve=-1.0;
double glo=GamLo;
double ghi=GamHi;
const unsigned int Pixels = (vi.width * vi.height);
while(glo < ghi) {
double gmid = (glo + ghi) / 2.0;
const double igam = 1.0/gmid;
__int64 acc = 0;
for(int i=256;--i>=0;) {
double v=i/255.0; // scale 0.0 -> 1.0
if (v > 0.0) { // avoid error
v = pow(v,igam);
if (v > 1.0) v = 1.0; // avoid possible overflow
else if(v < 0.0) v = 0.0;
}
v = (v * 255.0) + 0.5;
int val = int(floor(v)); // Round towards -ve infinity
if (val > 255) val = 255;
else if(val < 0) val = 0;
acc += __int64(cnt[i]) * val;
}
double ave = double(acc) / Pixels;
if(ave==reqAve||fabs(ave-PrevAve)<0.00001) {result = gmid; break;}
else if(ave<reqAve) {glo=gmid;}
else {ghi=gmid;}
PrevAve=ave;
}
return result;
}


double GamMac::ChanAve(unsigned int *cnt) {
__int64 acc=0;
for(int i=256;--i>=0;) {acc += cnt[i] * __int64(i);}
const unsigned int Pixels = (vi.width * vi.height);
return ((double)acc / Pixels);
}



void GamMac::SetGammaLut(double gamma,BYTE *lut) {
if(fabs(gamma-1.0)<0.00001) {
for(int i=256;--i>=0;lut[i]=i);
} else {
const double igam = 1.0/gamma;
for(int i=256;--i>=0;) {
double v=i/255.0; // scale 0.0 -> 1.0
if (v > 0.0) { // avoid error
v = pow(v,igam);
if (v > 1.0) v = 1.0; // avoid possible overflow
else if(v < 0.0) v = 0.0;
}
v = (v * 255.0) + 0.5;
int val = int(floor(v)); // Round towards -ve infinity
if (val > 255) val = 255;
else if(val < 0) val = 0;
lut[i] = val;
}
}
}

double GamMac::ChanAveFromLut(unsigned int *cnt,BYTE *lut) {
__int64 acc=0;
for(int i=256;--i>=0;) {
acc += __int64(cnt[i]) * lut[i];
}
const unsigned int Pixels = (vi.width * vi.height);
return ((double)acc / Pixels);
}


PVideoFrame __stdcall GamMac::GetFrame(int n, IScriptEnvironment* env) {
n = (n<0) ? 0 : (n>= vi.num_frames) ? vi.num_frames - 1 : n;
unsigned int cntR[256],cntG[256],cntB[256];
CountRGB(n,cntR,cntG,cntB,env);
double inR=ChanAve(cntR);
double inG=ChanAve(cntG);
double inB=ChanAve(cntB);
double lockval;
if(LockChan==0) {lockval=inR;}
else if(LockChan==1) {lockval=inG;}
else if(LockChan==2) {lockval=inB;}
else if(LockChan==-2) {lockval=((inR+inG+inB)/3.0);}
else if(LockChan==-3) {double mx=max(max(inR,inG),inB); double mn=min(min(inR,inG),inB); lockval=inR+inG+inB-mx-mn;}
else {lockval =LockVal;}
bool offR=(inR<MinLim || inR>MaxLim);
bool offG=(inG<MinLim || inG>MaxLim);
bool offB=(inB<MinLim || inB>MaxLim);
double gammaR=(offR)?1.0:(fabs(inR-lockval*RedMul)<0.0001)?1.0:GuessGamma(cntR,lockval*RedMul);
double gammaG=(offG)?1.0:(fabs(inG-lockval*GrnMul)<0.0001)?1.0:GuessGamma(cntG,lockval*GrnMul);
double gammaB=(offB)?1.0:(fabs(inB-lockval*BluMul)<0.0001)?1.0:GuessGamma(cntB,lockval*BluMul);
BYTE lutR[256],lutG[256],lutB[256];
SetGammaLut(gammaR,lutR);
SetGammaLut(gammaG,lutG);
SetGammaLut(gammaB,lutB);
PVideoFrame src = child->GetFrame(n, env);
PVideoFrame dst = env->NewVideoFrame(vi);
int rowsize = src->GetRowSize();
int height = src->GetHeight();
int pitch = src->GetPitch();
int dpitch = dst->GetPitch();
const BYTE *srcp= src->GetReadPtr();
BYTE *dstp = dst->GetWritePtr();
int x,y;
// We process from bottom to top (weird RGB order)
if(vi.IsRGB32()) {
for(y=height;--y>=0;) {
for(x=rowsize;(x-=4)>=0;) {
dstp[x+0] = lutB[srcp[x+0]];
dstp[x+1] = lutG[srcp[x+1]];
dstp[x+2] = lutR[srcp[x+2]];
dstp[x+3] = srcp[x+3];
}
srcp += pitch;
dstp += dpitch;
}
} else {
for(y=height;--y>=0;) {
for(x=rowsize;(x-=3)>=0;) {
dstp[x+0] = lutB[srcp[x+0]];
dstp[x+1] = lutG[srcp[x+1]];
dstp[x+2] = lutR[srcp[x+2]];
}
srcp += pitch;
dstp += dpitch;
}
}
if(Show) {
double outR=ChanAveFromLut(cntR,lutR);
double outG=ChanAveFromLut(cntG,lutG);
double outB=ChanAveFromLut(cntB,lutB);
DrawFStr(dst,0,0,"%d] \a!GamMac v%.2f\a-\n"
" \a2R \a4G \a1B\a-\n"
"IN_AVE: %7.3f : %7.3f : %7.3f\nGAMMA : %7.3f : %7.3f : %7.3f\n"
"OUTAVE: %7.3f : %7.3f : %7.3f",n,GAMAC_VER,inR,inG,inB,gammaR,gammaG,gammaB,outR,outG,outB);
if(Verbosity!=0) {
if(Verbosity==1) {
DrawFStr(dst,0,vi.height/20-2,
"Lockchan=%d LockVal=%.3f\n"
"RedMul=%.3f GrnMul=%.3f BluMul=%.3f",
LockChan,lockval,RedMul,GrnMul,BluMul);
} else {
DrawFStr(dst,0,vi.height/20-4,
"Lockchan=%d LockVal=%.3f\nGamHi=%.3f GamLo=%.3f\nMinLim=%.3f MaxLim=%.3f\n"
"RedMul=%.3f GrnMul=%.3f BluMul=%.3f",
LockChan,lockval,GamHi,GamLo,MinLim,MaxLim,RedMul,GrnMul,BluMul);
}
}
}
return dst;
}


EDIT: Missed out CountRGB, added.

EDIT:
Starting to sober up a bit, and to some extent understand what you were saying, however, we do not do any resizing and
so dont understand where Gamma Aware Resizing would come into it.

Motenai Yoda
18th July 2016, 15:25
Yo, Yoda,
Faster aint a problem, not implemented at all like in script.
So far as I'm concerned, not possible to make much faster (all done on pixel count arrays[histograms], not clips).
It can be possible feed it with a LockClip with lower dimensions to do some stuff with less pixels IIRC LaTo's AutoAdjust can permitt that, can be faster when working on hi res stuff.

Can you expand upon this please
As it find out min/max of each channel too, it can be possible align min and max to the ref channel ones
like minB=>minG and maxB=>maxG
also the averages can be easely recalculated before doing gamma stuff


[CODE]
memset(cntR,0,sizeof(cntR[0])*256);
memset(cntG,0,sizeof(cntG[0])*256);
memset(cntB,0,sizeof(cntB[0])*256);
Wasn't better to use a runtime defined value to set cntR/G/B size?
Just to get the code ready for a future 16/32bit capability

StainlessS
18th July 2016, 16:14
It can be possible feed it with a LockClip with lower dimensions to do some stuff with less pixels IIRC LaTo's AutoAdjust can permitt that, can be faster when working on hi res stuff.


As we do all work on count arrays, that is irrelevant, you would still have to access hd source to create you lo-rez lock clip, even less work to create count arrays, and less again to do later processing on arrays rather than another clip.
As said previously, I'm getting about double realtime on the Parade stack4 clip, that is at least 6x realtime speed, and on my lowly core duo 2.4Ghz, better machine would probably get ~8, maybe 10 times faster than that.

Wasn't better to use a runtime defined value to set cntR/G/B size?
Just to get the code ready for a future 16/32bit capability

To use standard stack space, must be sized at compile time.
I doubt very much if you would want to be using stack space if using 16/32 bit count array, so whole lot would need to be changed anyway to use heap [ie 16 bit, 65536*sizeof(unsigned int]). For 32 bit, would be way too big to be practical, maybe Sparce Array or something would be required.


As it find out min/max of each channel too, it can be possible align min and max to the ref channel ones
like minB=>minG and maxB=>maxG
also the averages can be easely recalculated before doing gamma stuff


Dont know if that is a good idea, what say you Fred ?

EDIT: By the way, the last version v1.02 was an almost complete re-write to use almost exclusively count arrays and luts,
so is probably somewhat faster than previous. Judging by times of posts, re-write took about 2 hours total,
and so would guess modding for 16 bit would not be such a very hard job, there is not really very much of it.
As far as Stack16 is concerned, would not bother doing that, the only other RGB Stack16 plugin that I am aware of is
my ClipBlend16() which was a bit of a mistake as I did not know that there is no support for Stack16 RGB and no
demand for such.

videoFred
18th July 2016, 16:55
Dont know if that is a good idea, what say you Fred ?

I say stretching the histogram from R, G and B before GamMac() would be a very good idea. I have done it with autolevels() and it looks like this:

http://www.super-8.be/Doom/GamMac000001_small.jpg

http://www.super-8.be/Doom/GamMac000002_small.jpg

http://www.super-8.be/Doom/GamMac000003_small.jpg

http://www.super-8.be/Doom/GamMac000004_small.jpg

http://www.super-8.be/Doom/GamMac000005_small.jpg

http://www.super-8.be/Doom/GamMac000006_small.jpg

http://www.super-8.be/Doom/GamMac000007_small.jpg

http://www.super-8.be/Doom/GamMac000008_small.jpg

Original full size:
http://www.super-8.be/Doom/GamMac000001.jpg
http://www.super-8.be/Doom/GamMac000002.jpg
http://www.super-8.be/Doom/GamMac000003.jpg
http://www.super-8.be/Doom/GamMac000004.jpg
http://www.super-8.be/Doom/GamMac000005.jpg
http://www.super-8.be/Doom/GamMac000006.jpg
http://www.super-8.be/Doom/GamMac000007.jpg
http://www.super-8.be/Doom/GamMac000008.jpg

First frame is very old regular-8 film with blue cast. All other frames are 1970's Fuji Single-8 film with green cast, as you see.

By now, I have tested GamMac() on all kinds of 8mm films. I have made a AvsPmod script with sliders for Lockchan, RedMul, GrnMul and BluMul. Imho Lochchan can stay on 1. The "Mul' settings can be used for very fine tuning.

I can give hundreds of other examples, GamMac is removing the color cast (whatever it may be) in 90% of the cases.

More specific color tuning can be done afterwards with Tweak(hue, starhue, endhue) and RGBAdjust().

But GamMac gives a very good and full automatic base to work with, thank you again StainlessS :)

Fred.

Sparktank
18th July 2016, 18:03
Wow! Now that are some mighty fine results.

This is pretty amazing stuff. Not that I have any older material to work with, but just in general this is aboslutely fascinating to see.

This looks like something that can be fun to use on movies for an entirely different viewing.

I will definitely be looking at this one. :D

Motenai Yoda
18th July 2016, 19:41
As we do all work on count arrays, that is irrelevant, you would still have to access hd source to create you lo-rez lock clip, even less work to create count arrays, and less again to do later processing on arrays rather than another clip.
Well but to create count arrays it still scan the entire frame, I was thinking not only to a low rez clip but also at a denoised one to be used as a reference like some dither stuff do.
also why don't use a find min/max/avg cicle using the accumulator directly into countRGB?

ps Level stretching can be done in gammaLut, or just before, as average values can be scaled with a plain mx+q expression.

StainlessS
18th July 2016, 20:29
Fred, where we have minR, minG, minB, maxR, maxG, maxB,
should we do levels for eg R on minR, maxR, OR, min(minR,minG,minB) and max(maxR,maxG,maxB) ?

EDIT: On levels inputs ie Levels(minimum_r, gamma, maximum_r,0,255,coring=false)

EDIT: Can you post full script for top pic (lollipop lady), I'm not getting same numbers.

EDIT: Also, can you post the original unaltered pic and what you get out of autolevels. (No idea what autolevels does, its closed source I believe).

EDIT: And which autolevels do you use (LaTo seems to refer to both autogain and autolevels, as autolevels).

EDIT: Forget above about AutoLevels, was mixing up Frustum and LaTo plugins.

videoFred
18th July 2016, 21:47
Fred, where we have minR, minG, minB, maxR, maxG, maxB,
should we do levels for eg R on minR, maxR, OR, min(minR,minG,minB) and max(maxR,maxG,maxB) ?

Simple: not trying to match min, max or whatever but straight forward levels as close as possible to 0 and 255 for each individual RGB channel. Perhaps with a small margin like 5-250 or an adjustable margin.


EDIT: Can you post full script for top pic (lollipop lady), I'm not getting same numbers.

EDIT: Also, can you post the original unaltered pic and what you get out of autolevels.

Sure, I will do this as soon as possible (probably tomorrow afternoon)

Fred.

StainlessS
18th July 2016, 22:18
I suspected that you should NOT use separate min for R G and B, and so min(minR,minG,minB) and max(maxR,maxG,maxB)
are the correct values otherwise screws up relations to each other. See by IanB here:- http://forum.doom9.org/showthread.php?p=1457091#post1457091

I will not be attempting (or will discard any attempt so far) to add in Auto Levels functionality into GamMac(), one of the reasons
is that AutoLevels does temporal sampling to avoid sudden changes as happens with ColorYUV(AutoLevels=true), so to continue to
use AutoLevels would be the best solution, and I dont want to re-invent the wheel.
I see no gain in trying to combine two plugins into one, when using first AutoLevels and then GamMac, would provide the exact same result.

Dont bother with the requested samples Fred, dont need them now.

videoFred
18th July 2016, 23:18
I suspected that you should NOT use separate min for R G and B, and so min(minR,minG,minB) and max(maxR,maxG,maxB)
are the correct values otherwise screws up relations to each other. See by IanB here:- http://forum.doom9.org/showthread.php?p=1457091#post1457091

Ok it might be not correct but it works as you can see on my examples.


I will not be attempting (or will discard any attempt so far) to add in Auto Levels functionality into GamMac(), one of the reasons
is that AutoLevels does temporal sampling to avoid sudden changes as happens with ColorYUV(AutoLevels=true), so to continue to
use AutoLevels would be the best solution, and I dont want to re-invent the wheel.

Of cource, I understand. The autolevels temporal sampling works very well, no flickering and no sudden changes.

Fred.

StainlessS
19th July 2016, 00:46
This should hopefully be of use.
All it does is pass on args to respective plugins.
Some of the AutoLevels args are not passed where it makes no sense, eg the gamma related ones.

EDIT: Ignore below, have since implemented Scale=2.

Function AutoLevelsGamMac(clip c,
\ Bool "DoAutoLevels", bool "DoGamMac",
\ int "filterRadius",int "sceneChgThresh",String "frameOverrides",
\ int "input_low",int "input_high",int "output_low",int "output_high",float "ignore",float "ignore_low",float "ignore_high",
\ int "border",int "border_l",int "border_r",int "border_t",int "border_b",bool "debug",
\ int "LockChan",Float "LockVal",Float "RedMul",Float "GrnMul", Float "BluMul",
\ Float "MinLim",Float "MaxLim",float "GamLo",Float "GamHi",Bool "Show",int "Verbosity") {
c
DoAutoLevels=Default(DoAutoLevels,True) DoGamMac=Default(DoGamMac,True)
(DoAutoLevels)
\ ? Autolevels(filterRadius=filterRadius,sceneChgThresh=sceneChgThresh,frameOverrides=frameOverrides,
\ input_low=input_low,input_high=input_high,output_low=output_low,output_high=output_high,
\ ignore=ignore,ignore_low=ignore_low,ignore_high=ignore_high,
\ border=border,border_l=border_l,border_r=border_r,border_t=border_t,border_b=border_b,
\ debug=debug)
\ : NOP
(DoGamMac)
\ ? GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,
\ MinLim=MinLim,MaxLim=MaxLim,GamLo=GamLo,GamHi=GamHi,
\ Show=Show,Verbosity=Verbosity)
\ : NOP
Return Last
}

#Imagesource("test_RGB_Doom.jpg",end=0) Crop(0,0,width/2,height/2) Crop(0,0,width/4*4,height/4*4)

Imagesource("G1.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Lollipop lady minus RHS and histograms
#Imagesource("G2.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Plant lady minus RHS and histograms
#Imagesource("G3.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Avenue minus RHS and histograms
#Imagesource("G4.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Walkers minus RHS and histograms
#Imagesource("G5.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Parot minus RHS and histograms
#Imagesource("G6.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Deer minus RHS and histograms
#Imagesource("G7.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Taj Mahal minus RHS and histograms
#Imagesource("G8.bmp",end=0) crop(0,0,0,-48) Spline36Resize(480,360) # Puppy minus RHS and histograms

#Avisource("1937 Lund Utah 16mm Film [Low, 360p].mp4.avi")
#Avisource("1941 Flint Michigan Parade [Low, 360p].mp4.AVI")
#Avisource("v.avi")
#A=Trim(0,99)
#B=A.BlankClip(length=1) # Test Black Frame @ 100
#C=A.BlankClip(length=1,Color=$FFFFFF) # Test White Frame @ 101
#D=Trim(100,0)
#A++B++C++D

ConvertToRGB24
ORG=Last
LockChan = 1 # Chan for lock to Ave, 0=R, 1=G, 2=B
# -1 = Use LockVal below.
# -2 = LockVal=(RedAve+GrnAve+BluAve)/3.0 for LockVal.
# -3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal = 128 # Only valid if LockChan == -1
GamHi = 4.0 # Extreme values for guess gamma (starting guess range and limit)
GamLo = 0.25 # Extreme values for guess gamma (starting guess range and limit)
RedMul = 1.00 # Required Ave multiplier for Red Channel, applied when requesting GuessGamma(reqAve*RedMul), Even applied when LockChan=-1.
GrnMul = 1.00 # Same for Green channel. GrnMul even applies when LockChan is Green Channel, etc for chans.
BluMul = 1.0 # Same for Blue channel. Allows tinkering/fine tuning.
MinLim = 32.0 # If Original channel Ave lesser then DO NOT FIX.
MaxLim = 255.0-32.0 # If Original channel Ave greater then DO NOT FIX.
Show = true # Subtitles
Verbosity= 1 # 0=Only Upper metrics, 1(default)=Upper + important ones. 2=All metrics.

A= AutoLevelsGamMac(DoGamMac=False)
B= AutoLevelsGamMac(DoAutoLevels=false,LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=Verbosity)
C= AutoLevelsGamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=Verbosity)
TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last



EDIT:
Ok it might be not correct but it works as you can see on my examples.

That IanB quote was from the AutoLevels thread, I assume that Frustum corrected it in later versions of AutoLevels
to match what IanB said.

Bloax
19th July 2016, 09:23
https://dl.dropboxusercontent.com/u/63152810/lenna.png
Doing some adjustment bullshit in Photoshop (https://dl.dropboxusercontent.com/u/63152810/lenna.7z) (primarily curves with a little Color Balance cheating) certainly reveals that you can do much more.


But it's good to see people making interesting things, even if I have no use for them myself being the young'un I am.

videoFred
19th July 2016, 11:49
But it's good to see people making interesting things, even if I have no use for them myself being the young'un I am.

But perhaps there are some old family films hidden somewhere at your uncles or grandfathers house, you never know :)

Fred.

udobroemme
19th July 2016, 15:12
Thank you very much for this great filter. I did a few tests with some faded prints and the results are very impressing. :thanks:

StainlessS
19th July 2016, 15:17
Ok it might be not correct but it works as you can see on my examples.

I did try in GamMaC to set levels using both individual min,max for each channel, and also chanmin=min(minR,minG,minB), chanmax=max(maxR,maxG,maxB),
and results were not terribly encouraging. Looking at AutoLevels source, I find that it converts R,G & B to Luma Y to get min, max and mean (together
with the ignore args). So, AutoLevels currently uses neither RGB min,max method mentioned. Dont know whether this change was done since the previous
IanB quote or whether always as is.
Anyway, we really dont need to be re-inventing that wheel and I am now happy to completely abandon any idea to do same in purely RGB.

Fred can you verify that you did not use any of the gamma args in AutoLevels in your tests [they are unused by default and I did not pass on gamma
related args in the AutoLevelsGamMac() stub]. EDIT: better still can you post the script for eg the Lollipop Lady [GamMac000001.jpg].

EDIT: @ Bloax, you could have gotten LockChan 2 Lenna without the metrics setting Show=false, and perhaps better
results in PhotoShop due to missing white text. (guess you just downloaded the pic and not GamMac).

StainlessS
19th July 2016, 18:43
Fred, I cocked up previous trials at the RGB prelevel thing (such is the lot of a nincompoop),
I had another go and this time a bit better.

Here v1.03Beta for you to have a play with, because I'm going t' pub.
LINK REMOVED

and the puppy pic

https://s20.postimg.cc/gu3vq0uil/Puppy_zpsgozcwht1.png (https://postimg.cc/image/q1w46q1kp/)

args added

Th, Default 0.0(off) 0.0 -> 10.0(perhaps limited to ~2.0 in non Beta). Default for MinTh and MaxTh. Suggest about 0.2(percent).
MinTh, Default Th. 0.0 -> ??? as for Ignore_low in AutoLevels, or threshold in YPlaneMin.
MaxTh, Default Th. 0.0 -> ??? as for Ignore_high in AutoLevels, or threshold in YPlaneMax.
MinMaxLock, Default True. If true, input_low and input_high set identical eg min(rmin,gmin,bmin) for R,G,B. Else set individually.


Can use instead of AutoLevels if required.

EDIT: If you (or anyone) would like to knock up a little documentation, that would be glorious :) [me hates dat]

videoFred
20th July 2016, 00:04
Fred can you verify that you did not use any of the gamma args in AutoLevels in your tests [they are unused by default and I did not pass on gamma
related args in the AutoLevelsGamMac() stub]. EDIT: better still can you post the script for eg the Lollipop Lady [GamMac000001.jpg].

Yes, I can confirm I have not used any gamma args in AutoLevels(). Do you still need my script? Because I have seen your update in the next post.

Fred.

videoFred
20th July 2016, 00:07
Here v1.03Beta for you to have a play with, because I'm going t' pub.

Thank you! Have a Belgian beer on my account :)
Will test it asap...


EDIT: If you (or anyone) would like to knock up a little documentation, that would be glorious :) [me hates dat]

I can do this, but first we must be sure to have the final version. :)

Fred.

StainlessS
20th July 2016, 00:31
I can do this, but first we must be sure to have the final version.
Fantastic, however, you also have to pick names (mine usually start out as temp or fred (not in honour of you, [but you can tell people that it is, I will not deny it])
Need full docs, way more sensible than wot I wud wryt.
Take your time, be careful, and good. May the lord be with you, and also with you. :) (no idea wot that means).

EDIT: Pick, suggest whatever arg names you want, I pretty much guarantee your suggestions are acceptable and concrete.
EDIT:
Lost my other Vape stick again, today is a very sad day, again. Sick 0' losin my Vape sticks, the world is such a dangerous place.

Stella, beer, pretty damn good. Also your Trappist, brewed by drunken clerics (very well recommended).
Belgians are the most prolific of Beer makers (high up there on drinkers too) in the world.

Motenai Yoda
20th July 2016, 14:21
I did try in GamMaC to set levels using both individual min,max for each channel, and also chanmin=min(minR,minG,minB), chanmax=max(maxR,maxG,maxB),
and results were not terribly encouraging.
Just to be sure, did you recalculate the avg values with the scale formula before guess gamma?
my suggestion was about to match a reference channel range coz other tools yet did increase global contrast using something like min(minR,minG,minB)/max(maxR,maxG,maxB), also autolevels roughly do the same but it stretch all of them to 0/255 - 16/235 not maintaining the source frame's dynamic range

also IanB was talking about don't change RGB ratios as AutoLevels is to increase global contrast without change colors or dominances.
GamMac will change those ratios anyway.

ps as gamma function is more effective on the lower part of the range than on the upper one, maybe will be better to use a different, more balanced, curve?

StainlessS
20th July 2016, 15:51
Not terribly encouraging
Basically I forgot that the thresholds were in percent, and so forgot to divide by 100.0, so results were garbage :(

did you recalculate the avg values with the scale formula before guess gamma?
No.
Yoda, discuss what is required with Fred, so far as I'm concerned it is his baby and he's the gaffer.

Source is included in the zip below (I shall not try to convert the original script to be similar to current version).


GamMac(), [Gamma Machine] An extraordinary Idea by VideoFred (the gent from Gent). Coded by StainlessS.

Home Thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281
Idea:- http://forum.doom9.org/showthread.php?t=173683

RGB Only.

Useful to correct color cast on old 8mm films.
Alters channel pixel average to match LockChan using Gamma correction. (By default alters Red and Blue channels to match Green).
Additional tweaking via RedMul, GrnMul and BluMul.

GamMac(clip c,int "LockChan"=1,Float "LockVal"=128,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "MinLim"=32.0,Float "MaxLim"=255.0-32.0,float "GamLo"=0.25,Float "GamHi"=4.0,
\ Bool "Show"=false,int "Verbosity"=1
\ Float "Th"= -1.0,Float "loTh"=Th,Float "hiTh"=Th,Bool "loHiLock"=True
\ )

LockChan Default 1. Chan for lock to Ave. (0)LockVal=RedAve, (1)LockVal=GreenAve, (2)LockVal=BlueAve
-1 = Use explicit LockVal below.
-2 = LockVal=(RedAve+GrnAve+BluAve)/3.0.
-3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal, default 128.0. Ignored if LockChan != -1.0. 0.0 < LockVal < 255.0
RedMul, default 1.0. 0.1 <= RedMul <= 10.0. Red channel adjustment.
GrnMul, default 1.0. 0.1 <= GrnMul <= 10.0. Green channel adjustment.
BluMul, default 1.0. 0.1 <= BluMul <= 10.0. Blue channel adjustment.
Above Multipliers only shown in metrics when at least one is != 1.0.
MinLim, default 32.0. 0.0 < MinLim < 255.0 If any channel average smaller than this, then no effect on channel.
MaxLim, default 255.0-32.0. MinLim < MaxLim < 255.0 If any channel average greater than this, then no effect on channel.
GamLo, default 0.25. 0.1 <= GamLo <= 10.0. Lower value for guess gamma (starting guess range and limit)
GamHi, default 4.0. GamLo < GamHi <= 10.0. Upper value for guess gamma (starting guess range and limit)
Show, default false. True, show info.
Verbosity, default 1. 0=Only Upper metrics, 1(default)=Upper + important ones. 2=Full metrics.

Th, Default -1.0(off) -1.0, or 0.0 -> 2.0. Sets Default for loTh and hiTh. Suggest about 0.0, or 0.1(percent).
loTh, Default Th. -1.0, or 0.0 -> 2.0. as for Ignore_low in AutoLevels, or Threshold in YPlaneMin.
hiTh, Default Th. -1.0, or 0.0 -> 2.0. as for Ignore_high in AutoLevels, or Threshold in YPlaneMax.
Percent, amount of extreme pixels (eg noise) to ignore when finding minimum and maximum R, G or B channel values.
-1.0 is OFF, input channel minimum is set to 0 and maximum set to 255.0, as in levels(0,gamma,255, ... ).
If loTh set to eg 0.0, then will scan frame looking for minimum accumulated channel values that have more than 0.0%
of pixels, and set input_min for that channel to that value. (Accumulated:- including values that were lower but not
of great enough number to break the threshold).
If hiTh set to eg 0.0, then will scan frame looking for maximum accumulated channel values that have more than 0.0%
of pixels, and set input_max for that channel to that value.
loTh and hiTh, only shown in metrics if greater or equal to 0.0 ie switched ON.

loHiLock, Default True. If (loHiLock==true) then input_lowR = min(rmin,gmin,bmin) ELSE input_lowR = rmin
If (loHiLock==true) then input_highR = max(rmax,gmax,bmax) ELSE input_highR = rmax
Where for Red, sort of equivalent to outR=LevelsR(input_lowR,gamma,input_highR, 0,255)
Same for other channels.



Imagesource("GreenChurch.png",end=0)
#Imagesource("Puppy.png",end=0)
#Imagesource("lennaRed.png",end=0)

#Avisource("1937 Lund Utah 16mm Film [Low, 360p].mp4.avi")
#Avisource("1941 Flint Michigan Parade [Low, 360p].mp4.AVI")

ConvertToRGB24
ORG=Last

LockChan = 1 # Chan for lock to Ave, 0=R, 1=G, 2=B
# -1 = Use LockVal below.
# -2 = LockVal=(RedAve+GrnAve+BluAve)/3.0 for LockVal.
# -3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal = 128 # Only valid if LockChan == -1
RedMul = 1.0 # Required Ave multiplier for Red Channel, applied when requesting GuessGamma(reqAve*RedMul), Even applied when LockChan=-1.
GrnMul = 1.0 # Same for Green channel. GrnMul even applies when LockChan is Green Channel, etc for chans.
BluMul = 1.0 # Same for Blue channel. Allows tinkering/fine tuning.
Show = true # Subtitles

A=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=0)

thB = 0.0
BluMulB = 0.95
B=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMulB,Show=Show,Verbosity=1,th=thB)

thC = 0.0
BluMulC = 0.95
LoHiLockC=False
C=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMulC,Show=Show,Verbosity=2,th=thC,LoHiLock=LoHiLockC)

TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last


Produces this
https://s20.postimg.cc/ruz0v1mrh/Green_Church_zps9vcfwkvl.png (https://postimg.cc/image/569tvh5dl/)

Here v1.04, incl GreenChurch, Puppy, and red Lenna pings(990KB):- LINK REMOVED

Fred, I'm leaving totally up to you to say what your orders are, including any arg name changes or whatever,
awaiting instruction.

I can do what Yoda suggests if required (or make optional via arg if required).

StainlessS
20th July 2016, 21:03
OK, have tried to implement what Yoda was talking about.

update to DOC

GamMac(clip c,int "LockChan"=1,Float "LockVal"=128,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "MinLim"=32.0,Float "MaxLim"=255.0-32.0,float "GamLo"=0.25,Float "GamHi"=4.0,
\ Bool "Show"=false,int "Verbosity"=1
\ Float "Th"= -1.0,Float "loTh"=Th,Float "hiTh"=Th,Bool "loHiLock"=True
\ bool "Scale"=true
\ )

...


Scale, default False. if Scale (and loTh and/or hiTh >= 0.0) then scales input channel averages
eg, in_scale = (Scale) ? 255.0 / (in_max - in_min) : 1.0;
where in_min = min(minR,minG,minB) and in_max = max(maxR,maxG,maxB)


Script

#Imagesource("GreenChurch.png",end=0)
Imagesource("Puppy.png",end=0)
#Imagesource("lennaRed.png",end=0)

#Avisource("1937 Lund Utah 16mm Film [Low, 360p].mp4.avi")
#Avisource("1941 Flint Michigan Parade [Low, 360p].mp4.AVI")

ConvertToRGB24
ORG=Last

LockChan = 1 # Chan for lock to Ave, 0=R, 1=G, 2=B
# -1 = Use LockVal below.
# -2 = LockVal=(RedAve+GrnAve+BluAve)/3.0 for LockVal.
# -3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal = 128 # Only valid if LockChan == -1
RedMul = 1.0 # Required Ave multiplier for Red Channel, applied when requesting GuessGamma(reqAve*RedMul), Even applied when LockChan=-1.
GrnMul = 1.0 # Same for Green channel. GrnMul even applies when LockChan is Green Channel, etc for chans.
BluMul = 1.0 # Same for Blue channel. Allows tinkering/fine tuning.
Show = true # Subtitles
th = 0.0

A=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,Th=Th)

B=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=Th,SCALE=True)

LoHiLockC=False
C=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=th,LoHiLock=LoHiLockC,SCALE=True)

TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last


EDIT: Below images have full precision metrics, forgot to switch that off.

https://s20.postimg.cc/fhm6o4x31/puppy104b2c_zps5vriy6nk.png (https://postimg.cc/image/y9y1rpth5/)

Here:- LINK REMOVED

Can both Fred and Yoda check it out [not sure if its worth the bother].

here main changes


PVideoFrame __stdcall GamMac::GetFrame(int n, IScriptEnvironment* env) {
n = (n<0) ? 0 : (n>= vi.num_frames) ? vi.num_frames - 1 : n;
CountRGB(n,cntR,cntG,cntB,env);
int in_minR = (loTh>=0.0) ? ChanMin(cntR,loTh) : 0;
int in_minG = (loTh>=0.0) ? ChanMin(cntG,loTh) : 0;
int in_minB = (loTh>=0.0) ? ChanMin(cntB,loTh) : 0;

int in_maxR = (hiTh>=0.0) ? ChanMax(cntR,hiTh) : 255;
int in_maxG = (hiTh>=0.0) ? ChanMax(cntG,hiTh) : 255;
int in_maxB = (hiTh>=0.0) ? ChanMax(cntB,hiTh) : 255;

int in_min = (loTh>=0.0) ? min(min(in_minR,in_minG),in_minB) : 0;
int in_max = (hiTh>=0.0) ? max(max(in_maxR,in_maxG),in_maxB) : 255;

if(loTh>=0.0 && loHiLock) in_minR=in_minG=in_minB=in_min;
if(hiTh>=0.0 && loHiLock) in_maxR=in_maxG=in_maxB=in_max;

double inR=ChanAve(cntR);
double inG=ChanAve(cntG);
double inB=ChanAve(cntB);

double in_scale = (Scale) ? 255.0 / (in_max - in_min) : 1.0;

double scaleR = inR * in_scale;
double scaleG = inG * in_scale;
double scaleB = inB * in_scale;

double lockval;

if(LockChan==0) {lockval=scaleR;}
else if(LockChan==1) {lockval=scaleG;}
else if(LockChan==2) {lockval=scaleB;}
else if(LockChan==-2) {lockval=((scaleR+scaleG+scaleB)/3.0);}
else if(LockChan==-3) {
double mx=max(max(scaleR,scaleG),scaleB);
double mn=min(min(scaleR,scaleG),scaleB);
lockval=scaleR+scaleG+scaleB-mx-mn;
} else {
lockval =LockVal;
}

bool skipR=(inR<MinLim || inR>MaxLim);
bool skipG=(inG<MinLim || inG>MaxLim);
bool skipB=(inB<MinLim || inB>MaxLim);
bool guessR=(in_minR!=0 || in_maxR!=255 || fabs(scaleR-lockval*RedMul)>=0.00001);
bool guessG=(in_minG!=0 || in_maxG!=255 || fabs(scaleG-lockval*GrnMul)>=0.00001);
bool guessB=(in_minB!=0 || in_maxB!=255 || fabs(scaleB-lockval*BluMul)>=0.00001);
double gammaR=(skipR || !guessR)?1.0:GuessGamma(in_minR,in_maxR,cntR,lockval*RedMul);
double gammaG=(skipG || !guessG)?1.0:GuessGamma(in_minG,in_maxG,cntG,lockval*GrnMul);
double gammaB=(skipB || !guessB)?1.0:GuessGamma(in_minB,in_maxB,cntB,lockval*BluMul);
SetGammaLut(in_minR,in_maxR, gammaR,lutR);
SetGammaLut(in_minG,in_maxG, gammaG,lutG);
SetGammaLut(in_minB,in_maxB, gammaB,lutB);

...



EDIT: Or instead of

double in_scale = (Scale) ? 255.0 / (in_max - in_min) : 1.0;

double scaleR = inR * in_scale;
double scaleG = inG * in_scale;
double scaleB = inB * in_scale;

should it be

double in_scale = (Scale) ? 255.0 / (in_max - in_min) : 1.0;

double scaleR = (inR - in_min) * in_scale;
double scaleG = (inG - in_min) * in_scale;
double scaleB = (inB - in_min) * in_scale;


with above mod
https://s20.postimg.cc/6b3w0urul/puppy104b2d_zpspojskifs.png (https://postimg.cc/image/4jax5y8hl/)

Without metrics
https://s20.postimg.cc/l8cd1v531/puppy104b2e_zpstnbddabv.png (https://postimg.cc/image/sbk8hhaih/)

Think that last mod was correct, updating soon. EDIT: Damn that bottom RHS one is good (Nice one Yoda :) ).

jmac698
20th July 2016, 22:27
Nice Job, S, beautiful work! Makes faded images look brand new.

p.s. Lena is here
https://www.cs.cmu.edu/~chuck/lennapg/lenna.shtml

There is a full scan of the original, yes it was reddish and the scarf looks to be blue.

StainlessS
20th July 2016, 23:02
Thanx very much JMac, but, me gots dem all 12/53 (Marilyn) up till bout 08.

GamMac() v1.04Beta3:- LINK REMOVED

Check it out.

https://s20.postimg.cc/ql17fzszh/Lolli_zps9zyhwmre.png (https://postimg.cc/image/736k01w1l/)

https://s20.postimg.cc/5bin29j0d/G1_zpsxdyimvnj.png (https://postimg.cc/image/6dstkt1tl/)

https://s20.postimg.cc/p7emht01p/G2_zps01ibj6qw.png (https://postimg.cc/image/bdq9sr7g9/)

https://s20.postimg.cc/cu1sawad9/G4_zpsobptunax.png (https://postimg.cc/image/hspapfe61/)

https://s20.postimg.cc/vaw71pqbh/G6_zps66okw48b.png (https://postimg.cc/image/eadat1da1/)

https://s20.postimg.cc/xthw2ec1p/G7_zpsehfpphci.png (https://postimg.cc/image/rsk75bpfd/)

https://s20.postimg.cc/cl47kyxkt/G8_zpseslldvzj.png (https://postimg.org/image/w2yv0wuih/)

EDIT: Non of the above have had any of the xxxMul settings applied.
All setting same as in first image metrics.

Waiting for orders Fred, change anything ? (then beta lifted)

StainlessS
21st July 2016, 01:38
GamMac() v1.04Beta4:- LINK REMOVED

Scale changed from Bool to Int.

GamMac(), [Gamma Machine] An extraordinary Idea by VideoFred (the gent from Gent). Coded by StainlessS.

Home Thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281
Idea:- http://forum.doom9.org/showthread.php?t=173683

RGB Only.

Useful to correct color cast on old 8mm films.
Alters channel pixel average to match LockChan using Gamma correction. (By default alters Red and Blue channels to match Green).
Additional tweaking via RedMul, GrnMul and BluMul.

GamMac(clip c,int "LockChan"=1,Float "LockVal"=128,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "MinLim"=32.0,Float "MaxLim"=255.0-32.0,float "GamLo"=0.25,Float "GamHi"=4.0,
\ Bool "Show"=false,int "Verbosity"=1
\ Float "Th"= -1.0,Float "loTh"=Th,Float "hiTh"=Th,Bool "loHiLock"=True,
\ int "Scale"=0
\ )

LockChan Default 1. Chan for lock to Ave. (0)LockVal=RedAve, (1)LockVal=GreenAve, (2)LockVal=BlueAve
-1 = Use explicit LockVal below.
-2 = LockVal=(RedAve+GrnAve+BluAve)/3.0.
-3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal, default 128.0. Ignored if LockChan != -1.0. 0.0 < LockVal < 255.0
RedMul, default 1.0. 0.1 <= RedMul <= 10.0. Red channel adjustment.
GrnMul, default 1.0. 0.1 <= GrnMul <= 10.0. Green channel adjustment.
BluMul, default 1.0. 0.1 <= BluMul <= 10.0. Blue channel adjustment.
Above Multipliers only shown in metrics when at least one is != 1.0.
MinLim, default 32.0. 0.0 < MinLim < 255.0 If any channel average smaller than this, then no effect on channel.
MaxLim, default 255.0-32.0. MinLim < MaxLim < 255.0 If any channel average greater than this, then no effect on channel.
GamLo, default 0.25. 0.1 <= GamLo <= 10.0. Lower value for guess gamma (starting guess range and limit)
GamHi, default 4.0. GamLo < GamHi <= 10.0. Upper value for guess gamma (starting guess range and limit)
Show, default false. True, show info.
Verbosity, default 1. 0=Only Upper metrics, 1(default)=Upper + important ones. 2=Full metrics.

Th, Default -1.0(off) -1.0, or 0.0 -> 2.0. Sets Default for loTh and hiTh. Suggest about 0.0, or 0.1(percent).
loTh, Default Th. -1.0, or 0.0 -> 2.0. as for Ignore_low in AutoLevels, or Threshold in YPlaneMin.
hiTh, Default Th. -1.0, or 0.0 -> 2.0. as for Ignore_high in AutoLevels, or Threshold in YPlaneMax.
Percent, amount of extreme pixels (eg noise) to ignore when finding minimum and maximum R, G or B channel values.
-1.0 is OFF, input channel minimum is set to 0 and maximum set to 255.0, as in levels(0,gamma,255, ... ).
If loTh set to eg 0.0, then will scan frame looking for minimum accumulated channel values that have more than 0.0%
of pixels, and set input_min for that channel to that value. (Accumulated:- including values that were lower but not
of great enough number to break the threshold).
If hiTh set to eg 0.0, then will scan frame looking for maximum accumulated channel values that have more than 0.0%
of pixels, and set input_max for that channel to that value.
loTh and hiTh, only shown in metrics if greater or equal to 0.0 ie switched ON.

loHiLock, Default True. If (loHiLock==true) then input_lowR = min(rmin,gmin,bmin) ELSE input_lowR = rmin
If (loHiLock==true) then input_highR = max(rmax,gmax,bmax) ELSE input_highR = rmax
Where for Red, sort of equivalent to outR=LevelsR(input_lowR,gamma,input_highR, 0,255)
Same for other channels.

Scale, default 0. Range 0 -> 2. No Effect unless Scale > 0 and loTh and/or hiTh >= 0.0.
0) No Effect.
1) Scales input channel average maximum dynamic range of R,G,B, 0.0 -> 255.0
where,
in_min = min(minR,minG,minB) and in_max = max(maxR,maxG,maxB)
inR = R channel Average, inG = G channel Average, inB = B channel Average.
in_scale = 255.0 / (in_max - in_min)
scaleR = min(max((inR - in_min) * in_scale,0.0),255.0)
scaleG = min(max((inG - in_min) * in_scale,0.0),255.0)
scaleB = min(max((inB - in_min) * in_scale,0.0),255.0)
2) Scales input channel average dynamic range of R and G and B, Individually, 0.0 -> 255.0
in_scaleR = 255.0 / (in_maxR - in_minR)
in_scaleG = 255.0 / (in_maxG - in_minG)
in_scaleB = 255.0 / (in_maxB - in_minB)
scaleR = min(max((inR - in_minR) * in_scaleR,0.0),255.0)
scaleG = min(max((inG - in_minG) * in_scaleG,0.0),255.0)
scaleB = min(max((inB - in_minB) * in_scaleB,0.0),255.0)


test

#Imagesource("GreenChurch.png",end=0)
Imagesource("Puppy.png",end=0)
#Imagesource("lennaRed.png",end=0)
#Imagesource("G1.BMP",end=0) crop(0,0,0,-48) Spline36Resize(480,360)

ConvertToRGB24
ORG=Last

LockChan = 1 # Chan for lock to Ave, 0=R, 1=G, 2=B
# -1 = Use LockVal below.
# -2 = LockVal=(RedAve+GrnAve+BluAve)/3.0 for LockVal.
# -3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal = 128 # Only valid if LockChan == -1
RedMul = 1.0 # Required Ave multiplier for Red Channel, applied when requesting GuessGamma(reqAve*RedMul), Even applied when LockChan=-1.
GrnMul = 1.0 # Same for Green channel. GrnMul even applies when LockChan is Green Channel, etc for chans.
BluMul = 1.0 # Same for Blue channel. Allows tinkering/fine tuning.
Show = true # Subtitles
th = 0.0

LoHiLockC=True Scale=0
A=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,Th=Th,SCALE=SCALE)

LoHiLockC=False Scale=1
B=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=th,LoHiLock=LoHiLockC,SCALE=Scale)

LoHiLockC=False Scale=2
C=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=th,LoHiLock=LoHiLockC,SCALE=Scale)

TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last


https://s20.postimg.cc/ucftzfczh/Pup_A_zps3qz6vncg.png (https://postimg.cc/image/inbubgm0p/)

Show=False

https://s20.postimg.cc/x7sx6agzh/Pup_B_zpsujd5pdki.png (https://postimg.cc/image/nahwd89dl/)

Bot RHS is new Scale=2 (prev Bot RHS puppy now on BOT LHS).

EDIT: I can probably move LoHiLock functionality into Scale, once I figure out how they 'interfere/interact' with each other.

tormento
21st July 2016, 11:20
I have some underwater diving photo, with red channel almost dead.

Is there any way to apply the script to images?

StainlessS
21st July 2016, 13:53
Not being magic, I do not know.

Have you tried it ?

EDIT:
Dont know much about the underwater thing, but think much of the red part of spectrum is absorbed by the water,
and weird things happen due to distance, the further away things would be further affected by red absorption
of the water between observer and the viewed object. There probably is no magic bullet due to the distance
from viewer thing, I guess all you can really do is try to correct for the object of main concern.

Here, just a quick knock up

Imagesource("Fish.BMP",end=0) Spline36Resize(480,360)
ConvertToRGB24
ORG=Last
LockChan = 1 # Chan for lock to Ave, 0=R, 1=G, 2=B
# -1 = Use LockVal below.
# -2 = LockVal=(RedAve+GrnAve+BluAve)/3.0 for LockVal.
# -3 = LockVal=Median(RedAve,GrnAve,BluAve)
LockVal = 128 # Only valid if LockChan == -1
RedMul = 1.0 # Required Ave multiplier for Red Channel, applied when requesting GuessGamma(reqAve*RedMul), Even applied when LockChan=-1.
GrnMul = 1.0 # Same for Green channel. GrnMul even applies when LockChan is Green Channel, etc for chans.
BluMul = 1.0 # Same for Blue channel. Allows tinkering/fine tuning.
Show = false # Subtitles
th = 0.0

LoHiLock=True Scale=0
A=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,Th=Th,LoHiLock=LoHiLock,SCALE=SCALE)
RedMul = 0.87
LoHiLock=false
Scale=1
B=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=th,LoHiLock=LoHiLock,SCALE=Scale)
Scale=2
C=GamMac(LockChan=LockChan,LockVal=LockVal,RedMul=RedMul,GrnMul=GrnMul,BluMul=BluMul,Show=Show,Verbosity=1,th=th,LoHiLock=LoHiLock,SCALE=Scale)
TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last


https://s20.postimg.cc/z0vtum265/Fish_1_zpsykxzqtsi.png (https://postimg.cc/image/etie2b4op/)

https://s20.postimg.cc/o2kkcfdkt/Fish_2_zpsypemjnj9.png (https://postimg.cc/image/wxlemy2d5/)

Also does not help if you got no idea what something is supposed to look like.

Here, same pic modded via something else:- http://forum.doom9.org/showthread.php?p=1726798#post1726798

EDIT: Some more stuff here:- http://forum.doom9.org/showthread.php?t=156774

EDIT: On above pic, LoHiLock would have no effect dues to all min being 0, and all max being 255.
Also, scale 2 and 3 should produce same I think (for same reason, bottom two pics are therefore probably identical,
maybe try adjust eg bluMul a little for one of them).

shekh
21st July 2016, 15:55
I made "6-axis color" for underwater thing. Not sure if it is useful with avisynth environment.
https://gfycat.com/PaleShorttermBelugawhale

StainlessS
21st July 2016, 23:00
Shekh, that dont look anything like any beluga whale to me (dont they normally look like a dolphin wearing a crash helmet ?)
https://www.google.com/search?q=beluga+whale&biw=1280&bih=839&tbm=isch&tbo=u&source=univ&sa=X&sqi=2&ved=0ahUKEwjO6MbJxoXOAhWmDcAKHcJ8BuIQiR4IiAE&dpr=1

StainlessS
21st July 2016, 23:52
tormento,

It is possible that MinLim, or MaxLim is preventing any changes on a channel.
Try something like

MinLim=0.01
MaxLim=254.99

to disable them. Is intended to prevent attempts to modify eg black or white frames.

I'll see if I can implement some kind of indicator when channel mods are disabled due to MinLim/MaxLim.
If channel mods are disabled, then will probably see Gamma=1.0 for that channel.

videoFred
22nd July 2016, 09:20
Waiting for orders Fred, change anything ? (then beta lifted)
Sorry for the delay, I was away for a few days. Tomorrow I have time to do some testing!

Fred.

tormento
22nd July 2016, 14:47
Ok... Thanks! Will try in the next days!

videoFred
25th July 2016, 09:50
First impression GamMac() v1.04Beta4 : very very good :)

A few remarks: on complete black frames, GamMac() has the same behaviour as Autolevels(). To much correction. But I use the same trick as I did with Autolevels: adding a small 4 pixels white border on the left side of the frame, then apply GamMac(), then remove the border. Examples will follow, but I have to time right now.

Sometimes on old 8mm film there is emulsion damage. This damage can have any color. Often red or green. A green spot for example (even a small one) can mess up the GamMac() results. I assume because these spots are very bright and they are changing average lume from the green channel. This can be solved by using RemoveDirt() before GamMac(). RemoveDirt() can deal very well with these spots because they are always changing from frame to frame.

Resumed: a very good (imho the best) filter for those who are working with digitized film files (8mm, 16mm or whatever format)

It might be useful for other sources too, to fix color cast etc.. but I leave this for others to test.

A happy Fred() . :)

StainlessS
25th July 2016, 13:40
GamMac() v1.05Beta1:- LINK REMOVED


GamMac(), [Gamma Machine] An extraordinary Idea by VideoFred (the gent from Gent). Coded by StainlessS.

Home Thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281
Idea:- http://forum.doom9.org/showthread.php?t=173683

RGB Only.

Useful to correct color cast on old 8mm films.
Alters channel pixel average to match LockChan using Gamma correction. (By default alters Red and Blue channels to match Green).
Additional tweaking via RedMul, GrnMul and BluMul multipliers.

What it does(roughly):-
Gets Channel averages, minimums, and maximums (using loTh for minumums and hiTh for maximums).
if(Scale > 0 and loTh>=0.0 and hiTh>=0.0) then rescale averages using dynamic range of r,g,b [0.0 -> DynamicRange(R,G,B)]
For each channel, estimate gamma function that will remap (scaled channel average * channel multiplier) to match a particular
LockVal (chosen via LockChan). Render frame using the output averages from estimated gamma.

GamMac(clip c,int "LockChan"=1,Float "LockVal"=128,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "MinLim"=10.0,Float "MaxLim"=245.0,float "GamLo"=0.1,Float "GamHi"=10.0,
\ Bool "Show"=false,int "Verbosity"=1
\ Float "Th"= -1.0,Float "loTh"=Th,Float "hiTh"=Th,
\ int "Scale"=0,
\ int "x"=0,int "y"=0,int "w"=0,int "h"=0
\ )

LockChan Default 1(Grn). Channel for lock to Average. [range -3 -> 2]
0 ] LockVal = Scaled(RedAve)
1 ] LockVal = Scaled(GreenAve)
2 ] LockVal = Scaled(BlueAve)
-1] LockVal = Use explicit LockVal arg (see below).
-2] LockVal = (Scaled(RedAve)+Scaled(GrnAve)+Scaled(BluAve))/3.0.
-3] LockVal = Median(Scaled(RedAve),Scaled(GrnAve),Scaled(BluAve))
Where Scaled(Channel Average) depends upon Scale, and loTh, and hiTh.

LockVal, default 128.0 Only used if LockChan = -1.0. [0.0 < LockVal < 255.0] (set via LockChan if LockChan != -1)

RedMul, default 1.0 Red channel multiplier adjustment. [0.1 <= RedMul <= 10.0]
GrnMul, default 1.0 Green channel multiplier adjustment. [0.1 <= GrnMul <= 10.0]
BluMul, default 1.0 Blue channel multiplier adjustment. [0.1 <= BluMul <= 10.0]
Scaled averages are multiplied by their multiplier then given as args to the gamma estimator.
Allow tweaking of R,G,B channels.
Above Multipliers only shown in metrics when at least one is != 1.0 (Always shown when Verbosity=3=FULL).

MinLim, default 10.0 If any input channel average smaller than this, then no effect on that channel. [0.0 <= MinLim <= 64.0]
MaxLim, default 245.0 If any input channel average greater than this, then no effect on that channel. [191.0 <= MaxLim <= 255.0]
Allows to skip remapping of a channel where input channel average (un-scaled) is above or below these limits.

GamLo, default 0.1 Lower value for guess gamma [0.1 <= GamLo < GamHi]
GamHi, default 10.0 Upper value for guess gamma [GamLo < GamHi <= 10.0]
Starting guess range and limits for gamma estimator (probably best left alone).

Show, default false True, show metrics info on frame.
Verbosity, default 1 0 = Only upper frame metrics
1 = Upper + important ones. (default)
2 = Nearly Full metrics.
3 = Full Metrics
Upper frame metrics shown as eg:- (when Verbosity=3=FULL)

nnnnn] Flags:- 1SL
R G B
IN: 10,253 10,253 10,253
IN_AVE: 78.466 88.552 78.767
SCALED: 71.847 82.431 72.162
GAMMA: 1.135 1.000 1.123
OUTAVE: 82.431 82.422 82.451

where,
nnnnn, is the frame number.
Flags:- (Specific to current frame, can change frame to frame)
'1' = LockChan, as above, channel '1'[ScaleAveG].
Can be, '0', '1', '2' [ScaleAve Channel number LockChan=-3(median) assigns '0', '1' or '2' as appropriate]
'A'[LockChan=-2, (ScaleAveR+ScaleAveG+ScaleAveB)/3.0]
'V'[LockChan=-1, Explicit LockVal]
'S' = Scale, mode signfied by color.
Greyed out. Scale = 0(No Effect). May be Greyed out if all channels Min/Max are 0,255.
White. Scale = 1[Scales input channel average maximum dynamic range of R,G,B]
Orange. Scale = 2[Scales input channel average dynamic range of R and G and B, Individually]
'L' = Limited by MinLim or MaxLim, mode signfied by color.
Greyed out. Not Limited
Red, at least 1 channel has remapping disabled.
IN: Shows comma separated channel minimum and maximum (dependent upon loTh, hiTh).
IN_AVE: Input channel averages.
SCALED: Scaled input averages, (dependent upon Scale, loTh, hiTh, channel minumums and maximums).
GAMMA: Estimated gamma to achieve lockval for channel. (dependent upon pretty much everything).
OUTAVE: Output channel average ie rendered result.

Th, Default -1.0(off) Sets Default for loTh and hiTh. Suggest about 0.0(percent). [-1.0, or 0.0 -> 2.0]
loTh, Default Th As for Ignore_low in AutoLevels, or Threshold in YPlaneMin. [-1.0, or 0.0 -> 2.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding minimum R, G or B channel values.
-1.0 is OFF, input channel minimum is set to 0 as for levels(0,gamma,input_max, ... ).
If loTh >=0.0, then will scan frame looking for lowest pixel value whose cumlative sum
[including all pixels counts of lower value pixels] is greater than loTh%.
loTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).
hiTh, Default Th As for Ignore_high in AutoLevels, or Threshold in YPlaneMax. [-1.0, or 0.0 -> 2.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding maximum R, G or B channel values.
-1.0 is OFF, input channel maximum set to 255, as in levels(input_min,gamma,255, ... ).
If hiTh >=0.0, then will scan frame looking for highest pixel value whose cumlative sum
[including all pixels counts of higher value pixels] is greater than hiTh%.
hiTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).

Scale, default 0 Range 0 -> 2. No Effect unless Scale > 0 and loTh and/or hiTh >= 0.0.
where described for Red Channel only:-
RedMin = Red_Channel_Minimum(ignorePerc=loTh) # Pixel minimum for red channel, ignoring up to loTh%, ie noise.
RedMax = Red_Channel_Maximum(ignorePerc=hiTh) # Pixel maximum for red channel, ignoring up to hiTh%, ie noise.
RedAve = Red_Channel_Average() # Pixel average for red Channel.
0 (Scale==0 || (loTh==-1.0 && hiTh==-1.0)) No Effect.
in_min = RedMin = GrnMin = BluMin = 0
in_max = RedMax = GrnMax = BluMax = 255
scaledAveR = RedAve
scaledAveG = GrnAve
scaledAveB = BluAve
1) Scales input channel average maximum dynamic range of R,G,B, to 0.0->(ChanAve-in_min)*255.0/(in_max-in_min+(in_max==in_min))
in_min = min(RedMin,GrnMin,BluMin)
in_max = max(RedMax,GrnMax,BluMax)
in_Rng = in_max - in_min
scaler = (in_Rng==0) ? 255.0 : 255.0 / (in_max - in_min+(in_max==in_min))
scaledAveR = min(max((RedAve - in_min) * scaler,0.0),255.0)
scaledAveG = min(max((GrnAve - in_min) * scaler,0.0),255.0)
scaledAveB = min(max((BluAve - in_min) * scaler,0.0),255.0)
2) Scales input channel average dynamic range of R & G & B, Individually, to 0.0->(ChanAve-Chan_min)*255.0/(ChanMax-ChanMin)
in_RngR = RedMax - RedMin
in_RngG = GrnMax - GrnMin
in_RngB = BluMax - BluMin
scalerR = (In_RngR==0) ? 255.0 : 255.0 / (RedMax-RedMin+(RedMax==RedMin))
scalerG = (In_RngG==0) ? 255.0 : 255.0 / (GrnMax-GrnMin+(GrnMax==GrnMin))
scalerB = (In_RngB==0) ? 255.0 : 255.0 / (BluMax-BluMin+(BluMax==BluMin))
scaledAveR = min(max((RedAve - RedMin) * scalerR,0.0),255.0)
scaledAveG = min(max((GrnAve - GrnMin) * scalerG,0.0),255.0)
scaledAveB = min(max((BluAve - BluMid) * scalerB,0.0),255.0)

x,y,w,h. All Default 0. Area of frame to sample when getting averages and estimating Gamma function, allows to ignore rubbish at frame edges.
Specified as for crop ie x=10,y=20,w=-30,h=-40, as in crop(10,20,-30,-40).


Fred, I am aware of and working on Black Frame/single color frame, problem, especially tricky if single value channel that is also lock channel.
Anyway, above most recent working version without diagnostic stuff.

https://s20.postimg.cc/ejfgwpfgd/105_B1_zpslfgxddri.png (https://postimg.cc/image/bpcbj9da1/)

EDIT: Above image (Verbosity=3 Full metrics), LockChan= -3 (Median), and in above instance red channel is the median channel and so shown in Flags as '0' red channel
for the current frame, 'S' and 'L' flags are both 'Greyed Out').

EDIT: I have to admit to a little puzzlement as to why above 'S' flag is Greyed Out when we are using Scale=1.
Well, as all channel minimums are 0, and all channel maximums are 255, there is no Scaling for this frame.
(I forgot how it works :), guess I'll havta put that in the doc. )

StainlessS
28th July 2016, 22:38
Yo Fred, here v1.06Beta1,:- LINK REMOVED

Have rearranged args to more sensible order, renamed or changed args (was necessary) and changed defaults in some cases.

If you have any problems with changes then say (although functionality may suffer if having to put back as was).

here update doc.

GamMac(), [Gamma Machine] An extraordinary Idea by VideoFred (the gent from Gent). Coded by StainlessS.

Home Thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281
Idea:- http://forum.doom9.org/showthread.php?t=173683

RGB Only.

Useful to correct color cast on old 8mm films.
Alters channel pixel average to match LockChan using Gamma correction. (By default alters Red and Blue channels to match Green).
Additional tweaking via RedMul, GrnMul and BluMul multipliers.

What it does(roughly):-
Gets Channel averages, minimums, and maximums (using loTh for minumums and hiTh for maximums).
if(Scale > 0 and loTh>=0.0 and hiTh>=0.0) then rescale averages using dynamic range of r,g,b [0.0 -> DynamicRange(R,G,B)]
For each channel, estimate gamma function that will remap (scaled channel average * channel multiplier) to match a particular
LockVal (chosen via LockChan). Render frame using the output averages from estimated gamma.

GamMac(clip c,int "LockChan"=1,int "Scale"=0,
\ Float "RedMul"=1.0,Float "GrnMul"=1.0, Float "BluMul"=1.0,
\ Float "Th"= 0.0,Float "loTh"=Th,Float "hiTh"=Th,
\ Float "LockVal"=128.0,Float "RngLim"=10.0,Float "GamMax"=10.0,
\ int "x"=0,int "y"=0,int "w"=0,int "h"=0,
\ Bool "Show"=false,int "Verbosity"=1
\ )

LockChan Default 1(Grn). Channel for lock to Average. [range -3 -> 2]
0 ] LockVal = Scaled(RedAve)
1 ] LockVal = Scaled(GreenAve)
2 ] LockVal = Scaled(BlueAve)
-1] LockVal = Use explicit LockVal arg (see below).
-2] LockVal = (Scaled(RedAve)+Scaled(GrnAve)+Scaled(BluAve))/3.0.
-3] LockVal = Median(Scaled(RedAve),Scaled(GrnAve),Scaled(BluAve))
Where Scaled(Channel Average) depends upon Scale, and loTh, and hiTh.

Scale, default 0 Range 0 -> 2. No Effect unless Scale > 0 and loTh and/or hiTh >= 0.0.
where described for Red Channel only:-
RedMin = Red_Channel_Minimum(ignorePerc=loTh) # Pixel minimum for red channel, ignoring up to loTh%, ie noise.
RedMax = Red_Channel_Maximum(ignorePerc=hiTh) # Pixel maximum for red channel, ignoring up to hiTh%, ie noise.
RedAve = Red_Channel_Average() # Pixel average for red Channel.
0 (Scale==0 || (loTh==-1.0 && hiTh==-1.0)) No Effect.
in_min = RedMin = GrnMin = BluMin = 0
in_max = RedMax = GrnMax = BluMax = 255
scaledAveR = RedAve
scaledAveG = GrnAve
scaledAveB = BluAve
1) Scales input channel average maximum dynamic range of R,G,B, to 0.0->(ChanAve-in_min)*255.0/(in_max-in_min+(in_max==in_min))
in_min = min(RedMin,GrnMin,BluMin)
in_max = max(RedMax,GrnMax,BluMax)
in_Rng = in_max - in_min
scaler = (in_Rng==0) ? 255.0 : 255.0 / (in_max - in_min)
scaledAveR = min(max((RedAve - in_min) * scaler,0.0),255.0)
scaledAveG = min(max((GrnAve - in_min) * scaler,0.0),255.0)
scaledAveB = min(max((BluAve - in_min) * scaler,0.0),255.0)
2) Scales input channel average dynamic range of R & G & B, Individually, to 0.0->(ChanAve-Chan_min)*255.0/(ChanMax-ChanMin)
in_RngR = RedMax - RedMin
in_RngG = GrnMax - GrnMin
in_RngB = BluMax - BluMin
scalerR = (In_RngR==0) ? 255.0 : 255.0 / (RedMax-RedMin)
scalerG = (In_RngG==0) ? 255.0 : 255.0 / (GrnMax-GrnMin)
scalerB = (In_RngB==0) ? 255.0 : 255.0 / (BluMax-BluMin)
scaledAveR = min(max((RedAve - RedMin) * scalerR,0.0),255.0)
scaledAveG = min(max((GrnAve - GrnMin) * scalerG,0.0),255.0)
scaledAveB = min(max((BluAve - BluMid) * scalerB,0.0),255.0)

RedMul, default 1.0 Red channel multiplier adjustment. [0.1 <= RedMul <= 10.0]
GrnMul, default 1.0 Green channel multiplier adjustment. [0.1 <= GrnMul <= 10.0]
BluMul, default 1.0 Blue channel multiplier adjustment. [0.1 <= BluMul <= 10.0]
Scaled averages are multiplied by their multiplier then given as args to the gamma estimator.
Allow tweaking of R,G,B channels.
Above Multipliers only shown in metrics when at least one is != 1.0 (Always shown when Verbosity=3=FULL).

Th, Default 0.0 Sets Default for loTh and hiTh. Suggest Default, 0.0(percent). [-1.0(OFF) , or 0.0 -> 2.0]
loTh, Default Th As for Ignore_low in AutoLevels, or Threshold in YPlaneMin. [-1.0, or 0.0 -> 2.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding minimum R, G or B channel values.
-1.0 is OFF, input channel minimum is set to 0 as for levels(0,gamma,input_max, ... ).
If loTh >=0.0, then will scan frame looking for lowest pixel value whose cumlative sum
[including all pixels counts of lower value pixels] is greater than loTh%.
loTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).
hiTh, Default Th As for Ignore_high in AutoLevels, or Threshold in YPlaneMax. [-1.0, or 0.0 -> 2.0]
Percent, amount of extreme pixels (eg noise) to ignore when finding maximum R, G or B channel values.
-1.0 is OFF, input channel maximum set to 255, as in levels(input_min,gamma,255, ... ).
If hiTh >=0.0, then will scan frame looking for highest pixel value whose cumlative sum
[including all pixels counts of higher value pixels] is greater than hiTh%.
hiTh, only shown in metrics if greater or equal to 0.0 ie switched ON (Always shown when Verbosity=3=FULL).

LockVal, default 128.0 Only used if LockChan = -1.0. [0.0 < LockVal < 255.0] (set via LockChan if LockChan != -1)
There is no restricted range on this (other than 0.0 < LockVal < 255.0), so if you set a stupid value,
you will likely get stupid results.

RngLim, default 10.0 If any input channel average is nearer than this to either channel minimum or channel maximum, then no effect on that channel.
[4.0 <= RngLim <= 32.0]
Allows to skip remapping of a channel where input channel average (un-scaled) is too close to either channel
minimum or maximum, ie avoid remapping of Black, White frames, or single color frames.

GamMax, default 10.0 Upper value for guess gamma [2.0 <= GamMax <= 10.0]
Starting guess upper range and limit for gamma estimator (probably best left alone).
The lower guess range and limit will be set to 1.0 / GamMax, by default 0.1.

x,y,w,h. All Default 0. Area of frame to sample when getting averages and estimating Gamma function, allows to ignore rubbish at frame edges.
Specified as for crop ie x=10,y=20,w=-30,h=-40, as in crop(10,20,-30,-40).

Show, default false True, show metrics info on frame.
Verbosity, default 1 0 = Only upper frame metrics
1 = Upper + important ones. (default)
2 = Nearly Full metrics.
3 = Full Metrics
Upper frame metrics shown as eg:- (when Verbosity=3=FULL)

nnnnn] Flags:- 1SR
R G B
IN: 10,253 10,253 10,253
IN_AVE: 78.466 88.552 78.767
SCALED: 71.847 82.431 72.162
GAMMA: 1.135 1.000 1.123
OUTAVE: 82.431 82.422 82.451

where,
nnnnn, is the frame number.
Flags:- (Specific to current frame, can change frame to frame)
'1' = LockChan, as above, channel '1'[ScaleAveG].
Can be, '0', '1', '2' [ScaleAve Channel number LockChan=-3(median) assigns '0', '1' or '2' as appropriate]
'A'[LockChan=-2, (ScaleAveR+ScaleAveG+ScaleAveB)/3.0]
'V'[LockChan=-1, Explicit LockVal]
'S' = Scale, mode signfied by color.
Greyed out. Scale = 0(No Effect).May be Greyed out if all channels Min/Max are 0,255.
White. Scale = 1[Scales input channel average maximum dynamic range of R,G,B]
Orange. Scale = 2[Scales input channel average dynamic range of R and G and B, Individually]
'R' = Limited by RngLim, mode signfied by color.
Greyed out. Not Range Limited.
Red, at least 1 channel has remapping disabled.
IN: Shows comma separated channel minimum and maximum (dependent upon Scale, loTh, hiTh).
IN_AVE: Input channel averages.
SCALED: Scaled input averages, (dependent upon Scale, loTh, hiTh, channel minumums and maximums).
GAMMA: Estimated gamma to achieve lockval for channel. (dependent upon pretty much everything).
OUTAVE: Output channel average ie rendered result.


Check it out with black / white / and single color frames (should leave without alteration).

Report any problems you may encounter. Thanx. :)

EDIT: Fred, here frame given random color ($204080),
https://s20.postimg.cc/tgnxxpsot/Blue_zpswxafoxan.png (https://postimg.cc/image/xd19tpdo9/)

Scale = 1, alters the frame but only non RngLim-ited channel, I could make it disable all channel alterations where a
single channel is RngLim-ited.
EDIT: Maybe I should disable all channel mods when RngLim triggered for Scale==0 and Scale==1, and only disable for
channels that trigger RngLim when Scale==2.

EDIT: Supposed Beluga Whale, with not much info in red channel.
https://s20.postimg.cc/qnuqdoscd/Beluga_zpsjchz2yqh.png (https://postimg.cc/image/ow1ris8zd/)

Scale=1 is same as Scale=0, due to combined R,G,B dynamic range being 0->255 (R min=0, G max=255),
whereas Scale=2 scales channels individually.

EDIT: Fred, I feel like the last Dalek in Dr Who, S01E06 (Eccelston), "where shall I get my orders now",
give me some orders now, what do you want ?

shekh
28th July 2016, 23:15
Supposed Beluga Whale

Joke? gfy gives random stupid names to uploads, it is just a coincidence to get whale or whatever :)

videoFred
29th July 2016, 09:23
Fred, I feel like the last Dalek in Dr Who, S01E06 (Eccelston), "where shall I get my orders now",
give me some orders now, what do you want ?

:) Haha Doktorrrrr it already does much more than I ever wanted.

Going to test v1.06Beta1 and report back here. Please give me a few days for this because I'm testing it on all kinds of scenes: over exposed, under exposed, blue cast, green cast etc... etc...

Fred.

StainlessS
29th July 2016, 13:59
Fred, I've had an idea to check for single color frame at beginning of CPP GetFrame(), and if single color then do no further scaling etc,
just show on upper metrics, maybe the input min and max and also averages, and flag to show single color 'No action', this would
make things much easier (this plugin is doin' my head in, too many alternative ways to do things, and each of the -ve LockChans
complicate and make it necessary to backtrack and undo metrics, would have been a lot simpler without the belated add-ons).

I'll knock up a quick function to demo single color frame detection for anybody to play with.
Probably will not update GamMac today.

StainlessS
29th July 2016, 16:15
Here first version using channel min, max and Average, not so successful as I had hoped.


Function IsSingleColorFrame(clip c,Int n,Float "Tol",Float "Th",String "Matrix",Int "X",Int "Y",Int"W",Int"H",Bool "Debug") {
# n = Frame Number, eg call with current_frame from ScriptClip (Best if RGB already)
c
myName="IsSingleColorFrame: "
Tol=Default(Tol,2.5) # Suggest about 4.0 to 8.0
Th=Default(Th,0.4) # Ingnore up to about 1 pixel in every 250 (noise), suggest 0.4.
Matrix=Default(Matrix,(Width>=1100||Height>=600)?"Rec709":"Rec601") # ConvertToRGB24
(!IsRGB)?ConvertToRGB24(Matrix=Matrix):NOP
Chan=-1 # R, and G, and B.
Flgs=$13 # RT_RgbChanMin, RT_RgbChanMax, RT_RgbChanAve.
Prefix="ISCF_" # Returned local vars name prefix.
RT_RgbChanStats(n,x=X,y=Y,W=W,h=H,threshold=Th,chan=Chan,flgs=Flgs,prefix=Prefix) # Simultaneous stats.
Result= (
\ (ISCF_Ave_0-ISCF_Min_0<=Tol || ISCF_Max_0-ISCF_Ave_0<=Tol) &&
\ (ISCF_Ave_1-ISCF_Min_1<=Tol || ISCF_Max_1-ISCF_Ave_1<=Tol) &&
\ (ISCF_Ave_2-ISCF_Min_2<=Tol || ISCF_Max_2-ISCF_Ave_2<=Tol)
\ )
(Debug)
\ ? RT_DebugF("RMin=%d RMax=%d RAve=%.3f : GMin=%d GMax=%d GAve=%.3f : BMin=%d BMax=%d BAve=%.3f : Result=%s",
\ ISCF_Min_0,ISCF_Max_0,ISCF_Ave_0,
\ ISCF_Min_1,ISCF_Max_1,ISCF_Ave_1,
\ ISCF_Min_2,ISCF_Max_2,ISCF_Ave_2,
\ Result,name=myName)
\ : NOP
Return Result
}

AviSource("D:\V\StarWars.avi") ConvertToRGB24
DEBUG=TRUE
INDICATOR=32

SSS="""
n=current_frame
T=IsSingleColorFrame(n,h=-INDICATOR,Debug=DEBUG)
(T)?crop(0,0,0,-INDICATOR):NOP
(T)?AddBorders(0,0,0,INDICATOR,$FF0055):NOP
return Last
"""

AddBorders(0,0,0,INDICATOR,$000000)

ScriptClip(SSS)


And one just using channel MinMaxDifference

Function IsSingleColorFrame(clip c,Int n,Float "Tol",Float "Th",String "Matrix",Int "X",Int "Y",Int"W",Int"H",Bool "Debug") {
# n = Frame Number, eg call with current_frame from ScriptClip (Best if RGB already)
c
myName="IsSingleColorFrame: "
Tol=Default(Tol,2.0) # Suggest about 4.0 to 8.0
Th=Default(Th,0.4) # Ingnore up to about 1 pixel in every 250 (noise), suggest 0.4.
Matrix=Default(Matrix,(Width>=1100||Height>=600)?"Rec709":"Rec601") # ConvertToRGB24
(!IsRGB)?ConvertToRGB24(Matrix=Matrix):NOP
Chan=-1 # R, and G, and B.
Flgs=$04 # RT_RgbChanMinMaxDifference
Prefix="ISCF_" # Returned local vars name prefix.
RT_RgbChanStats(n,x=X,y=Y,W=W,h=H,threshold=Th,chan=Chan,flgs=Flgs,prefix=Prefix) # Simultaneous stats.
Result= (
\ (ISCF_MinMaxDiff_0<=Tol) &&
\ (ISCF_MinMaxDiff_1<=Tol) &&
\ (ISCF_MinMaxDiff_2<=Tol)
\ )
(Debug)
\ ? RT_DebugF("%d %d %d : Result=%s",
\ ISCF_MinMaxDiff_0,
\ ISCF_MinMaxDiff_1,
\ ISCF_MinMaxDiff_2,
\ Result,name=myName)
\ : NOP
return Result
}

AviSource("D:\V\StarWars.avi") ConvertToRGB24

DEBUG=TRUE
INDICATOR=32

SSS="""
n=current_frame
T=IsSingleColorFrame(n,h=-INDICATOR,Debug=DEBUG)
(T)?crop(0,0,0,-INDICATOR):NOP
(T)?AddBorders(0,0,0,INDICATOR,$FF0055):NOP
return Last
"""

AddBorders(0,0,0,INDICATOR,$000000)

ScriptClip(SSS)

Will not be so noise tolerant.

Scripts show Red border at bottom of clip when single color frame detected.

We need more experimentation :(

Gotta catch my bus now.

dani75
30th July 2016, 18:30
http://studiotransfert.fr/Images/Beluga.jpg

videoFred
21st August 2016, 13:24
I have tested GamMac on all kinds of old 8mm footage by now.

My favorite fixed settings are:
----------------------------------
LockChan = 1
Verbosity = 1
Scale = 2
x=20, y=20, w=-20, h=-20 (to avoid border artefacts messing up the filter results)

These settings are variable, depending on the source:
----------------------------------------------------
LoTh: 0.04 is a good start point
HiTh : same as loTh

RedMul, GrnMul, Blumul: startvalue 1.00, then adjusting depending on the source.

I have here a Regular-8 reel from 1965. It was made in Greece and for some reason most of the colors are gone.
I assume this film was not well developed in the lab.
At the end result, the only colors left over are red and cyan.

With special thanks to Dr. Matthias Weisser and his family from Munich to make this film available for testing and publishing here.


GamMac is correcting the green cast very well, restoring true white:
http://www.super-8.be/Doom/Matt_Greece_001.jpg

A few frames further, the color cast changes suddenly to blue but the GamMac result stays the same
http://www.super-8.be/Doom/Matt_Greece_002.jpg

A few more GamMac examples:
http://www.super-8.be/Doom/Matt_Greece_004.jpg
http://www.super-8.be/Doom/Matt_Greece_005.jpg

This is my workflow: as you can see here I have changed the red and cyan hue after GamMac
http://www.super-8.be/Doom/Matt_Greece_006.jpg

Another example, only GamMac:
http://www.super-8.be/Doom/Matt_Greece_007.jpg

GamMac + color corrected:
http://www.super-8.be/Doom/Matt_Greece_008.jpg

And this last example is only GamMac:
http://www.super-8.be/Doom/Matt_Greece_009.jpg


And of cource special thanks to our friend StainlessS for making this wonderful rust proof filter. :p

Fred.