Log in

View Full Version : Auto-adjusting Average Luma for R and B


videoFred
10th July 2016, 12:51
Hello,

As you all might know, I often work with old 8mm films. These films have often a color cast: sometimes green, sometimes blue etc..

If I split a frame to R, G and B and then look at the Average Luma, I see this:
www.super-8.be/Doom/test_RGB_Doom.jpg

But I want this: (I have set average luma manual with levels() by adjusting the gamma)
www.super-8.be/Doom/test_RGB_Doom_002.jpg

After MergeRGB(), the result is this:
http://www.super-8.be/Doom/test_RGB_Doom_003.jpg

And with some hue color tweaking I get this:
www.super-8.be/Doom/test_RGB_Doom_004.jpg (http://www.super-8.be/Doom/test_RGB_Doom_004.jpg)

With some sharpening:
www.super-8.be/Doom/test_RGB_Doom_005.jpg (http://www.super-8.be/Doom/test_RGB_Doom_005.jpg)


So the question is: is it possible to automatically set average luma on the blue and red channel according to the average luma from the green channel? And this must be done for every frame because it's constant changing.


PS: why is ... not working?

Fred.

Bernardd
10th July 2016, 15:02
Hello Fred
You can do it with Stainless's plugin RGBadapt.

For example, i have changed my script linked below, line 346 how you are waitting.

https://www.dropbox.com/s/dpl5o70iafne87h/RGBAdapt_awb_en%20-%20mod%20Fred.avs?dl=0

I have tried to make one automatic process, but i must keep args to tune. I use Avspmod "Tag selection for toggling" and "user sliders" to control script action and adjust args.

videoFred
10th July 2016, 20:22
For example, i have changed my script linked below, line 346 how you are waitting.

Thank you Bernardd! What a script... it makes me dizzy :)
Can you show me the exact lines of code where R and B gamma are adjusted to according to G gamma?


I have tried to make one automatic process, but i must keep args to tune.

Yes, I must do the same with my scripts. However, the idea to adjust R,G and B gamma to the same value (before any other color correction) looks very promising.


I use Avspmod "Tag selection for toggling" and "user sliders" to control script action and adjust args.

I do the same :)

Fred.

videoFred
10th July 2016, 21:03
Perhaps this is half of the solution: It looks like I can use RT_AverageLuma() from RT_stats to store the average luma value from the green channel into a intermediate variable. This is important because with ScriptClip() this is not possible.

Fred.

johnmeyer
10th July 2016, 21:44
I am watching this discussion closely because I too often have to correct film that looks like this (one channel faded far less than the other two). However, I'm not sure that using AverageLuma is going to get you where you want to go. I say this because when I do the manual correction in Sony Vegas that you are trying to do automatically using RT_Stats and other functions, I find that the corrections needed in the shadows are quite different from those needed in the highlights. The reason, I think, is that combining the three film color layers is not a linear function, and a different amount of correction is therefore needed in the shadows, midtones, and highlights.

A slightly different problem is also going to interfere with getting good results, namely that the three color layers in any film emulsion are most definitely not RGB, and therefore using that representation to create the corrections cannot really repair the results correctly. Instead, I think you want to create a color representation that matches exactly the three color layers used in each specific emulsion, and then use that color model as the underlying structure for your correction.

I know of software where this has been done. For instance, Ed Hammrick, the author of the popular film scanning software Vuescan (https://www.hamrick.com/) provides, within the software, profiles for every major still photo emulsion, and he does his color corrections based on the profile that matches the film being scanned.

videoFred
10th July 2016, 22:49
You are right John but average luma comes close enough for me because it's a first correction, followed by many other corrections.

And you are right about the film color layers of cource, but on the other hand: my digital files are captured in RGB, with an RGB led light system.

In my experience the best results can be achieved (after RGB, back to YV12) with tweak(hue=...,starthue=...,endhue=...) because this way you can select every specific color and tweak this color only.

On my color corrected example you will see that the vegetation on the foreground is green (tweak green range) while the vegetation on the background is more blue (tweak cyan range).

www.super-8.be/Doom/test_RGB_Doom_005.jpg (http://www.super-8.be/Doom/test_RGB_Doom_005.jpg)

Fred.

Bernardd
10th July 2016, 23:38
One year ago, on french forum http://letransfert.soforums.com/t936-Correction-des-couleurs.htm, we have discussed about color correction. After this, it was the release of StudioTransfert programm http://letransfert.soforums.com/t1022-StudioTransfert-Restauration-de-films-argentiques-et-vid-o.htm. You can find with it display and advise for manual color correction.

My script illustrate the human approach of color correction : i change something, i look and i change something else. Thus you can identify five pass. The three last pass would be in accordance with gray world theory. one pass to change gamma, one pass to change gain and one pass to change bias.

For each pass the input data is the average channel value given by StainlessS's RT_Stats plugin, then i compute the difference between the average of the sum of channel values and each channel value, the corrections are based on these differences. It is not like With auto white balance in coloryuv where corrections are based on 128 - U or V channel value. It is not scientific it is my test result.

Gamma correction first, gain correction second and bias correction third is not scientific order result, it is only a test result.

In my last post, i have a little mistake. To be in full accordance with your idea of green reference, we must change calculation of difference betwween RGB value average and channel value line 346, but also lines 396 and 433.

Can you show me the exact lines of code where R and B gamma are adjusted to according to G gamma?
Line 352 to 357. The channel difference is used like pow factor. It is no scientific, StainlessS has seen many versions. First this formula is in accordance with RGBAdapt rpow arg min max, second it is efficient (but no perfect).

I have not the knowledge to product more scientific and more smart.

Bernard

Wilbert
11th July 2016, 13:25
Perhaps this is half of the solution: It looks like I can use RT_AverageLuma() from RT_stats to store the average luma value from the green channel into a intermediate variable. This is important because with ScriptClip() this is not possible.

Fred.

FrameEvaluate(clip, "luma = AverageLuma(clip)")
#FrameEvaluate(clip, "luma = AverageLuma(clip.ShowGreen)")

videoFred
12th July 2016, 12:02
One year ago, on french forum http://letransfert.soforums.com/t936-Correction-des-couleurs.htm, we have discussed about color correction. After this, it was the release of StudioTransfert programm http://letransfert.soforums.com/t1022-StudioTransfert-Restauration-de-films-argentiques-et-vid-o.htm. You can find with it display and advise for manual color correction.

Very nice indeed! But I will go on with my own script for now.


My script illustrate the human approach of color correction

Yes, I do the same. It's the end result that counts for me.


Line 352 to 357.

I see this:

r_pord = (sr < 1.0) ? true : false
g_pord = (sg < 1.0) ? true : false
b_pord = (sb < 1.0) ? true : false


Please explain some more, I do not understand this. :o

Fred.

videoFred
12th July 2016, 12:04
FrameEvaluate(clip, "luma = AverageLuma(clip)")
#FrameEvaluate(clip, "luma = AverageLuma(clip.ShowGreen)")


Thank you for the hint, but this is not storing AverageLuma() to a variable is it?

Fred.

StainlessS
12th July 2016, 13:41
v = Avisource("StarWars.avi").Trim(10000,0)

Function g(clip c) {
global w = c
c2 = ScriptClip(c, "subtitle(t)")
c3 = FrameEvaluate(c2, "t = String(luma)")
c4 = FrameEvaluate(c3, "luma = AverageLuma(w)")
return c4
}

g(v)


FrameEvaluate/Advanced Conditional Filtering:- http://avisynth.nl/index.php/ConditionalFilter#FrameEvaluate

It can get a bit complicated. http://www.cosgan.de/images/smilie/konfus/k040.gif

EDIT: I tend to avoid that stuff if I can, RT_Stats/ScriptClip can make things more simple (for me).


Avisource("StarWars.avi").Trim(10000,0)

SSS="""
luma = AverageLuma
t = String(luma)
Subtitle(String(current_frame)+"] "+t)
"""

ScriptClip(SSS)

EDIT: Above, in v2.61, has Averageluma(offset) arg, as for below delta. (current_frame +- offset).

or with RT

Avisource("StarWars.avi").Trim(10000,0)

SSS="""
x=16 y=16 w=-16 h=-16 # Avoid crud at frame edges
cur = RT_AverageLuma( x=x,y=y,w=w,h=h)
prv = RT_AverageLuma(delta=-1,x=x,y=y,w=w,h=h)
nxt = RT_AverageLuma(delta= 1,x=x,y=y,w=w,h=h)
RT_Subtitle("%d] AveLuma=%.2f (Prev=%.2f : Next=%.2f)",current_frame,cur,prv,nxt)
"""

ScriptClip(SSS)


PS: why is ... not working?

I'm getting same, not sure think it is converting
... to [U.R.L] ... [/U.R.L]
If dont work, then edit and change to img. (Note above, could not enter the u.r.l.'s they kept being removed, so I separated with '.').
Adding the above gif link a second time (after initial post) made the link work ???

EDIT: And with GScript use (Need DebugView)

Avisource("StarWars.avi").Trim(10000,0)

# Use GSCript via ScriptClip, (current_frame set via ScriptClip before each frame call to Func and implicitly used by below RT_ funcs)
GSCript("""
Function Func(clip c,int "x",int "y",int "w",int "h",bool "DebugOnly") {
c
x=Default(x,16) y=Default(y,16) w=Default(w,-16) h=Default(h,-16) DebugOnly=Default(DebugOnly,False)
cur = RT_AverageLuma( x=x,y=y,w=w,h=h)
prv = RT_AverageLuma(delta=-1,x=x,y=y,w=w,h=h)
nxt = RT_AverageLuma(delta= 1,x=x,y=y,w=w,h=h)
# simple use of GScript within Func
if(DebugOnly) { RT_DebugF("%d] AveLuma=%.2f (Prev=%.2f : Next=%.2f)",current_frame,cur,prv,nxt,name="Func: ") }
else { RT_Subtitle("%d] AveLuma=%.2f (Prev=%.2f : Next=%.2f)",current_frame,cur,prv,nxt) }
Return Last
}
""")

ScriptClip("Func(8,8,-8,-8,False)")

Bernardd
12th July 2016, 16:55
Hello Fred

When i have written this script, i have read gray world theory and her correction based on difference between average channel value and middle (128) or near : the average of average channel value sum.

With scriptclip function i have been able to apply this theory in ColorYUV, ColorYUV2, RGBAdjust and RGBAdapt with bias whitout problem.

After bias i have tried to modify gamma with the same data. But for ColorYUV and ColorYUV2 i have not found a smart formula to modify gamma for improving the color look. With RGBAdjust and RGBAdapt, i have found idea for each channel gamma correction is pow(4.0, channel value diff/128). It is result of many tests, it is not scientific . But with this formula the color look is better after correction. RGBAdapt is more efficient than RGBAdjust, because with it we can get S curves (Thousand thanks to StainlesS). Thus with it we have four args for tune gamma action. rpow for gamma curve, spow for S shaped curve, spmid for control inflection point of shaped curve and pord to indicate if gamma curve or S shaped is computed in first.

When i have written this script, i have searched to make one automatic white balance. I have found a formula for gamma (rpow arg). No scientific, but after tests i have also found pow(4.0, channel value diff/256) for spow, args.0.5 * spow for spmid and Pord egal true if spow < 1.0. After many tests i have thought that this formulas are useful compromise for automatic white balance.

Useful but not perfect, thus i have keep for rpow et spow arg a manual tune factor to control automatic action of gamma curve and S shaped curve.

Conclusion, i think my script is like "Automated Colorization Script" http://forum.doom9.org/showthread.php?t=173364 a happy chance.

Bernard

Gavino
12th July 2016, 16:59
v = Avisource("StarWars.avi").Trim(10000,0)

Function g(clip c) {
global w = c
c2 = ScriptClip(c, "subtitle(t)")
c3 = FrameEvaluate(c2, "t = String(luma)")
c4 = FrameEvaluate(c3, "luma = AverageLuma(w)")
return c4
}

g(v)


FrameEvaluate/Advanced Conditional Filtering:- http://avisynth.nl/index.php/ConditionalFilter#FrameEvaluate

It can get a bit complicated. http://www.cosgan.de/images/smilie/konfus/k040.gif
As the wiki page goes on to say, that function is just an illustration to demonstrate the various features and can be written more simply as:
Function g(clip c) {
ScriptClip(c, """
luma = AverageLuma()
t = String(luma)
subtitle(t)
""")
}
or even:
Function g(clip c) {
ScriptClip(c, "subtitle(String(AverageLuma()))")
}
EDIT: Above, in v2.61, has Averageluma(offset) arg, as for below delta. (current_frame +- offset).
The GRunT version of AverageLuma() (and all the other run-time functions) also has the offset arg.

StainlessS
12th July 2016, 17:50
As the wiki page goes on to say, that function is just an illustration
Yep, I did not bother to repeat what it said on wiki as I thought Fred would see that anyways, it was not a real practial example, but just illustrates the weird and wonderful ways it could be used, if you were mad enough. :)


The GRunT version of AverageLuma() (and all the other run-time functions) also has the offset arg.
Yes indeed, and very useful it is too, I should have mentioned that. :stupid:

videoFred
12th July 2016, 19:06
Thank you all for the hints. ;)

But I already have a script that shows me AverageLuma() for R, G and B:
source = Avisource("E:\VDP\film0000.7.avi").trim(100,0).converttoRGB()

GR = source.ShowGreen().converttoYV12().scriptclip(""" Subtitle ("GREEN average luma: " + (string(round(AverageLuma()))),align=5,size=60)""")
RE = source.ShowRed().converttoYV12().scriptclip(""" Subtitle ("RED average luma: " + (string(round(AverageLuma()))),align=5,size=60)""")
BL = source.ShowBlue().converttoYV12().scriptclip(""" Subtitle ("BLUE average luma: " + (string(round(AverageLuma()))),align=5,size=60)""")

clip1 = stackhorizontal(source.converttoYV12(),RE)
clip2 = stackhorizontal(GR,BL)
clip3 = stackvertical(clip1, Clip2)

clip3


What I want is to adjust AverageLuma() -with levels(0, ..., 255, 255, 0) for example- from R and B to be equal to AverageLuma from G. Not manual of cource, but automatic frame by frame.

Fred.

Bernardd
13th July 2016, 00:20
Hello Fred

I understand your need.

What I want is to adjust AverageLuma() -with levels(0, ..., 255, 255, 0) for example- from R and B to be equal to AverageLuma from G. Not manual of cource, but automatic frame by frame.

But i can not explain why in this script with process on bias, the result is only near your target. (RGBADjust or RGBAdapt same result)


AViSource("C:\......\your video.avi")

LoadPlugin("C:\......\GRunT101\GRunT.dll")
LoadPlugin("C:\......\RGBAdapt_dll_v0.4-20150728\RGBAdapt.dll")
LoadPlugin("C:\......\RT_Stats_25&26_dll_v1.43_20141008\Avisynth26\RT_Stats26.dll")


matrix = Select([<"Color space matrix - (Rec 601= 0 ou Rec 709 =1) = default = 0", 0, 1, 0>], "Rec601" , "Rec709")

x = [<"Output setting - ignored the left edge - percentage of the width - default : 0.00", 0.00, 0.99, 0.0>]

Y= [<"Output setting - ignored the top edge - percentage of the height - default : 0.00", 0.00, 0.99, 0.0>]

w = [<"Output setting - ignored the right edge - percentage of the width - default : 0.00", 0.00, 0.99, 0.0>]

h = [<"Output setting - ignored the bottom edge - percentage of the height - default : 0.00", 0.00, 0.99, 0.0>]

threshold = [<"Output setting - Test setting - pixels value threshold - default : 0.00", 0.00, 1.00, 0.0>]

ScriptClip(last, """

#---------- Specify the source rectangle under scrutiny

x = int(Width(last) * x)
y = int(Height(last) * y)
w = int(-Width(last) * w)
h = int(-Height(last) * h)

#---------- Convert to RGB color space

RGB_clip = (!isRGB(last)) ? ConvertToRGB24(last, matrix=matrix) : last

#----------- RGB values extraction

RT_RgbChanStats(RGB_clip, x = x, y = y, w = w, h = h, threshold = threshold, chan = -1, flgs = 16)

#----------- RGB corrections calcul

dr_bias = RCS_Ave_1 - RCS_Ave_0
dg_bias = 0
db_bias = RCS_Ave_1 - RCS_Ave_2

output = RGBAdapt( RGB_clip, R_Bias = dr_bias, G_Bias = 0, B_Bias = db_bias)
#~ output = RGBAdjust( RGB_clip, rb = dr_bias, gb = 0, bb = db_bias)

output = RGBAdjust(output, analyze = true)

output = (!isRGB(last)) ? ConvertToYV12(output, matrix=matrix) : output

return output
""", args = " matrix, x, y, w , h, threshold")



Bernard

StainlessS
13th July 2016, 05:16
This does what you want Fred :)

Not too zippy, has to repeatedly guess at gamma until it gets it about right.
You could cut down on number of guesses by providing GamHi and GamLo args.


GSCript("""
Function GuessGamma(clip c,float reqAveLuma,float "GamHi",Float "GamLo") { # Single Frame Clip only
Assert(c.IsYV12,"GuessGamma: Requires YV12")
gamHi = Default(GamHi,2.0) gamLo = Default(GamLo,1.0/gamHi)
Result = -1.0
ALDif=0.0001
PrevAveL = -1.0
while(GamLo < gamHi) {
gamMid = (gamLo + gamHi) / 2.0
AveL=c.Levels(0,gamMid,255,0,255,coring=false).RT_AverageLuma(0)
RT_DebugF("gamLo=%f : gamHi=%f : GamMid=%f : AveL=%f",gamLo,gamHi,gamMid,AveL)
if(abs(AveL-PrevAveL)<=ALDif) {
Result = gamMid
gamLo = gamHi + 1.0 # Force Exit, Not getting any nearer
} else if(AveL < reqAveLuma) {
gamLo = gamMid
} else if(AveL > reqAveLuma) {
gamHi = gamMid
} else {
Result = gamMid
gamLo = gamHi + 1.0 # Force Exit, exact match
}
PrevAveL = AveL
}
Return Result
}
""")

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

cR = ShowRed(pixel_type="YV12")
cG = ShowGreen(pixel_type="YV12")
cB = ShowBlue(pixel_type="YV12")

#return cR.RT_Subtitle("cR AveLuma=%f",cR.RT_AverageLuma(n=0))
#return cG.RT_Subtitle("cG AveLuma=%f",cG.RT_AverageLuma(n=0))
#return cB.RT_Subtitle("cB AveLuma=%f",cB.RT_AverageLuma(n=0))

AveLumaG = cG.RT_AverageLuma(n=0)
gammaR = cR.GuessGamma(AveLumaG)
gammaB = cB.GuessGamma(AveLumaG)

fixedR=cR.Levels(0,gammaR,255,0,255,coring=false)
fixedB=cB.Levels(0,gammaB,255,0,255,coring=false)

AveLumaR=fixedR.RT_AverageLuma(n=0)
AveLumaB=fixedB.RT_AverageLuma(n=0)

SubsR=FixedR.RT_Subtitle("gammaR=%f : AveLumaR=%f",gammaR,AveLumaR)
SubsB=FixedB.RT_Subtitle("gammaB=%f : AveLumaB=%f",gammaB,AveLumaB)

Return SubsR
Return SubsB


EDIT: I'm not sure if ShowGreen.ConvertToYV12 is the correct way to do what you are doing,
Are you going to be altering gamma in RGB or YV12 Y plane only ?
If RGB, then I can mod above to use RT_RGBChanStats, just as Bernardd did above.
EDIT: RT_RGBChanStats can return RGB Channel Average, and can mod channel gamma in either RgbAdjust or RgbAdapt.


Your posted Jpeg green channel showed as AveLuma 123.923370
below results from DebugView

AveLuma of provided jpeg ShowGreen.ConvertToYV12 was 123.923370

Red Channel
00000004 05:22:53.484 RT_DebugF: gamLo=0.500000 : gamHi=2.000000 : GamMid=1.250000 : AveL=99.477219
00000005 05:22:53.484 RT_DebugF: gamLo=1.250000 : gamHi=2.000000 : GamMid=1.625000 : AveL=121.803970
00000006 05:22:53.484 RT_DebugF: gamLo=1.625000 : gamHi=2.000000 : GamMid=1.812500 : AveL=130.938644
00000007 05:22:53.484 RT_DebugF: gamLo=1.625000 : gamHi=1.812500 : GamMid=1.718750 : AveL=126.541656
00000008 05:22:53.500 RT_DebugF: gamLo=1.625000 : gamHi=1.718750 : GamMid=1.671875 : AveL=124.224213
00000009 05:22:53.500 RT_DebugF: gamLo=1.625000 : gamHi=1.671875 : GamMid=1.648438 : AveL=122.975197
00000010 05:22:53.500 RT_DebugF: gamLo=1.648438 : gamHi=1.671875 : GamMid=1.660156 : AveL=123.602821
00000011 05:22:53.500 RT_DebugF: gamLo=1.660156 : gamHi=1.671875 : GamMid=1.666016 : AveL=123.879623
00000012 05:22:53.500 RT_DebugF: gamLo=1.666016 : gamHi=1.671875 : GamMid=1.668945 : AveL=124.071327
00000013 05:22:53.500 RT_DebugF: gamLo=1.666016 : gamHi=1.668945 : GamMid=1.667480 : AveL=123.904846
00000014 05:22:53.500 RT_DebugF: gamLo=1.667480 : gamHi=1.668945 : GamMid=1.668213 : AveL=123.938972
00000015 05:22:53.500 RT_DebugF: gamLo=1.667480 : gamHi=1.668213 : GamMid=1.667847 : AveL=123.936470
00000016 05:22:53.515 RT_DebugF: gamLo=1.667480 : gamHi=1.667847 : GamMid=1.667664 : AveL=123.909752
00000017 05:22:53.515 RT_DebugF: gamLo=1.667664 : gamHi=1.667847 : GamMid=1.667755 : AveL=123.932060
00000018 05:22:53.515 RT_DebugF: gamLo=1.667664 : gamHi=1.667755 : GamMid=1.667709 : AveL=123.932060

Oops, below is for Blue
00000019 05:22:53.515 RT_DebugF: gamLo=0.500000 : gamHi=2.000000 : GamMid=1.250000 : AveL=145.483154
00000020 05:22:53.515 RT_DebugF: gamLo=0.500000 : gamHi=1.250000 : GamMid=0.875000 : AveL=117.127487
00000021 05:22:53.515 RT_DebugF: gamLo=0.875000 : gamHi=1.250000 : GamMid=1.062500 : AveL=132.873169
00000022 05:22:53.531 RT_DebugF: gamLo=0.875000 : gamHi=1.062500 : GamMid=0.968750 : AveL=125.270378
00000023 05:22:53.531 RT_DebugF: gamLo=0.875000 : gamHi=0.968750 : GamMid=0.921875 : AveL=121.301468
00000024 05:22:53.531 RT_DebugF: gamLo=0.921875 : gamHi=0.968750 : GamMid=0.945313 : AveL=123.464508
00000025 05:22:53.531 RT_DebugF: gamLo=0.945313 : gamHi=0.968750 : GamMid=0.957031 : AveL=124.378815
00000026 05:22:53.531 RT_DebugF: gamLo=0.945313 : gamHi=0.957031 : GamMid=0.951172 : AveL=123.794090
00000027 05:22:53.531 RT_DebugF: gamLo=0.951172 : gamHi=0.957031 : GamMid=0.954102 : AveL=124.262619
00000028 05:22:53.531 RT_DebugF: gamLo=0.951172 : gamHi=0.954102 : GamMid=0.952637 : AveL=123.990211
00000029 05:22:53.531 RT_DebugF: gamLo=0.951172 : gamHi=0.952637 : GamMid=0.951904 : AveL=123.880852
00000030 05:22:53.546 RT_DebugF: gamLo=0.951904 : gamHi=0.952637 : GamMid=0.952271 : AveL=123.932320
00000031 05:22:53.546 RT_DebugF: gamLo=0.951904 : gamHi=0.952271 : GamMid=0.952087 : AveL=123.900612
00000032 05:22:53.546 RT_DebugF: gamLo=0.952087 : gamHi=0.952271 : GamMid=0.952179 : AveL=123.919975
00000033 05:22:53.546 RT_DebugF: gamLo=0.952179 : gamHi=0.952271 : GamMid=0.952225 : AveL=123.919975

videoFred
13th July 2016, 10:59
This does what you want Fred :)
Thank you StainlessS ;)

I will have a very close look at this.


EDIT: I'm not sure if ShowGreen.ConvertToYV12 is the correct way to do what you are doing,

Well... I had to! ShowGreen only works in RGB and Scriptclip only works in YV12.


Are you going to be altering gamma in RGB or YV12 Y plane only ?

In RGB, if possible.

Fred.

videoFred
13th July 2016, 11:05
But i can not explain why in this script with process on bias

Thank you too Bernardd ;)

But individual RGB bias can be set very easy with Autolevels() separately on the R, G and B channels, then merge them back with MergeRGB().

Fred.

Reel.Deel
13th July 2016, 11:07
EDIT: I'm not sure if ShowGreen.ConvertToYV12 is the correct way to do what you are doing,

Well... I had to! ShowGreen only works in RGB and Scriptclip only works in YV12.

ShowGreen("YV12") is faster and lossless.

StainlessS
13th July 2016, 11:14
and Scriptclip only works in YV12.

Nah, not ruddy likely :)

I'll convert to RGB.

StainlessS
13th July 2016, 12:26
OK Fred here we go, colorspace agnostic.


Function ChanAve(clip c,int chan,int "n") {
# RT_ChanAve is colorspace agnostic, returns number of channels.
# Local vars result default Prefixed "RCA_", default chan0 local var = RCA_Ave_0. Chan 0=Red/Y, 1=Grn/U, 2=Blue/V
nChannels=c.RT_ChanAve(n=Default(n,0)) # nChannels unused
Return (chan==0)?RCA_Ave_0:(chan==1)?RCA_Ave_1:RCA_Ave_2
}

Function GuessGamma(clip c,int Chan,int "n",float "reqAve",float "GamHi",Float "GamLo",Bool "Debug") {
# Chan, 0=Red/Y, 1=Grn/U, 2=Blue/V
n=Default(n,0) reqAve=Default(reqAve,128.0) gamHi = Default(GamHi,2.0) gamLo = Default(GamLo,1.0/gamHi)
Debug=Default(Debug,True) n = min(max(n,0),c.FrameCount-1) c=c.Trim(n,-1)
Result = -1.0 ADif=0.0001 PrevAve = -1.0
GSCript("""
while(GamLo < gamHi) {
gamMid = (gamLo + gamHi) / 2.0
Ave = c.Levels(0,gamMid,255,0,255,coring=false).ChanAve(chan,0)
(Debug)?RT_DebugF("gamLo=%f : gamHi=%f : GamMid=%f : Ave=%f",gamLo,gamHi,gamMid,Ave):NOP
if(abs(Ave-PrevAve)<=ADif) {Result = gamMid gamLo = gamHi + 1.0} # Force Exit, Not getting any nearer
else if(Ave < reqAve) {gamLo = gamMid}
else if(Ave > reqAve) {gamHi = gamMid}
PrevAve = Ave
}
""")
Return Result
}

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

R=0 G=1 B=2

cR = ShowRed
cG = ShowGreen
cB = ShowBlue

#return cR.RT_Subtitle("cR Ave=%f",cR.ChanAve(R))
#return cG.RT_Subtitle("cG Ave=%f",cG.ChanAve(G))
#return cB.RT_Subtitle("cB Ave=%f",cB.ChanAve(B))

AveG = cG.ChanAve(G,n=0)
gammaR = cR.GuessGamma(R,0,reqAve=AveG)
gammaB = cB.GuessGamma(B,0,reqAve=AveG)

fixedR=cR.Levels(0,gammaR,255,0,255,coring=false)
fixedB=cB.Levels(0,gammaB,255,0,255,coring=false)

AveR=fixedR.ChanAve(R)
AveB=fixedB.ChanAve(B)

SubsR=FixedR.RT_Subtitle("gammaR=%f : AveR=%f",gammaR,AveR)
SubsB=FixedB.RT_Subtitle("gammaB=%f : AveB=%f",gammaB,AveB)

Return SubsR
Return SubsB


Oops, little fix.

EDIT: Fred, what you mean by "Scriptclip only works in YV12", what dont work ?
EDIT: Arh, you mean builtin AverageLuma only works in YV12, then use something else (RT_ maybe where necessary).
EDIT: Removed Eval.

@Reel.Deel: ShowGreen("YV12") is faster and lossless.
thanks fixed, about 05:00 am, brain weren't workin' proper.
EDIT: Oops, nuther fix.

videoFred
13th July 2016, 20:45
EDIT: Fred, what you mean by "Scriptclip only works in YV12", what dont work ?

Answer:

EDIT: Arh, you mean builtin AverageLuma only works in YV12
Yes :)
Sorry, I have no experience with the runtime environment so I tought it was ScriptClip itself. :o

This was not working:
Return SubsR
Return SubsB

I had to remove remove the "return" because no matter what I did, it always returned SubsR.

This is working fine:
SubsR
SubsB

It returns the clip in the last line, in this case SubsB but I can return all intermediate clips from your script this way.

Anyhow, it works fine but: I'm using it on a clip, not on a single image. So the values are the same for the entire clip. Do we have the average luma from the first frame for the entire clip here?

Fred.

StainlessS
14th July 2016, 15:07
For the lines below, you un-comment ONLY one of them, un-comment both will of course always return the first one ie red.

#Return SubsR
#Return SubsB


OK, I'll quickly knock up scriptclip version working on entire clip. [It is not limited to single frame now, just call with frame number (current_frame) from within scriptclip].

EDIT: Not sure but I think that the 2nd oops fix mentioned in script post might have been to fix the return problem mentioned, so
was probably my fault, you probably tested prior to fix version.

videoFred
14th July 2016, 16:16
OK, I'll quickly knock up scriptclip version working on entire clip. [It is not limited to single frame now, just call with frame number (current_frame) from within scriptclip].

Fine! Looking forwards and by this I'm learning how to use the runtime environtment. It's not so simple if you never have done it before. :o


EDIT: Not sure but I think that the 2nd oops fix mentioned in script post might have been to fix the return problem mentioned, so
was probably my fault, you probably tested prior to fix version.

Yes, first thing I did was leaving subsB and un-comment subsR but it always returned subsR anyhow. Anyhow, it's fixed now.

Fred.

StainlessS
14th July 2016, 17:00
OK Fred, wanna give this a whirl (its not exactly smooth running though):-


Function ChanAve(clip c,int chan,int "n") {
# RT_ChanAve is colorspace agnostic, returns number of channels.
# Local vars result default Prefixed "RCA_", default chan0 local var = RCA_Ave_0. Chan 0=Red/Y, 1=Grn/U, 2=Blue/V
nChannels=c.RT_ChanAve(n=Default(n,0)) # nChannels unused
Return (chan==0)?RCA_Ave_0:(chan==1)?RCA_Ave_1:RCA_Ave_2
}

GSCript("""
Function GuessGamma(clip c,int Chan,int "n",float "reqAve",float "GamHi",Float "GamLo",Bool "Debug") {
# Chan, 0=Red/Y, 1=Grn/U, 2=Blue/V
n=min(max(Default(n,0),0),c.FrameCount-1) c=c.Trim(n,-1)
reqAve=Default(reqAve,128.0) gamHi = Default(GamHi,2.0) gamLo = Default(GamLo,1.0/gamHi)
Debug=Default(Debug,False)
Result = -1.0 ADif=0.0001 PrevAve = -1.0
while(GamLo < gamHi) {
gamMid = (gamLo + gamHi) / 2.0
Ave = c.Levels(0,gamMid,255,0,255,coring=false).ChanAve(chan,0)
(Debug)?RT_DebugF("gamLo=%f : gamHi=%f : GamMid=%f : Ave=%f",gamLo,gamHi,gamMid,Ave):NOP
if(abs(Ave-PrevAve)<=ADif) {Result = gamMid gamLo = gamHi + 1.0} # Force Exit, Not getting any nearer
else if(Ave < reqAve) {gamLo = gamMid}
else if(Ave > reqAve) {gamHi = gamMid}
PrevAve = Ave
}
Return Result
}

Function GammaFixFrame(clip c,int n,float "GamHi",Float "GamLo",Bool "Debug") {
n=min(max(n,0),c.FrameCount-1) c=c.Trim(n,-1)
cR = c.ShowRed cG = c.ShowGreen cB = c.ShowBlue
AveG = cG.ChanAve(1)
gammaR = cR.GuessGamma(0,reqAve=AveG,GamHi=GamHi,GamLo=GamLo)
gammaB = cB.GuessGamma(2,reqAve=AveG,GamHi=GamHi,GamLo=GamLo)
fixedR = cR.Levels(0,gammaR,255,0,255,coring=false)
fixedB = cB.Levels(0,gammaB,255,0,255,coring=false)
MergeRGB(fixedR,cG,fixedB)
if(Debug) {
AveR = fixedR.ChanAve(0)
AveB = fixedB.ChanAve(2)
RT_Subtitle("%d]\nIN_AVE: R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "GAMMA : R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "OUTAVE: R=%6.2f : G=%6.2f : B=%6.2f",
\ n,cR.ChanAve(0),AveG,cB.ChanAve(2),
\ gammaR,1.0,gammaB,
\ AveR,AveG,AveB)
}
Return Last
}
""") # End of GScript


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

ConvertToRGB32
ORG=Last

GamHi=2.0
GamLo=0.5
DEBUG=True

ScriptClip("GammaFixFrame(Last,current_frame,GamHi,GamLo,Debug)",args="GamHi,GamLo,Debug")

StackHorizontal(ORG,Last)

return Last


EDIT: Requires Grunt(), due to use of 'Args' in ScriptClip call (as well ar RT_ and GScript).

EDIT: In the test clip I used, all three channels were over 128, would it be useful to lock all three (incl green) to Ave 128 instead ?

StainlessS
14th July 2016, 18:22
would it be useful to lock all three (incl green) to Ave 128 instead ?

Implemented here ("as well as" NOT "instead of")

Function ChanAve(clip c,int chan,int "n") {
# RT_ChanAve is colorspace agnostic, returns number of channels.
# Local vars result default Prefixed "RCA_", default chan0 local var = RCA_Ave_0. Chan 0=Red/Y, 1=Grn/U, 2=Blue/V
nChannels=c.RT_ChanAve(n=Default(n,0)) # nChannels unused
Return (chan==0)?RCA_Ave_0:(chan==1)?RCA_Ave_1:RCA_Ave_2
}

GSCript("""
Function GuessGamma(clip c,int Chan,int "n",float "reqAve",float "GamHi",Float "GamLo",Bool "Debug") {
# Chan, 0=Red/Y, 1=Grn/U, 2=Blue/V
n=min(max(Default(n,0),0),c.FrameCount-1) c=c.Trim(n,-1)
reqAve=Default(reqAve,128.0) gamHi = Default(GamHi,2.0) gamLo = Default(GamLo,1.0/gamHi)
Debug=Default(Debug,False)
Result = -1.0 ADif=0.0001 PrevAve = -1.0
while(GamLo < gamHi) {
gamMid = (gamLo + gamHi) / 2.0
Ave = c.Levels(0,gamMid,255,0,255,coring=false).ChanAve(chan,0)
(Debug)?RT_DebugF("gamLo=%f : gamHi=%f : GamMid=%f : Ave=%f",gamLo,gamHi,gamMid,Ave):NOP
if(abs(Ave-PrevAve)<=ADif) {Result = gamMid gamLo = gamHi + 1.0} # Force Exit, Not getting any nearer
else if(Ave < reqAve) {gamLo = gamMid}
else if(Ave > reqAve) {gamHi = gamMid}
PrevAve = Ave
}
Return Result
}

Function GammaFixFrame(clip c,int n,int "LockChan",Float "LockVal",float "GamHi",Float "GamLo",Bool "Debug") {
n=min(max(n,0),c.FrameCount-1)
LockChan=Default(LockChan,1) # Default Green, -1=Use LockVal ALL channels, 0 = Red, 1=Green, 2=Blue
Assert(LockChan>=-1 && LockChan<=2,"GammaFixFrame: LockChan -1 -> 2 Only")
LockVal=(LockChan>=0)?0.0:Default(LockVal,128)
c=c.Trim(n,-1) cR=c.ShowRed cG=c.ShowGreen cB=c.ShowBlue
c.RT_ChanAve(n=0,Prefix="In_") # Simultaneous get all three averages
LockVal= (LockChan==0)?In_Ave_0:(LockChan==1)?In_Ave_1:(LockChan==2)?In_Ave_2:LockVal
gammaR = (LockChan==0)?1.0:cR.GuessGamma(0,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
gammaG = (LockChan==1)?1.0:cG.GuessGamma(1,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
gammaB = (LockChan==2)?1.0:cB.GuessGamma(2,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
fixedR = (LockChan==0)?cR:cR.Levels(0,gammaR,255,0,255,coring=false)
fixedG = (LockChan==1)?cG:cG.Levels(0,gammaG,255,0,255,coring=false)
fixedB = (LockChan==2)?cB:cB.Levels(0,gammaB,255,0,255,coring=false)
MergeRGB(fixedR,fixedG,fixedB)
if(Debug) {
RT_ChanAve(n=0) # Simultaneous get all three averages
RT_Subtitle("%d]\nIN_AVE: R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "GAMMA : R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "OUTAVE: R=%6.2f : G=%6.2f : B=%6.2f",
\ n,In_Ave_0,In_Ave_1,In_Ave_2,
\ gammaR,gammaG,gammaB,
\ RCA_Ave_0,RCA_Ave_1,RCA_Ave_2)
RT_Subtitle("Lockchan=%d LockVal=%.2f\nGamHi=%.2f GamLo=%.2f",LockChan,LockVal,GamHi,GamLo,align=1)
}
Return Last
}
""") # End of GScript


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

ConvertToRGB32
ORG=Last

LockChan = -1 # Chan for lock to Ave, 0=R, 1=G, 2=B, -1 = Use LockVal below
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)
DEBUG = True # Subtitles

ScriptClip("GammaFixFrame(Last,current_frame,LockChan,LockVal,GamHi,GamLo,Debug)",args="LockChan,LockVal,GamHi,GamLo,Debug")
StackHorizontal(ORG,Last)

return Last



EDIT: Tiny mod (Simultaneous get all three averages)
EDIT: Nuther small mod.

EDIT: Fred, would you have any ideas about limiting on eg on black/white frames ?
(eg if orig channel ave above/below some value then do not attempt fix with gamma).

StainlessS
14th July 2016, 19:56
Added limit for gamma mod as in last edit above.


Function ChanAve(clip c,int chan,int "n") {
# RT_ChanAve is colorspace agnostic, returns number of channels.
# Local vars result default Prefixed "RCA_", default chan0 local var = RCA_Ave_0. Chan 0=Red/Y, 1=Grn/U, 2=Blue/V
nChannels=c.RT_ChanAve(n=Default(n,0)) # nChannels unused
Return (chan==0)?RCA_Ave_0:(chan==1)?RCA_Ave_1:RCA_Ave_2
}

GSCript("""
Function GuessGamma(clip c,int Chan,int "n",float "reqAve",float "GamHi",Float "GamLo",Bool "Debug") {
# Chan, 0=Red/Y, 1=Grn/U, 2=Blue/V
n=min(max(Default(n,0),0),c.FrameCount-1) c=c.Trim(n,-1)
reqAve=Default(reqAve,128.0) gamHi = Default(GamHi,2.0) gamLo = Default(GamLo,1.0/gamHi)
Debug=Default(Debug,False)
Result = -1.0 ADif=0.0001 PrevAve = -1.0
while(GamLo < gamHi) {
gamMid = (gamLo + gamHi) / 2.0
Ave = c.Levels(0,gamMid,255,0,255,coring=false).ChanAve(chan,0)
(Debug)?RT_DebugF("gamLo=%f : gamHi=%f : GamMid=%f : Ave=%f",gamLo,gamHi,gamMid,Ave):NOP
if(abs(Ave-PrevAve)<=ADif) {Result = gamMid gamLo = gamHi + 1.0} # Force Exit, Not getting any nearer
else if(Ave < reqAve) {gamLo = gamMid}
else if(Ave > reqAve) {gamHi = gamMid}
PrevAve = Ave
}
Return Result
}

Function GammaFixFrame(clip c,int n,int "LockChan",Float "LockVal",float "GamHi",Float "GamLo",Float "MinLim",Float "MaxLim",Bool "Debug") {
n=min(max(n,0),c.FrameCount-1)
LockChan=Default(LockChan,1) # Default Green, -1=Use LockVal ALL channels, 0 = Red, 1=Green, 2=Blue
MinLim=Default(MinLim,32.0) MaxLim=Default(MaxLim,255.0-32.0)
Assert(LockChan>=-1 && LockChan<=2,"GammaFixFrame: LockChan -1 -> 2 Only")
LockVal=(LockChan>=0)?0.0:Default(LockVal,128)
c=c.Trim(n,-1) cR=c.ShowRed cG=c.ShowGreen cB=c.ShowBlue
c.RT_ChanAve(n=0,Prefix="In_") # Simultaneous get all three averages
LockVal= (LockChan==0)?In_Ave_0:(LockChan==1)?In_Ave_1:(LockChan==2)?In_Ave_2:LockVal
OffR=(LockChan==0 || In_Ave_0<MinLim || In_Ave_0>MaxLim)
OffG=(LockChan==1 || In_Ave_1<MinLim || In_Ave_1>MaxLim)
OffB=(LockChan==2 || In_Ave_2<MinLim || In_Ave_2>MaxLim)
gammaR = (OFFR)?1.0:cR.GuessGamma(0,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
gammaG = (OFFG)?1.0:cG.GuessGamma(1,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
gammaB = (OFFB)?1.0:cB.GuessGamma(2,reqAve=LockVal,GamHi=GamHi,GamLo=GamLo)
fixedR = (OFFR)?cR:cR.Levels(0,gammaR,255,0,255,coring=false)
fixedG = (OFFG)?cG:cG.Levels(0,gammaG,255,0,255,coring=false)
fixedB = (OFFB)?cB:cB.Levels(0,gammaB,255,0,255,coring=false)
MergeRGB(fixedR,fixedG,fixedB)
if(Debug) {
RT_ChanAve(n=0) # Simultaneous get all three averages
RT_Subtitle("%d]\nIN_AVE: R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "GAMMA : R=%6.2f : G=%6.2f : B=%6.2f\n" +
\ "OUTAVE: R=%6.2f : G=%6.2f : B=%6.2f",
\ n,In_Ave_0,In_Ave_1,In_Ave_2,
\ gammaR,gammaG,gammaB,
\ RCA_Ave_0,RCA_Ave_1,RCA_Ave_2)
RT_Subtitle("Lockchan=%d LockVal=%.2f\nGamHi=%.2f GamLo=%.2f\nMinLim=%.2f MaxLim=%.2f",
\ LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,align=1)
}
Return Last
}
""") # End of GScript


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

#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


ConvertToRGB32
ORG=Last

LockChan = -1 # Chan for lock to Ave, 0=R, 1=G, 2=B, -1 = Use LockVal below
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)
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.
DEBUG = true # Subtitles

ScriptClip("GammaFixFrame(Last,current_frame,LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug)",args="LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug")
StackHorizontal(ORG,Last)

return Last


EDIT: Oops, I broke it, fixed.

EDIT: Just tried with your original sample frame, works pretty darn good whether LockChan=1, OR LockChan= -1, LockVal=128.
(Original Green channel ave shows as 125.66 )
Doubt if universal fix, but certainly works pretty well with that clip.
https://s20.postimg.cc/7yyg1rgcd/fix_Frame_zpslim2zd6s.png (https://postimg.cc/image/54vaobe61/)

EDIT: Added MinLim, MaxLim to Debug output bottom left (not shown in above graphic).

EDIT: Looks like it is only really changing red gamma significantly.

videoFred
14th July 2016, 20:37
Just tried with your original sample frame, works pretty darn good whether LockChan=1, OR LockChan= -1, LockVal=128.
(Original Green channel ave shows as 125.66)
Doubt if universal fix, but certainly works pretty well with that clip.

I have done a few fast tests and it looks pretty darn good on all kinds of clips StainlessS ! :thanks:

I have no time now but more examples will follow within a few days.

Fred.

StainlessS
14th July 2016, 23:17
From what I've just read on net, Magenta pigment (-green) is most stable, and as film tends to turn Red, then suggests that
Cyan layer is most unstable, with Yellow (-Blue) being somewhere in between.

Also seems to suggest that some types of film are more robust and do not deteriorate (as much) over time.
I think it also said that Eastman Kodak was very prone to fade to Red appearance, would your films be of such type (EK) ?

EDIT: This might be of interest to some:

Guide to Identifying Color Movie Film Stocks
http://www.paulivester.com/films/filmstock/guide.htm

johnmeyer
15th July 2016, 02:46
From quite a bit of experience transferring old amateur color movies, many dating to the mid-1930s, I can confirm that the most common problem is film that has turned to "red and white." I don't have a before/after clip handy, but here is some footage from the late 1930s that initially looked entirely red and white. I was able to retrieve a little color by killing most of the red channel and boosting the other two:

1937 Lund Utah 16mm Film (https://www.youtube.com/watch?v=OwNXV84ztHw)

I did this manually, and applied the same correction to the entire clip. However, I am intrigued by what StainlessS and VideoFred are developing here, and think that it might have a lot of application. That test image looks pretty darned good to me.

I have also seen film that is "blue and white" and "yellow and white." I think the main reason that red and white is so common is that Kodak manufactured stock used for theatrical prints which was, by design, cheaply made, with no thought to long-term stability. Because this stock was manufactured in such huge quantities (every movie had to be duplicated to be shown in tens of thousands of theaters worldwide) it became really cheap and was sold to the public at discount rates. In the modern era, many of you will remember Seattle Filmworks who sold this stock and would then make both slides and prints from the same image. Those slides lasted about two years before they deteriorated.

The one film stock that is rock solid is Kodachrome. What a stunning technical achievement it was. Here's Kodachrome from only three years after that other sample. This film was stored in the attic of a garage in Wisconsin for seventy years, withstanding the 140+ degree attic heat in the summer and -20 temperatures in the winter. The color is amazing:

1941 Flint Michigan Parade (https://www.youtube.com/watch?v=t8HjRN0rw5M)

StainlessS
15th July 2016, 12:14
Actually, seems I got that back-to-front.
Fred's pic problem seems to be red channel but its not a red cast its cyan cast ???
EDIT: Instead of cyan layer fading and not absorbing enough red light (red cast), seems to be
absorbing more red light than it should (cyan cast).

Anyways, with this added to end of script

LockChan = 0 # Chan for lock to Ave, 0=R, 1=G, 2=B, -1 = Use LockVal below
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)
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.
DEBUG = true # Subtitles

R=ScriptClip("GammaFixFrame(Last,current_frame,LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug)",args="LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug")
LockChan = 1
G=ScriptClip("GammaFixFrame(Last,current_frame,LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug)",args="LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug")
LockChan = 2
B=ScriptClip("GammaFixFrame(Last,current_frame,LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug)",args="LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,Debug")
TOP=StackHorizontal(ORG,R)
BOT=StackHorizontal(G,B)
StackVertical(TOP,BOT)
return Last


Here Johns red clip
https://s20.postimg.cc/jpcdj594t/quad_zpsnjjhd9ve.png (https://postimg.cc/image/5iwmnwy9l/)

And another from same.
https://s20.postimg.cc/jdux6dsot/quad2_zpsyfrvctzg.png (https://postimg.cc/image/9gjwdbl2x/)

And the good one even looks a little red (perhaps below are a little over corrected)
https://s20.postimg.cc/ku6foivlp/Horsey_zpsxnxjatlo.png (https://postimg.cc/image/eghcl9qpl/)

StainlessS
15th July 2016, 13:58
Fred, bit of a mod, judge for yourself if of any worth.
Added RedMul, GrnMul, BluMul, fine tinkering.


GSCript("""
Function ChanAve(clip c,int chan,int "n") {
# RT_ChanAve is colorspace agnostic, returns number of channels.
# Local vars result default Prefixed "RCA_", default chan0 local var = RCA_Ave_0. Chan 0=Red/Y, 1=Grn/U, 2=Blue/V
nChannels=c.RT_ChanAve(n=Default(n,0)) # nChannels unused
Return (chan==0)?RCA_Ave_0:(chan==1)?RCA_Ave_1:RCA_Ave_2
}

Function GuessGamma(clip c,int Chan,int "n",float "reqAve",Float "GamLo",float "GamHi",Bool "Debug") {
# Chan, 0=Red/Y, 1=Grn/U, 2=Blue/V
n=min(max(Default(n,0),0),c.FrameCount-1) c=c.Trim(n,-1)
reqAve=Default(reqAve,128.0) gamLo = Default(GamLo,1.0/4.0) gamHi = Default(GamHi,4.0) Debug=Default(Debug,true)
Result = -1.0 ADif=0.0001 PrevAve = -1.0
while(GamLo < GamHi) {
gamMid = (gamLo + gamHi) / 2.0
Ave = c.Levels(0,gamMid,255,0,255,coring=false).ChanAve(chan,0)
(Debug)?RT_DebugF("Chan=%d gamLo=%f : gamHi=%f : GamMid=%f : Ave=%f",chan,gamLo,gamHi,gamMid,Ave):NOP
if(abs(Ave-PrevAve)<=ADif) {Result = gamMid gamLo = gamHi + 1.0} # Force Exit, Not getting any nearer
else if(Ave < reqAve) {gamLo = gamMid}
else if(Ave > reqAve) {gamHi = gamMid}
PrevAve = Ave
}
Return Result
}

Function GamMac(clip c,int n,int "LockChan",Float "LockVal",
\ Float "RedMul",Float "GrnMul", Float "BluMul",
\ Float "MinLim",Float "MaxLim",float "GamLo",Float "GamHi",Bool "Show",int "Verbosity") {
n=min(max(n,0),c.FrameCount-1)
LockChan=Default(LockChan,1) # Default Green, 0 = Red, 1=Green, 2=Blue. -1=Use LockVal ALL channels. -2 use (RedAve+GrnAve+BluAVE)/3.0 for LockVal.
MinLim=Default(MinLim,32.0) MaxLim=Default(MaxLim,255.0-32.0) Show=Default(Show,False) Verbosity=Default(Verbosity,1)
Assert(LockChan>=-3 && LockChan<=3,"GamMac: LockChan -3 -> 2 Only")
LockVal=(LockChan>=0)?0.0:Float(Default(LockVal,128.0))
RedMul=Default(RedMul,1.0) GrnMul=Default(GrnMul,1.0) BluMul=Default(BluMul,1.0)
c=c.Trim(n,-1) cR=c.ShowRed cG=c.ShowGreen cB=c.ShowBlue
c.RT_ChanAve(n=0,Prefix="In_") # Simultaneous get all three averages
LockVal =
\ (LockChan==0) ? In_Ave_0:
\ (LockChan==1) ? In_Ave_1:
\ (LockChan==2) ? In_Ave_2:
\ (LockChan==-2) ? (In_Ave_0+In_Ave_1+In_Ave_2)/3.0:
\ (LockChan==-3) ? (In_Ave_0+In_Ave_1+In_Ave_2)-Max(In_Ave_0,In_Ave_1,In_Ave_2)-Min(In_Ave_0,In_Ave_1,In_Ave_2) :
\ LockVal
OffR=(In_Ave_0<MinLim || In_Ave_0>MaxLim)
OffG=(In_Ave_1<MinLim || In_Ave_1>MaxLim)
OffB=(In_Ave_2<MinLim || In_Ave_2>MaxLim)
gammaR = (OFFR)?1.0:(abs(In_Ave_0-LockVal*RedMul) < 0.0001)?1.0:cR.GuessGamma(0,reqAve=LockVal*RedMul,GamLo=GamLo,GamHi=GamHi)
gammaG = (OFFG)?1.0:(abs(In_Ave_1-LockVal*GrnMul) < 0.0001)?1.0:cG.GuessGamma(1,reqAve=LockVal*GrnMul,GamLo=GamLo,GamHi=GamHi)
gammaB = (OFFB)?1.0:(abs(In_Ave_2-LockVal*BluMul) < 0.0001)?1.0:cB.GuessGamma(2,reqAve=LockVal*BluMul,GamLo=GamLo,GamHi=GamHi)
fixedR = (Abs(gammaR-1.0)<0.0001)?cR:cR.Levels(0,gammaR,255,0,255,coring=false)
fixedG = (Abs(gammaG-1.0)<0.0001)?cG:cG.Levels(0,gammaG,255,0,255,coring=false)
fixedB = (Abs(gammaB-1.0)<0.0001)?cB:cB.Levels(0,gammaB,255,0,255,coring=false)
MergeRGB(fixedR,fixedG,fixedB)
if(Show) {
RT_ChanAve(n=0,Prefix="Out_") # Simultaneous get all three averages
RT_Subtitle("%d] \a!GamMac v0.00\a-\n" +
\ " \a2R \a4G \a1B\a-\n" +
\ "IN_AVE: %7.3f : %7.3f : %7.3f\n" +
\ "GAMMA : %7.3f : %7.3f : %7.3f\n" +
\ "OUTAVE: %7.3f : %7.3f : %7.3f",
\ n,In_Ave_0,In_Ave_1,In_Ave_2,
\ gammaR,gammaG,gammaB,
\ Out_Ave_0,Out_Ave_1,Out_Ave_2)
(Verbosity==1)
\ ? RT_Subtitle("Lockchan=%d LockVal=%.3f\nRedMul=%.3f GrnMul=%.3f BluMul=%.3f",LockChan,LockVal,RedMul,GrnMul,BluMul,align=1)
\ : (Verbosity!=0)
\ ? RT_Subtitle("Lockchan=%d LockVal=%.3f\nGamHi=%.3f GamLo=%.3f\nMinLim=%.3f MaxLim=%.3f\nRedMul=%.3f GrnMul=%.3f BluMul=%.3f",
\ LockChan,LockVal,GamHi,GamLo,MinLim,MaxLim,RedMul,GrnMul,BluMul,align=1)
\ : NOP
}
Return Last
}
""") # End of GScript


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

#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

ConvertToRGB32
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.0 # 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.00 # 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=ScriptClip("GamMac(Last,current_frame,LockChan,LockVal,RedMul,GrnMul,BluMul,MinLim,MaxLim,GamLo,GamHi,Show,Verbosity)",
\ args="LockChan,LockVal,RedMul,GrnMul,BluMul,MinLim,MaxLim,GamLo,GamHi,Show,Verbosity")

BluMul=1.1
B=ScriptClip("GamMac(Last,current_frame,LockChan,LockVal,RedMul,GrnMul,BluMul,MinLim,MaxLim,GamLo,GamHi,Show,Verbosity)",
\ args="LockChan,LockVal,GamHi,GamLo,RedMul,GrnMul,BluMul,MinLim,MaxLim,Show,Verbosity")

BluMul=0.9

C=ScriptClip("GamMac(Last,current_frame,LockChan,LockVal,RedMul,GrnMul,BluMul,MinLim,MaxLim,GamLo,GamHi,Show,Verbosity)",
\ args="LockChan,LockVal,RedMul,GrnMul,BluMul,MinLim,MaxLim,GamLo,GamHi,Show,Verbosity")
TOP=StackHorizontal(ORG,A)
BOT=StackHorizontal(B,C)
StackVertical(TOP,BOT)
return Last


Top Right, standard. Bot Left BluMul 1.1, Bot Right BluMul 0.9

EDIT: Perhaps I should allow also when LockChan == - 1 (thoughts).
EDIT: Bot right a little too green but just rough mod so you get the idea, beginning to look really quite good.
EDIT: OK, even applies when LockChan == -1.
EDIT: Was not quite working right, fixed.

Code Modified.

StainlessS
16th July 2016, 04:08
Fred, modified previous post.
Doing plugin by name "GamMac()" [Gamma Machine], if you have alternative then say.
Re-arranged args a little in GamMac() as prev post mod.
If you are unhappy about anything then now is a good time to shout about it.

videoFred
16th July 2016, 10:58
Good work StainlessS! :thanks:

Must go now.... Tomorrow I have time to look at it.

Fred.

StainlessS
16th July 2016, 11:18
OK Fred, GamMac() v1.0 available in this thread:- http://forum.doom9.org/showthread.php?p=1774281#post1774281

Or in sig below.

EDIT: Perhaps you'de like to donate some docs (and explain the crazy idea) :)
(Perhaps an apple fell upon your head, or maybe a brick :) )

StainlessS
16th July 2016, 18:01
Post #33, update as per Bernardd post here:- http://forum.doom9.org/showthread.php?p=1774310#post1774310

videoFred
17th July 2016, 11:26
EDIT: Perhaps you'de like to donate some docs (and explain the crazy idea) :)
(Perhaps an apple fell upon your head, or maybe a brick :) )

Haha, as a child, I fell on my head once.
It was enough for the rest of my life :p


Fred.

StainlessS
17th July 2016, 17:17
Post #33, added LockChan= -3 (choose median of R, G, B for lock channel).
Added Verbosity arg (affects show metrics).
Updating Gamac plug (Thread):- https://forum.doom9.org/showthread.php?t=173695

mogsta
8th September 2017, 01:54
Hi everyone,

My first post, and I feel kinda out of place amongst you gentlemen who are obviously super experienced.

I'm desperate to try out GamMac on some Super 8 film I've digitised, but as a newbie, I'm not sure how to run the script you guys have been fiddling with here.

I've tried everything I could think of before hassling you, but I still can't get it to do anything. (I have never previously used AviSynth)

So let's say I have a file "clip.mp4" in a folder. I open AviSynth, then how do I open the script and link it to that clip?

I have had varying results digitising my grandfather's Super 8. One was rather screwed up, scorched with lots of damaged sprocket holes, but I was happy with what I could rescue.

I'd love to learn some methods to see what I could do to improve footage like this: https://youtu.be/tBd3tp6GyDE

I appreciate your help and valuable advice!

Regards,
Brett

StainlessS
8th September 2017, 02:57
Answering in GamMac thread:- https://forum.doom9.org/showthread.php?p=1817790#post1817790