Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Usage

Reply
 
Thread Tools Search this Thread Display Modes
Old 16th July 2016, 11:16   #1  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
GamMac v1.10 - 15 June 2018

GamMac, Gamma Machine. Original idea, see here:- http://forum.doom9.org/showthread.php?t=173683

Code:
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.


Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)


Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)


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.p...94#post1825394

Last edited by StainlessS; 8th January 2019 at 03:21.
StainlessS is offline   Reply With Quote
Old 16th July 2016, 15:04   #2  |  Link
FranceBB
Broadcast Encoder
 
FranceBB's Avatar
 
Join Date: Nov 2013
Location: Royal Borough of Kensington & Chelsea, UK
Posts: 2,869
Tested and it works perfectly on very old sources!
Thank you very much indeed; it's a way easier to adjust colours this way!
FranceBB is offline   Reply With Quote
Old 16th July 2016, 15:14   #3  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
Glad you like it France, dont forget to say thanx to VideoFred for his weird & whacky idea
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???
StainlessS is offline   Reply With Quote
Old 16th July 2016, 16:24   #4  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
And here Lenna Sjööblom

Source Left. Right, LockChan 0 (red) , Bot Left LockChan=1(Grn), BotRight LockChan=2((Blu)
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 15th June 2018 at 14:25.
StainlessS is offline   Reply With Quote
Old 16th July 2016, 17:17   #5  |  Link
Bernardd
Registered User
 
Join Date: Jan 2012
Location: Toulon France
Posts: 248
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

Last edited by Bernardd; 16th July 2016 at 17:27.
Bernardd is offline   Reply With Quote
Old 16th July 2016, 17:58   #6  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
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)



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:
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 15th June 2018 at 14:25.
StainlessS is offline   Reply With Quote
Old 17th July 2016, 11:22   #7  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Terneuzen, Zeeland, the Netherlands, Europe, Earth, Milky Way,Universe
Posts: 689
Quote:
Originally Posted by StainlessS View Post
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.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest
videoFred is offline   Reply With Quote
Old 17th July 2016, 19:19   #8  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
GamMac() v1.02, update. See 1st post.

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

EDIT:
Quote:
Originally Posted by videoFred View Post
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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 17th July 2016 at 19:51.
StainlessS is offline   Reply With Quote
Old 18th July 2016, 01:36   #9  |  Link
Motenai Yoda
Registered User
 
Motenai Yoda's Avatar
 
Join Date: Jan 2010
Posts: 709
@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?
__________________
powered by Google Translator
Motenai Yoda is offline   Reply With Quote
Old 18th July 2016, 01:53   #10  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
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
Quote:
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.
Code:
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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 18th July 2016 at 03:13.
StainlessS is offline   Reply With Quote
Old 18th July 2016, 15:25   #11  |  Link
Motenai Yoda
Registered User
 
Motenai Yoda's Avatar
 
Join Date: Jan 2010
Posts: 709
Quote:
Originally Posted by StainlessS View Post
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.

Quote:
Originally Posted by StainlessS View Post
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


Quote:
Originally Posted by StainlessS View Post
[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
__________________
powered by Google Translator

Last edited by Motenai Yoda; 18th July 2016 at 15:27.
Motenai Yoda is offline   Reply With Quote
Old 18th July 2016, 16:14   #12  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
Quote:
Originally Posted by Motenai Yoda View Post
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.

Quote:
Originally Posted by Motenai Yoda View Post
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.

Quote:
Originally Posted by Motenai Yoda View Post
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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 18th July 2016 at 16:41.
StainlessS is offline   Reply With Quote
Old 18th July 2016, 16:55   #13  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Terneuzen, Zeeland, the Netherlands, Europe, Earth, Milky Way,Universe
Posts: 689
Quote:
Originally Posted by StainlessS View Post
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:

















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.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest

Last edited by videoFred; 18th July 2016 at 21:37.
videoFred is offline   Reply With Quote
Old 18th July 2016, 18:03   #14  |  Link
Sparktank
47.952fps@71.928Hz
 
Sparktank's Avatar
 
Join Date: Mar 2011
Posts: 940
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.
__________________
Win10 (x64) build 19041
NVIDIA GeForce GTX 1060 3GB (GP106) 3071MB/GDDR5 | (r435_95-4)
NTSC | DVD: R1 | BD: A
AMD Ryzen 5 2600 @3.4GHz (6c/12th, I'm on AVX2 now!)
Sparktank is offline   Reply With Quote
Old 18th July 2016, 19:41   #15  |  Link
Motenai Yoda
Registered User
 
Motenai Yoda's Avatar
 
Join Date: Jan 2010
Posts: 709
Quote:
Originally Posted by StainlessS View Post
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.
__________________
powered by Google Translator
Motenai Yoda is offline   Reply With Quote
Old 18th July 2016, 20:29   #16  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 19th July 2016 at 16:02.
StainlessS is offline   Reply With Quote
Old 18th July 2016, 21:47   #17  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Terneuzen, Zeeland, the Netherlands, Europe, Earth, Milky Way,Universe
Posts: 689
Quote:
Originally Posted by StainlessS View Post
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.

Quote:
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.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest
videoFred is offline   Reply With Quote
Old 18th July 2016, 22:18   #18  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
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.ph...91#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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???
StainlessS is offline   Reply With Quote
Old 18th July 2016, 23:18   #19  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Terneuzen, Zeeland, the Netherlands, Europe, Earth, Milky Way,Universe
Posts: 689
Quote:
Originally Posted by StainlessS View Post
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.ph...91#post1457091
Ok it might be not correct but it works as you can see on my examples.

Quote:
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.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest
videoFred is offline   Reply With Quote
Old 19th July 2016, 00:46   #20  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
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.
Code:
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:
Quote:
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.
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 9th January 2019 at 05:20.
StainlessS is offline   Reply With Quote
Reply

Tags
color cast, correction, fade, gamma

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 08:31.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.