View Full Version : Automatic White Balance (AWB) script function(s)
martin53
13th June 2013, 20:01
EDIT: attached script version 27th October 2013 This one may be called more than once from a script, e.g. to compare settings :)
EDIT: script version 13th November a bit faster and hopefully no more AviSynth string heap issue - but has severe bugs in AWB algorithm, please don't use at this time. Update to come soon.
EDIT: script version 1st December Finalized White Patch (Max RGB) algorithm. Also:
+ uses arrays instead of global vars: full non linear access with 100% reproduceable output
+ multi instance capable
+ unprecedented plugin check
I'll concentrate on a new approach now (after I learned so much about things that are not what they seem in consumer cameras YUV or RGB values)
——————
Original Post
I'd like to announce a script function or maybe script function set for automatic white balance for real world footage, similar to the known function in digital cameras.
Some widely used algorithms implement theories of J. von Kries on the ability of the human visual system to recognize the color of objects under biased illumination, called "color constancy".
They are known as "Gray World", "Max-RGB" and "Shades of Gray".
Maybe I failed in finding an already existing plugin for AviSynth or VirtualDub that implements a useful automatic white balance (I do not count ColorYUV(autowhite=true) as useful). They may be not too fast since they use the runtime environment. But they show once again the power of AviSynth scripting when one refuses to write in C (as I do up to now). Here are the first appetizers, and comments are welcome.
Needs AviSynth V2.6 and dither, GRunt, mt_masktools and RT_Stats plugins.
--> see attached GrayWorldSimple.txt, Max-RGB_Simple.txt, ShadesOfGraySimple.txt
StainlessS
13th June 2013, 23:51
Have not tried as yet, but shall.
I have real expectations of this, hope you live up to them.
EDIT: Was the MYStats thing of any use ?
martin53
14th June 2013, 21:32
...have real expectations of this, hope you live up to them.
Same for me! To be honest, the underlying concepts of the three algorithms are quite ungarnished. But there was or is plenty of research on the topic, and this is what publicly evolved from it. To repeat myself: the concepts are not my inventions.
Was the MYStats thing of any use ?
I am convinced it will! I first tried it on itself on some clips and found that it seemed to work well. The list of challenges on my mind contains averaging over a group of frames, which in turn demands scene detection, an automatic mode which evaluates several algorithms and chooses/balances between them, and, maybe most important, masking of a color range that is valid at all for potential color temperature shift, i.e. lies along the temperature curve and is not a colorful object. I expect much from this to reduce misled corrections.
Up to now, I had to cope with color bleeding, banding etc. from the bad resolution of 8 bits in the neccessary YUV-RGB-YUV processing. Research on how to define the interesting color range will come next.
You can see from the examples that there is a lack of full featured statistics functions for RGB as there is for YUV for the task. I found workarounds though, so that's not a job stopper.
StainlessS
14th June 2013, 22:01
lack of full featured statistics functions for RGB as there is for YUV
Give me time and I'll do similar funcs for RGB
martin53
15th June 2013, 10:49
If you like to do it for fun, go ahead. For the white balance task, it will evolve which statistic functions will be needed.
Among them are, as far as I can judge today, masked versions for AverageChromaU and AverageChromaV. The mask should be taken from the Y plane of the mask clip, but the U and V planes of the main clip be evaluated.
Also, for the "Shades of Gray" algorithm, average functions that do not average the first power of samples, but an arbitrary power are useful.
To date the clip is prepared with 16 bit bitdepth and the samples taken to the power of 6 with Dither_lut8() and then dithered down to 8 bit resolution before measuring. But of course that's a workaround.
martin53
15th June 2013, 11:18
I am just wondering about color conversion documentation.
The AviSynth v2.6 doc in Advanced Topics / ColorSpace Conversions looks very reasonable. The constants Kr, Kg and Kb are taken from the standard, and the following equations derived from that.
But at second sight, the (v - 128 = ...) and (u - 128 = ...) equations have factors of 0.5, which seems arbitrary.
The web page http://en.wikipedia.org/wiki/YUV defines Wr=Kr, Wg=Kg and Wb=Kg, but furthermore Umax=0.436 and Vmax=0.615. This of course leads to different RGB->YUV and YUV->RGB transformations.
My question is: who is right, and are the AviSynth V2.6 colorspace conversion routines trustable?
Gavino
15th June 2013, 14:18
The AviSynth v2.6 doc in Advanced Topics / ColorSpace Conversions looks very reasonable. The constants Kr, Kg and Kb are taken from the standard, and the following equations derived from that.
But at second sight, the (v - 128 = ...) and (u - 128 = ...) equations have factors of 0.5, which seems arbitrary.
The web page http://en.wikipedia.org/wiki/YUV defines Wr=Kr, Wg=Kg and Wb=Kg, but furthermore Umax=0.436 and Vmax=0.615. This of course leads to different RGB->YUV and YUV->RGB transformations.
The difference is simply an arbitrary scale factor in U and V.
The Avisynth wiki page gives the formula to derive U and V in the range [-1, 1], while the Wikipedia article uses the range [-Umax, Umax] and [-Vmax, Vmax].
But when doing RGB<->YUV conversions on 8-bit inputs, you have to rescale anyway to fit the range [16, 240] or [0, 255] (depending on the matrix used). The Avisynth conversion routines do this correctly.
martin53
15th June 2013, 19:47
The Avisynth wiki page gives the formula to derive U and V in the range [-1, 1]
Thank you for answering, Gavino.
Alas, I intentionally asked for the lowercase letters u and v because I need the constants outside the builtin conversion routines.
Reading on in the Wikipedia article, I found now that I confused Y'UV with Y'CbCr anyway, and instead of calculating
U = -0.147 * R - 0.289 * G + 0.436 * B
which would only be valid for an analog device, for a digital device it must be
Cb = -0.169 * R - 0.331 * G + 0.499 * B + 128
I take 0.499 as 0.5, and so indeed the two formulas match. -0.169 comes from 0.5*Kr/(1-Kb) while the analog -0.147 comes from 0.436*Kr/(1-Kb) and so on, and it is confirmed that in AviSynth, the u and v scale factors are 0.5
(which btw. makes sense, because Y's [0,1] range is 1 wide while U,V's [-1,1] range has a width of 2. The [0,255] scaled variables share a range ot the same width, so a downscaling with 0.5 for u and v is obvious from that)
martin53
17th June 2013, 21:02
As an ancillary product, here is a script to change the 'color temperature' of a clip by a fixed offset.
It uses physically correct values to correct RGB and tries not to change the overall luminance. If that clamps RGB, you may enter a gain factor < 1.0 to make the clip darker with less clamping.
-> see 1st post
EDIT: function is part of complete script in 1st post
martin53
23rd June 2013, 10:23
More advanced versions of the simple estimators drafted above will restrict estimation to colors in the picture that might be biased grey.
Below is a mask generator that I have used lately and that will find its way into later versions of the AWB function itself.
It can be fed with this test clip, to show the functionality.
Luma statistics functions give much better results if their input is restricted to these pixels, i.e. the MYStats functions with this mask give much better results than the standard functions.
I'll be offline for several days from now.
function testClipYUV() {
c = BlankClip(240,640,480,"YV12")
c.mt_lutspa(yexpr="128", uexpr="x 255 *", vexpr="y 255 *", chroma="process")
}
Mask generator
function GrayGamutMask(clip c) {
#
# Function to create a mask that is only white for clip pixels within a defined color and luminance range.
# Expects clip in YUV format
# Returns mask clip in YV24 format
#
# Author doom9.org/martin53
# Version 1.0
# Date June 18, 2013
# Uses plugins GRunT, MaskTools v2.0a48
# Tested with Avisynth V2.6
#
# Changes n.a.
#
TOL_UP = 0.9
TOL_DN = -0.4
U_DN = 128-15 # -50 mired, i.e. color temperature change from D65 to 4900K warm day or 7800K to 5600K flash
V_UP = 128+7 # -50 mired, i.e. color temperature change from D65 to 4900K warm day or 7800K to 5600K flash
U_UP = 128+15 # 50 mired, i.e. color temperature change from 4900K light to D65
V_DN = 128-7 # 50 mired, i.e. color temperature change from 4900K light to D65
YMIN = 100
YMAX = 235 - 1
c24 = c.ConvertToYV24()
#Make a mask according to experimentally found U and V change over color temperature
#mt_lutxy is fed with the U and V components. x stands for the U component, y for the V component.
expr = mt_polish("(x-128)/15+(y-128)/7 <= "+string(TOL_UP)
\+" & (x-128)/15+(y-128)/7 >= "+string(TOL_DN)
\+" & x <= "+string(U_UP)
\+" & y >= "+string(V_DN)
\+" & x >= "+string(U_DN)
\+" & y <= "+string(V_UP)
\+" ? 255 : 0")
exprX = mt_polish("(x-128)/15+(y-128)/7 <= "+string(TOL_UP*2)
\+" & (x-128)/15+(y-128)/7 >= "+string(TOL_DN*2)
\+" & x <= "+string(U_UP*2)
\+" & y >= "+string(V_DN*2)
\+" & x >= "+string(U_DN*2)
\+" & y <= "+string(V_UP*2)
\+" ? 255 : 0")
#subtitle(expr)
mChroma1 = mt_lutxy(c24.UToY(), c24.VToY(), expr=expr, chroma="128")
#Choose this mask if it is not all black, otherwise choose fallback mask with doubled tolerance values
mChroma = GConditionalFilter(mChroma1, mChroma1, mt_lutxy(c24.UToY(), c24.VToY(), expr=exprX, chroma="128"), "AverageLuma()>0")
#Complete the mask with minimum and maximum luma restrictions.
#mt_lutxy is fed with the Y component and the previous mask. x stands for Y (luma), y for the previous mask.
expr = mt_polish("y > 0"+" & x >= "+string(YMIN)+" & x <= "+string(YMAX)+" ? 255 : 0")
#subtitle(expr)
mLuma = mt_lutxy(c24, mChroma, expr=expr, chroma="128")
#Choose mask mLuma if it is not all black, else choose fallback mask mChroma
GConditionalFilter(mLuma, mLuma, mChroma, "AverageLuma()>0")
#Choose this mask if it is not all black, else choose fallback all white mask
GConditionalFilter(last, last, BlankClip(c24, color_yuv=$f08080), "AverageLuma()>0")
#debug
#GScriptClip("""RT_Debug("Mask",string(AverageLuma())) last""") #"
}
Usage e.g.
TestClipYUV()
merge(last, GrayGamutMask().ConvertToYV12(), weight=0.1)
Navarre66
6th July 2013, 14:05
I'm trying to use MaxRGB1, but I keep getting the error: Script error: ScriptClip does not have a named argument "args"
Apparently inferring it doesn't like the args="RGBinYUV"
I'm using AviSynth 2.6.0 Alpha4 because I get problems with dither.avsi if I use AviSynth 2.5.8 or SEt's version of Avisynth 2.6.
:thanks:
Gavino
6th July 2013, 14:41
I keep getting the error:
Script error: ScriptClip does not have a named argument "args"
Apparently inferring it doesn't like the args="RGBinYUV"
You need to install the GRunT (http://forum.doom9.org/showthread.php?t=139337) plugin (which - among other things - extends ScriptClip with the 'args' parameter).
Navarre66
6th July 2013, 18:45
You need to install the GRunT (http://forum.doom9.org/showthread.php?t=139337) plugin (which - among other things - extends ScriptClip with the 'args' parameter).
Thanks. That did the trick.
:thanks:
martin53
25th July 2013, 22:14
Script is now attached to 1st post. Please read abstract before you try it.
Comments welcome.
martin53
18th August 2013, 18:23
This is a call for comments on expectations what an AWB script should serve for.
After I fiddled so much with my lots of images & scenes, I learned that it is not that difficult to average RGB in an image, but that slight adjustments of scenes require a script to restrict itself, while the correction of images taken with sunlight setting in bulb illuminated environment, or very shadowy environment, require strong impact.
So I may optimize it to do subtle enhancements, but sacrifice the ability to always return the same result, no matter how extreme the user may bias the input in advance. For that purpose, I shifted the 'correlated color temperature' of the input clip by +-1000°C, and the output does not stay the same, how I earlier defined the goal for myself. Personally I feel now that it is important to retain the original mood of scenes in most cases, so the color of the result just seems 'right' in an unobstrusive way, i.e. just eliminate annoying color bias e.g. from incandescent light, but not overdo compensation. That might require manual pre-balancing in some cases, however. A script for manual pre-shifting of the correlated color temperature is provided in this thread.
yup
19th August 2013, 18:25
Hi martin53!
I find Your script useful, thanks for Your time. My source mainly VHS capture indoor shooting with color distortion (a lot of yellow see https://www.dropbox.com/s/wyc1o6ygvxbuv8a/samplevhs.avi).
AVISource("tape1.avi")
AssumeTFF()
ConvertToYV12(interlaced=true)
Crop(16,12,-16,-12)
s=SeparateFields()
s.AutoGain(adjust_mode=1).ShadesOfGray1()
# OR s.AutoGain(adjust_mode=1).GrayWorld1()
StackVertical(s,last)
#AssumeFieldBased().Weave()
work fine shift yellow to blue color and video look good (I know real color because have photo and slide from this place), but script
AVISource("tape1.avi")
AssumeTFF()
ConvertToYV12(interlaced=true)
Crop(16,12,-16,-12)
s=SeparateFields()
s.AutoGain(adjust_mode=1).AWB()
StackVertical(s,last)
#AssumeFieldBased().Weave()
give more yellow than at source. At other source script give unstable output atr preview in VirtualDub.
Please advice parameter set for AWB for my source..
yup.
martin53
19th August 2013, 19:58
yup,
downloaded the example.
What codec should I use to decode lagarith LAGS video (W7 x86)? my (I admit, probably outdated) AviSource/ffdshow are helpless. VLC plays, but with *heavy* blue flicker.
(Forgive my ignorance, I of course know how to run google, but at the moment don't like to spend time learning about lagarith)
EDIT:[ don't bother, XMediaRecode->HuffYUV worked.
I checked with the current script on my HD. One problem is the date stamp. The "10/" shows good white. I will add xm and ym to the parameters list to allow masking of a rectangle, please have a bit patience.
In between, you might mask that rectangle with a Overlay() line.
But aside from automatic balancing, I found ShiftCCT(5600) quite appealing in this case.
If you allow, I'll use the short sequence as additional test case. It is interesting because if flickers more than my other test clips.
]/EDIT
StainlessS
19th August 2013, 20:42
M53, pretty sure I've found a bug in RT_Stats RT_RGBChanxxx filters, suspend usage for a few hours, I'll do a fix.
Found it when doing MRGBChanStats() plug.
Lagarith pretty common codec for AVI YV12
http://www.videohelp.com/tools/Lagarith-Lossless-Video-Codec
Just install it, not much to learn.
But being overtaken by UT_Video (RGB,YUY2,YV12)
http://www.videohelp.com/tools/Ut-Video-Codec-Suite
I hardly ever use Lagarith or HuffYUV now.
Just install it, not much to learn.
yup
20th August 2013, 12:39
martin53!
I try code
AVISource("tape1.avi")
AssumeTFF()
ConvertToYV12(interlaced=true)
Crop(16,12,-16,-12)
s=SeparateFields()
s.AutoGain(adjust_mode=1).ShiftCCT(5600)
StackVertical(s,last)
And see good result. Please confirm need I use Autogain before Your AWB functions?
yup.
martin53
20th August 2013, 18:04
Please confirm need I use Autogain before Your AWB functions?
I was not aware of any influence between the two. When I first checked some minutes ago, the clip with AutoGain was automatically balanced in a much better way than before, but then when I compared histograms I did not find an effect of Autogain on any single pixel of a frame - and I looked very closely, with a difference amplifier. And I can not reproduce the mentioned effect now.
At the moment, I can not identify any difference. Please describe the effect you observe with/-out it.
yup
27th August 2013, 11:08
Hi martin53!
Your script good work for me with small adjustments. One problem it is speed on my 4 core 2 thereaded CPU (8 virtual core) I see only speed near 8-10 fps for SD (720x576). Main reason is dither plugin or Stainless plugin? How can increase speed using MT. I find work version only with SetMTMode(5) before call Your script. May be can use avstp? How?
yup.
martin53
30th August 2013, 18:34
yup,
thank you that you use the script!
How can increase speed
You are absolutely welcome to help finding ways. The version I published yesterday is again optimized for function, not for speed. I will optimize a few things, e.g. parallelise the statistics with StainlessS' plugins and remove steps that can be avoided. Currently, the script needs up to three runtime environments; One might be obsolete and I will check how far the other two can be combined.
It was already a great help that the MYStats plugin was made and extended after I had asked for it, and I do of course hope that I can get more help of that kind in the future.
But especially when it comes to MT, I was out of luck every time I tried until now. You and everyone else are invited to understand the script - I commented every step with some short statements - and contribute speed optimizations.
StainlessS
30th August 2013, 20:44
Further to PM, What is RGBBalancedShift ?
I'm guessin that Gavino could sort the prob, bUt I cannot
The big G is such a wonderful individual. :)
.
StainlessS
31st August 2013, 02:13
OK. I tell a lot of porkies,
BUT, I think Gavino would be the first person to say that He is the one for predictions.
martin53
1st September 2013, 13:09
Further to PM, What is RGBBalancedShift ?
It is in the package in 1st post. In short, it does a RGBAdjust with normalized factors so the luma stays same.
In full it is more complicated to avoid clipping, be able to deal with YUV & RGB, be called from in-/outside RTE, reduce color banding ...
Gavino responded to the bug thread in the developer forum. Very kind!
Wilbert
1st September 2013, 17:31
Why do you turn these scripts not into a plugin?
martin53
1st September 2013, 20:43
Why do you turn these scripts not into a plugin?
for two reasons.
* I like prototyping in script language, and all users have a full chance to see what happens inside - and maybe improve it.
* I avoided to set up a plugin development environment till now. I am not unexperienced in C programming, but not accommodated to C++. And from reading some developer threads, I am a bit scared.
I also have the ulterior motive that one of the experienced plugin developers can't refuse to try it.
But to warn you: One part of the script is kinda 'academic' and easy to transer to C. But the fuzzy logic to deal with many real world illuminations is more a sort of to play with and gather experience than copy and compile. Let me just mention three situations I still struggle with:
- Frames showing sky and/or water only. These are blue but need to be distinguished from a scenery that suffers from a too high color temperature
- Closeups of face/body, vegetation, rock/gravel. These may be very biased towards different colours and should not be greyed out
- Clips that were taken at tungsten light typically still show white as the brightest colour because very bright orange can't be coded into the clip, or the sensor is saturated. It is neccessary to overdo the correction only for this situation and to the appropriate amount.
The three basic approaches I cited at thread start do not cope with such challenges. The script modifies 'Shades of Gray' with some additional rules and modified parameters, with an adaptive colour-local approach etc.
StainlessS
1st September 2013, 22:34
Dont be a scaredy cat M53. :eek:
Hope you've seen this thread in devs (Avisynth CPP Interface for C Programmers):
http://forum.doom9.org/showthread.php?t=163082
Once you've got the standard interface worked out, you can more of less forget CPP and just code C.
(Is easier than the C interface).
yup
1st October 2013, 14:02
Hi martin53!
I try Your last script and find small bug :).
1. Your define variable show after first use.
reduce = Default(reduce, show?1:3) # reduction factor for the stats clips
only after 6 lines I see
show = Default(show, false)
2. I get error at line
s = replace(replace(s, "--", "+"), "+-", "-")
I install Grunt, GScript, Mystats, RT_Stats.
Please advice.
yup.
martin53
1st October 2013, 20:13
yup,
reduce = Default(reduce, show?1:3) # reduction Please advice.
Please change to
reduce = 1
I learned that it is better not to scale down the analysis frame, in order to leave the RGB relations of single bright pixels unchanged.
I also did a lot more of research, but was still not satisfied with the latest results, so I took a break and did not publish more recent test versions.
The hardest problem with my test clips is that with outdoor scenes, Max-RGB (also called white patch) is the best algorithm. But with indoor scenes, cameras over-expose lamps and show them as full white, while the scene itself is too warm. So, a means to detect this fundamental defect of the scene is needed - in order to switch between algorithms accordingly - and I could not yet find useful and reliable clip properties for that.
yup
2nd October 2013, 04:09
martin53
2. I get error at line 691
s = replace(replace(s, "--", "+"), "+-", "-")
I install Grunt, GScript, Mystats, RT_Stats.
Please advice.
yup.
At Your script exist code starting from line 689
s = "((x<128+"+D_POS+" ? (y<128+"+D_POS+" ? 1 : (0.43*((128-x-0.37+"+D_POS+")^1.08) + 0.38*(exp((128-x-0.37+"+D_POS+")*0.08)-1))/(y-128-"+D_POS+")-1) : ( y>127+"+D_POS+" ? 0 : 1-(x-128+0.18-"+D_POS+")^0.8/(128-y+"+D_POS+")) > 0) & (x<128+"+D_NEG+" ? (y<128+"+D_NEG+" ? 0 : 1-(0.43*((128-x-0.37+"+D_NEG+")^1.08) + 0.38*(exp((128-x-0.37+"+D_NEG+")*0.08)-1))/(y-128-"+D_NEG+")) : ( y>127+"+D_NEG+" ? 1 : (x-128+0.18-"+D_NEG+")^0.8/(128-y+"+D_NEG+")-1) > 0) & y-x<="+minTempUV+" & y-x>="+maxTempUV+") ? 255 : 0"
s = replace(replace(s, "--", "+"), "+-", "-")
s = mt_polish(s)
When i load script in VirtualDub I get error related to replace function. Which plugin have replace function?
yup.
martin53
2nd October 2013, 18:04
Oops, I didn't notice this lacks in the script, it is part of my standard function library.
Replace() is a shortcut for StrReplace:
Function StrReplace(string s,string find,string replace) # Repeated, string replacements
# Original:- http://forum.doom9.org/showthread.php?t=147846&highlight=gscript By Vampiredom, Gavino, IanB
#{i=s.FindStr(find)return(i==0?s: s.LeftStr(i-1)+replace+s.MidStr(Strlen(find)+i).StrReplace(find,replace))}
# Converted to use RT_Stats RT_StrAddStr() to avoid 2.58/2.6a3 string concatenation bug:-
{i=s.FindStr(find) return(i==0?s:RT_StrAddStr(s.LeftStr(i-1),RT_StrAddStr(replace,s.MidStr(Strlen(find)+i)).StrReplace(find,replace)))}
#=====================================================================================================================
Function Replace(string s,string find,string replace) { StrReplace(s, find, replace) }
StainlessS
2nd October 2013, 22:08
m53, suggest convert to use rt_strreplace, is plugin, faster & no un-freeable temp strings created.
EDIT: Also can replace
s = replace(replace(s, "--", "+"), "+-", "-")
with
s = RT_StrReplaceMulti(s, "--"+Chr(10)+"+-" , "+"+Chr(10)+"-" )
but the Chr(10) separated multiline strings might best be constructed outside of ScriptClip/GScript rather than at each iteration.
yup
3rd October 2013, 07:27
Hi martin53!
After introduce show before first call and add replace function I get
Subtitle at frame
ScriptClip: Function did not return a video clip! (Was an int)
my script simple
AVISource("selbuild.avi")
s=AssumeTFF().Crop(16,12,-16,-12).ConvertToYV12(interlaced=true).SeparateFields().SelectEven()
AWB(s)
StackVertical(last,s)
Please explain.
yup.
Boulder
9th October 2013, 16:11
Got the same error after making those small corrections needed to open the script. I'd like to give the function a shot on some VHS stuff I need to process :)
StainlessS
9th October 2013, 22:06
After introduce show before first call and add replace function I get
Subtitle at frame
ScriptClip: Function did not return a video clip! (Was an int)
Check error reported in DebugView(Google) during catch(err).
@m53,
Suggestions:-
Drop MYStats, convert all to RT_Stats latest funcs.
Wrap Default(floatarg,floatnum) ---> Float(Default(floatarg,floatnum))
# If user supplied function arg as int var (Gavino Tip, avoid unintended int calcs).
Convert eg cR.MYPNorm() ---> RT_RgbChanStats(flgs=req_funcs,chan=-1)
# where same funcs called for R,G,and B channels, avoid conv to Y8 and multiple individual channel individual func calls.
# Guess that you are already doing that.
Convert eg Subtitle("#") ---> RT_Subtitle("#")
# faster but perhaps not necessary nor as pretty
Also see prev post.
Boulder
12th October 2013, 09:25
DebugView shows this: [2896] RT_Debug: ScriptClip 3 ######## MYPlaneMin: Mask clip dissimilar dimensions.
StainlessS
12th October 2013, 16:06
M53, is there any reason you chose height >= 600 as rec709 switch as opposed to eg height > 576 ?
It is better than using RT_Stats default Width>720, shall change RT to same as yours.
EDIT:
The fault reported by Yup and Boulder, resides here:-
cu = cYUV.UToY
cv = cYUV.VToY
Umin = MYPlaneMin(cu, cYUV, MaskMin=UVRANGE_CHECK_YMIN, MaskMax=255)
The debugview message "MYPlaneMin: Mask clip dissimilar dimensions." is issued by MYPlaneMin, (Throws the error)
cu and cv are quarter size (w/2,h/2) of mask when YV12.
If YV24 supplied to func then does not throw error but returns YV12 instead.
martin53
13th October 2013, 18:34
First, my apologies to all I did not answer earlier.
StainlessS, thanks for the suggestion. I copied the 600 threshold for the matrix somewhere, don't remember where. The future version on my HD is already mxRec = clp.width<=720?"Rec601":"Rec709". I agree that it is of advantage to be in line.
Luckily, I just read your post, because I was on the way to implement MYStats instead of MRGBStats because I hadn't seen that RT_Stats now includes all functions I need. Thanks you assembled everything together.
As for the bugs with the published script - it is difficult to keep up at the moment, because several things happened in parallel: the xxxStats functions grew better and were merged, I debugged the script and made it more robust, and I improved the algorithm. I can impossibly publish todays working version - it would cause too many questions and errors.
The most annoying and challenging issue is that consumer cameras depict warm white lamps as RGB=fully white, although the scene itself is balanced towards yellow. I.e. the white is heavily biased and unsuitable for AWB purposes. This has heavy impact because in almost every situation, the 'max RGB' alias 'White Patch' algorithm is the one which meets expectations best. In the meantime, I prepared three sets of reference clips with 'difficult' footage - and alas, the algorithm still produces inacceptable output with some scenes.
My proposal is that I'll concentrate on switching over to RT_Stats and provide a version that runs, and then continue with the improvements of the algorithm. Please have a few days more patience! Thanks!
EDIT: RT_Stats issue, see RT_Stats thread
martin53
13th October 2013, 19:56
M53, is there any reason you chose height >= 600 as rec709 switch as opposed to eg height > 576 ?
Firesledge (aka Cretindesalpes) in dither.html:
When the parameter is not defined, ITU-R BT.601 and ITU-R BT.709 are automatically selected depending on the clip definition (considered as HD if ≥ 600 lines).
:confused:
For me, width>720 is a reasonable distinguishing mark for HD. I paid homage to Cretindesalpes' greater experience.
martin53
13th October 2013, 20:20
OK everyone,
a running version is attached to 1st post. Don't blame me for any flaws please, work in progress.
StainlessS
15th October 2013, 22:22
M53, have changed 709 switch to > 600, is the better choice. RT_ updated, hope it works for you.
yup
16th October 2013, 12:01
Hi martin53 :thanks:
Last version work properly. I see improvements for my VHS capture source.
I see some speed up.
yup.
StainlessS
16th October 2013, 14:06
from AWB
# Calculate statistics from RGB clip
# a) Average of Maximum for White Patch (global color evaluation)
MRGBChanStats(cRGB, mask=cGamutMask, delta=delta, Chan=-1, flgs=$12, prefix="fRGB_", MaskMin=fGY_yMax-1)
if (RT_VarExist("fRGB_Ave_0") && RT_VarExist("fRGB_Ave_1") && RT_VarExist("fRGB_Ave_2")) {
From later EDIT to MRgbChanStats, to make same as MYStats docs
EDIT: Returns 0 if no pixels found in search area of mask within MaskMin and MaskMax.
Should use result of MRgbChanstats, testing for non 0 instead of RT_VarExist().
To be more specific, if no valid pixels found in masked area returns 0, the relevant channel pixel counts are set to 0
and no other global variables are altered in any way, ie will not be nullified nor deleted, if those variables already existed
before call, will remain in previous state.
Also note, I think we got crossed wires somewhere, I said I would alter RT_ 709 switch to be same as yours, based on height >= 600,
have now made that change in RT_Stats (also done but not released in RoboCrop, MYStats and one or two other places).
You have now changed yours to match my previous RT_stats, ie switched on width > 720.
ALL CHANGE PLEASE. :)
I shall keep same as you previously had it based on height >= 600, if its good enough for FireSledge (Cretindesalpes) its good
enough for me.
martin53
16th October 2013, 21:13
OK, fine with everything,
was confused a bit with the handling of variables and posted a question in the RT_Stats thread.
When existing variables are unaltered in the case of pixelcount==0, I need to set them to a neutral value in advance. Say I use chan=-1 then I can not know in advance whether all three channels will return data - although I'd think if one channel does, all channels should.
My observation was that when I initialize variables before I call RT_RgbChanStats, the function does not change them.
I observed some non-credibly low averages with real world footage, but was retarded with my tests by the variables thing.
Maybe I confused the system with local over global variables. Will check that.
However, the whole thing is a bit tricky to master, I think, for newbies, and maybe it should be redesigned a bit.
Will change back to height >= 600, no problem :)
StainlessS
17th October 2013, 13:18
Will answer in RT_Stats thread. EDIT: Here:-http://forum.doom9.org/showthread.php?p=1648305#post1648305
SamKook
17th October 2013, 19:26
Why not use width >= 900 to detect HD(which is what I personally use in some of my scripts)?
I don't have any concrete example in mind, but with some anamorphic 720p movies, I'm fairly certain that the height could drop below 600px, but the width should never drop much below 960px(4:3) for any HD sources and SD ones shouldn't be much over 854px(resized 16:9).
I mostly work with NTSC sources so maybe PAL ones would make my reasoning wrong though.
yup
11th November 2013, 07:00
Hi martin53!
I try last script version. Source was 50Hz after QTGMC.
SetMemoryMax(1024)
global MeGUI_darx=4
global MeGUI_dary=3
AVISource("chapter13f.avi")#.Trim(0,10000)
AssumeTFF()
AutoGain(adjust_mode=1)
AWB()
unsharp(varY=310,varC=155,strength=0.15,U=3,V=3)
SeparateFields().SelectEvery(4,0,3).Weave().AssumeTFF()
AddBorders(16,12,16,12)
https://www.dropbox.com/s/i1tpg7o7wskks6s/encawb.mp4
From 1:30 min encoded only 28 min, ant at medium file changing color balance, see file at Dropbox.
May be problem related to Global variables?
yup.
martin53
11th November 2013, 17:09
Only had time for a glimpse at your clip, but saw the sudden change at 8:39. Is the color after this time the original clip color? If so, the script is performing well up to this point, i feel :)
Before I answer your post, please allow me a note about the date stamp you use in this and the former example clip. Since the date stamp is created digitally, it is not part of the scene and might misguide the AWB estimation. You may supply a mask to the AWB script. The mask should be black in the area of the date stamp, then this area is excluded from the estimation.
Regarding your problem with the script and a longer clip, I share your suspicion about the variables. I was told that string memory is not cleaned up until script destruction, and therefore I minimise sring usage in RTE functions. Still, I don't know if this is also true with the GRunT plugin and local variable scope. And I have the suspicion that maybe the RTE script itself is a new string for every frame :(
I plan to rewrite the script as a plugin, but that will not happen in very near future. I saw that ultim is committedly working on AviSynth+. You might ask him if he's willing to implement string garbage collection during script serve stage (not possible with strict realtime requirements of course, but no problem with non-realtime processing scripts, and a great help).
As a short time workaround, I can only propose you split clips into parts of about 30', and join them after processing. It's what I do with the FSubstitute() script, which is even more complicated+long+demanding.
EDIT: For non-dynamic illuminant color situations, as your examples seem to me, I also suggest you use the ShiftCCT() script instead of AWB(). AWB() is able to adjust R, G, B independently, ShiftCCT() can only adjust along the temperature of a planckian radiator, and requires you to enter the originating color. But often, the result may still be pleasing.
StainlessS
11th November 2013, 18:01
ask him if he's willing to implement string garbage collection
Not sure if this could be possible, plugin writers call env->SaveString() to safely return a string (at least they are
supposed to) and the string is said to be a safe place to (EDIT: later) store whatever a plugin writer wishes, so long as he does
not overwrite outside of the original saved string boundaries. Would be hard not to break things without an entirely
differently defined new env->TempString() or whatever it might be called, perhaps free'ed on function exit.
(EDIT: Memory not re-usable by plugin writer, with memory total ownership passed to Avisynth.)
Gavino
11th November 2013, 19:33
I was told that string memory is not cleaned up until script destruction, and therefore I minimise sring usage in RTE functions. Still, I don't know if this is also true with the GRunT plugin and local variable scope. And I have the suspicion that maybe the RTE script itself is a new string for every frame :(
GRunT does not change the behaviour of ScriptClip regarding string usage, even when local scope is used, as string memory in Avisynth is orthogonal to the scope of variables and, as you say, is not released until script destruction.
The RTE script string (as a whole) is created only once when the containing script is loaded. However, that string itself is parsed afresh on every frame, which means that any identifiers and string literals within it are repeatedly added to the string heap.
Usually this is not significant, but for large run-time scripts, coupled with lots of source frames, it can add up. In fact, I discovered this was the source of a memory leak in SRestore (see here).
The solution is to move the code inside the run-time script to another function, reducing the run-time script itself to a simple function call. This effectively eliminates memory problems, and also gives a speed increase.
In other words, instead of
ScriptClip("""
... very long script ...
""")
use
function f(... some params ...) {
... previous script code ...
}
...
ScriptClip("f(...)")
Unless using GRunT, current_frame needs to be passed as a parameter to the function. (In GRunT, this is a global variable.)
martin53
11th November 2013, 22:54
In other words, instead of
ScriptClip("""
... very long script ...
""")
use
function f(... some params ...) {
... previous script code ...
}
...
ScriptClip("f(...)")
But this is in conflict with this (http://forum.doom9.org/showthread.php?p=1650250#post1650250) solution to create unique global variables for multiple instances of the script (because the replace function must be wrapped around the ScriptClip string). It is not possible to call one predefined function from all instances. :(
Gavino
12th November 2013, 00:55
I see - basically, you need a distinct function for each instance.
That complicates the solution, but I think it can still be done as follows. (Warning - untested)
Replace:
GScriptClip(RT_StrReplace("GScript("+chr(34)+chr(34)+chr(34)+"""
... run-time script ...
"""+chr(34)+chr(34)+chr(34)+")", "%%%%%%", string(RTE_InstanceNumber)), local=true)
by:
Eval(RT_StrReplace("GScript("+chr(34)+chr(34)+chr(34)+"""
function RTE_%%%%%%(...) {
... run-time script ...
}
"""+chr(34)+chr(34)+chr(34)+")", "%%%%%%", string(RTE_InstanceNumber)))
GScriptClip(RT_StrReplace("RTE_%%%%%%(...)", "%%%%%%", string(RTE_InstanceNumber)), local=true)
This also moves the GScript call outside the RTE, avoiding what amounts to double parsing of the run-time script text on every frame.
EDIT: The replacement code can be further simplified to:
GScript(RT_StrReplace("""
function RTE_%%%%%%(...) {
... run-time script ...
}
""", "%%%%%%", string(RTE_InstanceNumber)))
GScriptClip(RT_StrReplace("RTE_%%%%%%(...)", "%%%%%%", string(RTE_InstanceNumber)), local=true)
yup
12th November 2013, 07:41
Hi martin53!
I can see clip no problem in virtualDub from start tot end.
About mask for excluding date stamp, may be You can introduce area for processing or second clip for estimation color parameters, this approach exist in autogain from LaTo.
Waiting new version from You and try ShiftCCT.
yup.
martin53
12th November 2013, 07:58
Gavino,
since you seem to know much about the identifiers memory hole, maybe you can give an answer to this question: Is it really neccessary to have different functions, or can the eval/GScript string evaluating functions be used outside the ScriptClip string - but inside the called function - without creating ever new identifiers, like:
function RTE_f(clip c, string instance, int i, ...) {
c
GScript(RT_Replace("""
# runtime script payload body with global variables like:
global RTE_f_%%%%%% = i
""", "%%%%%%", instance))
return last
}
...
#instance counter creation + global var init from other thread here
GScriptClip(RT_Replace("RTE_f(c, %%%%%%, i, ...)", "%%%%%%", instance), local=true, args=RT_Replace("%%%%%%, , i...", "%%%%%%", instance))
(Code is meant symbolically, not tested)
The core of my question is: Do also Eval() and GEcript() create a situation where the string is re-evaluated frame by frame, or can they be used to simplify the wrapping, and to avoid the creation of identical functions per instance.
martin53
12th November 2013, 08:03
Hi martin53!
I can see clip no problem in virtualDub from start tot end.
About mask for excluding date stamp, may be You can introduce area for processing or second clip for estimation color parameters, this approach exist in autogain from LaTo.
Waiting new version from You and try ShiftCCT.
yup.
The mask already is a second clip. Please take a look inside the AWB() script for details. If you had to punch the clip itself, you'd need several commands working on every frame. A mask can be constant over the whole clip. Also, a punched clip contains black areas (if black color is used for punching out). But black is then evaluated as clip color and influences the algorithm. These things are done inside the script, but only where they are allowed to.
To create the mask, you can use clp.BlankClip(color=$ffffff).Overlay(clp.Blankclip(width=..., height=...), x=..., y=...)
Gavino
12th November 2013, 10:42
The core of my question is: Do also Eval() and GEcript() create a situation where the string is re-evaluated frame by frame, or can they be used to simplify the wrapping, and to avoid the creation of identical functions per instance.
Both Eval() and GScript() need to parse the provided string, so each time they are called, additional entries in Avisynth's string heap will be made for any identifiers and string literals found inside that string.
Therefore, although your solution will 'work', it will only reduce string memory consumption by approximately one half, since it still calls GScript() on every frame. What my proposed solution does is to generate and parse all the function bodies at compile-time, and hence only once (per instance).
martin53
12th November 2013, 20:06
Thanks for the clarification. I'll assemble a complete proposal for a syntax to fulfill the needs.
EDIT: please review. After approval, I'll update the other post too.
EDIT2: see here (http://forum.doom9.org/showthread.php?p=1650202#post1650202)
Gavino
13th November 2013, 00:52
EDIT: please review.
Yes, looks good.
I like the way you've tidied it up, moving the GScriptClip call inside GScript, requiring only a single call to RT_StrReplace.
yup
13th November 2013, 09:17
Hi martin53!
I am testing with mask which exclude area with date stamp.
Even when looking in VitrtalDub I see color shimmering from frame to frame.
I am try also other source (remember samplevhs.avi from my DropBox) and script work fine.
Different between 2 sources, first (first encawb.mp4) shooting during day time without artificial lighting (may be little cloudy weather and can note changing lighting condition), shooting non stop.
Second source (samplevhs.avi) shooting at evening time with fluorescent lamp lighting.
May be this info will be useful for debugging script.
yup.
martin53
13th November 2013, 18:23
ask him if he's willing to implement string garbage collection Not sure if this could be possible
Maybe it's a bit useless conversation here, I don't see fundamental problems.
The main problem seems to come from the fact that AviSynth just allocates string heap as ScriptClip("") & friends functions are evaluated again and again (those taking a string as a script), see Gavino's response string memory in Avisynth is orthogonal to the scope of variables and, as you say, is not released until script destruction.I don't want to be unfair of course, this is what I understood so far.
StainlessS
13th November 2013, 18:35
Perhaps strings created internally by Avisynth could be cleaned up, but the env->SaveString() thing is defined as
a possible permanent storage area for plugins, so would have to remain as is.(I think).
EDIT: ie any strings returned by external plugins. Perhaps new env->TempString() (or whatever)
could return total ownership to Avisynth and so be cleaned up, older plugins returning strings via env->SaveString() not so.
Internal/TempString would need some kind of secret indicator that it is clean-upable.
EDIT: SaveString() would best be deprecated. [Except for OnExit() type functions, where permanence is required]
If this came about, think I would seriously consider dropping all support for v2.58.
martin53
13th November 2013, 19:18
Well, if I understand the Filter SDK example right,
fnpluginnew = new char[string_len];
strcpy(fnpluginnew, fnplugin.AsString());
strcat(fnpluginnew, " ");
strcat(fnpluginnew, name);
env->SetGlobalVar("$PluginFunctions$", AVSValue(env->SaveString(fnpluginnew, string_len)));
// Since fnpluginnew has now been saved it can safely be deleted.
delete[] fnpluginnew;
env->SaveString() releases the plugin from the duty to release the memory when the plugin destructor is called. I could not identify companion functions like env->ReplaceString(), env->ReleaseString() and env->GetString(), so the meaning of this 'write only memory' is unclear to me at the moment ;), but it seems obvious, and the Filter SDK notes this, env->SaveString() is not meant to be called again and again for the same string, But you must still remember that these strings are not deallocated until the filter is unloaded. So in general if you don't want your filter to be allocating more memory on each load, try avoiding using non-const strings. but instead env->ReplaceString() would come in here °?°
StainlessS
13th November 2013, 19:39
try avoiding using non-const strings
This refers to the "$PluginFunctions$", if GlobalVar name is string literal, then no need to save anywhere, its always in the
same place in memory, and SetGlobalVar will set it to point at that location in mem.
If GlobalVar name is NOT string literal(non-const), and GlobalVar name does NOT already exist, then also need to SaveString for the
GlobalVar name too, if already exist then SetGlobalVar will re-use the original existing name string. (no point in saving)
The SaveString thing just allows plugin to pass string to Avisynth, so that Avisynth will make a copy of the string (using
the Avisynth memory allocators rather than plugin dll allocator, would be problematic/impossible for Avisynth to deallocate memory
allocated via a plugin dll compiled using different compiler, and/or not in avisynth memory lists), and that it is up to Avisynth
to delete that memory at closedown.
EDIT: Somewhere in SDK or other docs, it is said that plugin can re-use SaveString'ed memory area as it exists until closure,
so long as does not overwrite original saved memory boundaries, so could cause problems to RARE (mainly OnExit) plugin if they rely
on that memory being later available if it has been 'cleaned up'.
EDIT: Also applies to local vars, not specific to Global.
EDIT: If you use a fixed name Global/Local var as a return, then as for above "$PluginFunctions$" as it does not change,
however if using an eg user supplied prefix to create unique variable names then must save unless that unique name already
exists as a variable.
martin53
14th November 2013, 22:30
yup,
I had been concentrating on variables and stability, and today found at least one severe bug in the adjustment formulas itself. :(
Please wait for a few more days, I'll fix it. Then the algorithm will still be not perfect, but I hope it can meet average expectations then.
Good news is: I implemented RT_Stats arrays and the script now allows full nonlinear access. This is to say: you may navigate to every frame you want; the script will go backward to the next scene change, calculate all frames from there up to the current frame once, and store the RGB adjustment factors. Then, if you navigate to one of the already evaluated frames, it is directly adjusted with the factors from the stored array. Hooray! :D :D :D
StainlessS
1st December 2013, 22:40
Just thought I'de give a BUMP here to say that new version AWB posted today by Martin53 (1st post).
udobroemme
6th December 2013, 12:58
I'm not able to run the script. I'm getting the following error:
http://imageshack.us/a/img7/4160/i67w.png
My script:
ffvideosource("xyz.avi",threads=1)
awb()
I've tried also different source filters and different avi sources without luck.
I'm using the latest avisynth MT version and the plugin versions listed at the end of the script.
StainlessS
6th December 2013, 13:44
Line in red is error producing line (line 386) [I THINK]:
# Calculate basic determination data from YUV clip
flags = RT_YStats(cYUV, mask=cGamutMask, delta=delta, threshold=MINMAX_THRESH, flgs=$02, prefix="fGY_", MaskMin=18, MaskMax=255)
if (flags != $02) { eval_error = true }
flags = RT_YStats(cU, mask=cGamutMask, delta=delta, threshold=MINMAX_THRESH, flgs=$17, prefix="fGU_", MaskMin=18, MaskMax=255)
iMinLuma = max( RT_VarExist("fgY_yMax") ? fGY_yMax : 18, iMinLuma)
RT_ArraySet(iBright%%%, iMinLuma, current_frame+delta)
flags = RT_YStats(cV, mask=cGamutMask, delta=delta, threshold=MINMAX_THRESH, flgs=$17, prefix="fGV_", MaskMin=18, MaskMax=255).BitAnd(flags)
if (flags != $17) { eval_error = true }
Perhaps try Info and Show args to provide Martin53 with some additional hints.
I am leaving home now and will be away from home machine until tomorrow.
EDIT: Could also insert eg below lines immediately before error line just to see what they are (in debugview)
RT_DebugF("eval_error=%s",string(eval_error))
RT_DebugF("iMinLuma=%s",string(iMinLuma))
martin53
6th December 2013, 20:14
ffvideosource("xyz.avi",threads=1)
awb()
Please recheck with AviSource(). On my machine, ffvideosource produced exceptions in AvsPmod - but I use V2.6 alpha 5, no MT dll. Changed to AviSource() ran fine.
Also, please try without MT.
If that does not reveal the cause, please post the RT_DebugF() output lines.
udobroemme
6th December 2013, 23:36
I've now tried it with regular avisynth 2.6 alpha 5 and also with avisource and always get the above mentioned error.
Even if I call awb(info=1) to get debug output, nothing changes.
If I go to a frame that's not the first after a scene change, I get the following:
http://imageshack.us/a/img34/1023/7c5k.png
martin53
7th December 2013, 10:13
EDIT: you write that you still get the above mentioned error, and the new one on top of it. The only cause I can imagine at the moment is that there is some incompatibility between your RT_Stats and your AviSynth, because I really have no idea how both things - a non-int variable plus the invalid array index should happen at a time, regardless of source filter or footage, so persistently on your system. You say you checked several AviSynth versions. I'm sure you also checked that you installed RT_Stats26.dll and don't also have a RT_Stats.dll?
Because the GScript() string starts at line 285, line 70 of it is line 355 of the file. It seems GScript returns the line where the while loop closes. That would mean the exception comes from inside the while loop, and there are several RT_ArrayGet() functions inside. It looks like a negative sum current_frame+delta appears but I can not figure out where.
Please insert the two red lines into the script, they will be line 348 and line 350 afterwards, and run DebugView (http://download.sysinternals.com/files/DebugView.zip).
bSceneStart = (current_frame == 0 || fSC > ThSc)
RT_DebugF("AWB%%% Seeking scene start, current_frame:%d, delta:%d", current_frame, delta)
while (!bSceneStart && !RT_ArrayGet(bDone%%%, max(current_frame+delta-1,0)) && !RT_ArrayGet(bDone%%%, current_frame+delta)) {
RT_DebugF("AWB%%% Seeking scene start loop, current_frame:%d, delta:%d", current_frame, delta)
if (!RT_ArrayGet(bDone%%%, current_frame+delta)) {
fSC = RT_LumaSceneChange(cYUV, cYUV, delta=delta, delta2=delta-1)
bSceneStart = (current_frame+delta == 0 || fSC > ThSc) ? true : false
The output should read like this (frame 20 is not the 1st after a scene change in my example clip). AWB goes backward to a scene change or clip start and then goes forward compensating frame after frame.
[5620] RT_DebugF: AWB1 Seeking scene start, current_frame:20, delta:0
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:0
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-1
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-2
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-3
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-4
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-5
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-6
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-7
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-8
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-9
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-10
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-11
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-12
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-13
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-14
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-15
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-16
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-17
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-18
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-19
[5620] RT_DebugF: AWB1 Seeking scene start loop, current_frame:20, delta:-20
[5620] RT_DebugF: AWB1 #0 iMaskMin: 234
[5620] RT_DebugF: AWB1 #1 iMaskMin: 234
udobroemme
7th December 2013, 10:43
Thanks for Debugview, I didn't know that tool so far. It spits out the following:
[4204] CallCmd: Constructor IN
[4204] CallCmd: CallCmd: v1.02 - 01 Jan 2012 - by StainlessS
[4204] CallCmd: Command for Frames = ''
[4204] CallCmd: Close Command (Destructor) = 'CMD /C del C:\Users\Boeschi\AppData\Local\Temp\bDone1_20131207_103711_430.tmp'
[4204] CallCmd: FRAMES: About to Parse Frames String
[4204] CallCmd: Doing command on 0 Frames
[4204] CallCmd: Constructor OUT
[4204] CallCmd: Constructor IN
[4204] CallCmd: CallCmd: v1.02 - 01 Jan 2012 - by StainlessS
[4204] CallCmd: Command for Frames = ''
[4204] CallCmd: Close Command (Destructor) = 'CMD /C del C:\Users\Boeschi\AppData\Local\Temp\fRGB1_20131207_103711_430.tmp'
[4204] CallCmd: FRAMES: About to Parse Frames String
[4204] CallCmd: Doing command on 0 Frames
[4204] CallCmd: Constructor OUT
[4204] CallCmd: Constructor IN
[4204] CallCmd: CallCmd: v1.02 - 01 Jan 2012 - by StainlessS
[4204] CallCmd: Command for Frames = ''
[4204] CallCmd: Close Command (Destructor) = 'CMD /C del C:\Users\Boeschi\AppData\Local\Temp\iBright1_20131207_103711_430.tmp'
[4204] CallCmd: FRAMES: About to Parse Frames String
[4204] CallCmd: Doing command on 0 Frames
[4204] CallCmd: Constructor OUT
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:0, delta:0
[4204] RT_DebugF: AWB1 #0 ________ New scene init________fSC=0.0
[4204] RT_DebugF: AWB1 #0 delta 0 R:1.000 G:1.000 B:1.000
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:1, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:2, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:3, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:4, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:5, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:6, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:7, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:8, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:9, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:10, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:11, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:12, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:13, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:14, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:15, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:16, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:17, delta:0
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:18, delta:0
Btw.: I use the latest script from 12.01. and the inserted lines are at 337 and 339, not at 348 and 350 like you mentioned.
martin53
7th December 2013, 12:31
Debugview spits out the following
...
latest script from 12.01. inserted lines are at 337 and 339
Sorry about the different line numbers, seems I inserted something above - maybe the assertion that no RT_Stats.dll exists, nothing of importance, I'm sure.
It looks like you navigated from frame #0 forward. Frame #0 is a scene start and results in no color adjustment. Then each frame finds its predecessor already done and can start from there
while (!bSceneStart && !RT_ArrayGet(bDone%%%, max(current_frame+delta-1,0)) && !RT_ArrayGet(bDone%%%, current_frame+delta)) {
Info=2 makes AWB verbose in the DebugView output.
Complete output for one frame might look like
[1656] RT_DebugF: ______________________
[1656] RT_DebugF: AWB1 #0 Check point 1
[1656] RT_DebugF: AWB1 Seeking Scene start, current_frame:0, delta:0
[1656] RT_DebugF: AWB1 #0 ________ New scene init________fSC=0.0
[1656] RT_DebugF: AWB1 #0 delta 0 R:1.000 G:1.000 B:1.000
[1656] RT_DebugF: AWB1 #0 Umin:77.0 UMax:130.0 Uave:-14.9 Vmin:127.0 VMax:176.0 Vave:12.5
[1656] RT_DebugF: AWB1 #0 Ymax:233.0 Red:-20.9
[1656] RT_DebugF: AWB1 #0 Luma:233.0 UVarea:2597.0 UVavg:-27.4
[1656] RT_DebugF: AWB1 #0 iMaskMin: 232
[1656] RT_DebugF: AWB1 #0 Rmax:249.0 Gmax:234.0 Bmax:210.0
[1656] RT_DebugF: AWB1 #0 Red:263.3 Grn:221.5 Blu:167.9
[1656] RT_DebugF: AWB1 #0 After Temp check R:0.857 G:1.006 B:1.343
[1656] RT_DebugF: AWB1 #0 Factors R:0.857 G:1.006 B:1.343
[1656] RT_DebugF: AWB1 #0 Check point 2
(Instance- and Frame # always precede the information in case you have multiple instances in the graph or as part of temporal processing)
If check point 2 (ScriptClip end) is not reached, it is of interest where the script is terminated; a string search for the output lines above can narrow down the line.
According to RT_Stats.txt line 1991, RT_ArrayAlloc() initializes the arrays. I rely on that, so there is no script level initialization loop. The RT_Array... functions open a new dimension in scripting ( :D in both meanings of the word ) but they are quite new. I'm really curious what we'll find in the end.
udobroemme
7th December 2013, 13:00
With info=2 I get the following output:
[4204] RT_DebugF: AWB1 #0 Check point 1
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:0, delta:0
[4204] RT_DebugF: AWB1 #0 ________ New scene init________fSC=0.0
[4204] RT_DebugF: AWB1 #0 delta 0 R:1.000 G:1.000 B:1.000
[4204] RT_DebugF: ______________________
[4204] RT_DebugF: AWB1 #1 Check point 1
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:1, delta:0
[4204] RT_DebugF: ______________________
[4204] RT_DebugF: AWB1 #2 Check point 1
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:2, delta:0
[4204] RT_DebugF: ______________________
[4204] RT_DebugF: AWB1 #3 Check point 1
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:3, delta:0
[...]
[4204] RT_DebugF: AWB1 #62 Check point 1
[4204] RT_DebugF: AWB1 Seeking scene start, current_frame:62, delta:0
[4204] RT_DebugF: AWB1 #62 ________ New scene init________fSC=36.3
[4204] RT_DebugF: AWB1 #62 delta 0 R:1.000 G:1.000 B:1.000
[4204] RT_DebugF: ______________________
At frame #62 there's the next scene change. The script seems to terminate right after the start and starts a new instance for the next frame.
martin53
7th December 2013, 13:28
Please add the red characters to line 366, 372 and 386 (you can remove the two additional RT_DebugF lines we inserted, we'll probably not need them any more)
RT_ArraySet(iBright%%%, round(iMinLuma), current_frame+delta)
udobroemme
7th December 2013, 13:56
That did the trick for the first frame after a scene change, but the others are still not ok:
[2448] RT_DebugF: ______________________
[2448] RT_DebugF: AWB1 #0 Check point 1
[2448] RT_DebugF: AWB1 #0 ________ New scene init________fSC=0.0
[2448] RT_DebugF: AWB1 #0 delta 0 R:1.000 G:1.000 B:1.000
[2448] RT_DebugF: AWB1 #0 Umin:128.0 UMax:128.0 Uave:-0.0 Vmin:128.0 VMax:128.0 Vave:0.0
[2448] RT_DebugF: AWB1 #0 Ymax:19.0 Red:-0.0
[2448] RT_DebugF: AWB1 #0 Luma:19.0 UVarea:0.0 UVavg:-0.0
[2448] RT_DebugF: AWB1 #0 Color range small. U:128.0 V:128.0
[2448] RT_DebugF: AWB1 #0 Red:235.0 Grn:235.0 Blu:235.0
[2448] RT_DebugF: AWB1 #0 After Temp check R:1.000 G:1.000 B:1.000
[2448] RT_DebugF: AWB1 #0 Factors R:1.000 G:1.000 B:1.000
[2448] RT_Debug: RGB, locus, intersect after process 25 24 29 2.3 0.1 1.7 -1.0
[2448] RT_DebugF: AWB1 #0 Check point 2
[2448] RT_DebugF: ______________________
[2448] RT_DebugF: AWB1 #1 Check point 1
[2448] RT_DebugF: ______________________
[2448] RT_DebugF: AWB1 #2 Check point 1
[2448] RT_DebugF: ______________________
[2448] RT_DebugF: AWB1 #3 Check point 1
[2448] RT_DebugF: ______________________
[...]
[2448] RT_DebugF: AWB1 #62 Check point 1
[2448] RT_DebugF: AWB1 #62 ________ New scene init________fSC=36.3
[2448] RT_DebugF: AWB1 #62 delta 0 R:1.000 G:1.000 B:1.000
[2448] RT_DebugF: AWB1 #62 Umin:130.0 UMax:134.0 Uave:4.2 Vmin:130.0 VMax:134.0 Vave:4.4
[2448] RT_DebugF: AWB1 #62 Ymax:221.0 Red:-2.0
[2448] RT_DebugF: AWB1 #62 Luma:221.0 UVarea:16.0 UVavg:-0.2
[2448] RT_DebugF: AWB1 #62 Color range small. U:130.0 V:128.0
[2448] RT_DebugF: AWB1 #62 Red:235.0 Grn:233.9 Blu:238.5
[2448] RT_DebugF: AWB1 #62 After Temp check R:1.000 G:1.001 B:0.995
[2448] RT_DebugF: AWB1 #62 Factors R:1.000 G:1.001 B:0.995
[2448] RT_Debug: RGB, locus, intersect after process 251 217 231 1.3 15.9 -5.8 3.3
[2448] RT_DebugF: AWB1 #62 Check point 2
[2448] RT_DebugF: ______________________
martin53
7th December 2013, 15:48
General AviSynth syntax states that a literal assignment 'iMinLuma = 1' makes iMinLuma an integer variable.
BlankClip()
i = int(1)
IsInt(i) ? Subtitle("i has type integer") : last
IsFloat(i) ? Subtitle("i has type float",y=20) : last
This displays both texts on my system. If i is set to e.g. 1.1, it displays only the float text, and if i is a string, it displays none of the texts.
It is a vague idea, but maybe one of your plugins affects the way RT_Stats gets/interprets the variable types. I never heard about something like this though. I'll PM StainlessS about the question and could you please move all your other plugins to a different folder temporarily for a check?
EDIT: You can test the round(...) statement in every RT_ArrayGet() function around the 2nd parameter. It might help with your system, but I don't understand why it should be neccessary in general, and it would bloat the code and slow the script down a bit.
Gavino
7th December 2013, 15:59
IsFloat() returns true for ints as well as for floats.
In effect, it really means 'is numeric'.
http://avisynth.nl/index.php/Internal_functions/Boolean_functions
IsFloat(2) = true # ints are considered to be floats by this function
udobroemme
7th December 2013, 16:34
EDIT: You can test the round(...) statement in every RT_ArrayGet() function around the 2nd parameter. It might help with your system, but I don't understand why it should be neccessary in general, and it would bloat the code and slow the script down a bit.
That did it. It works now. I don't mind about a little slowdown.
Thank you very much for all your efforts and of course for the script.
Gavino
7th December 2013, 17:10
General AviSynth syntax states that a literal assignment 'iMinLuma = 1' makes iMinLuma an integer variable.
I don't think it actually does say that.
Variables don't have types, only values do.
In particular, if you later assign a float value to iMinLuma, the variable will then hold that float value; it is not truncated or rounded to an integer.
martin53
7th December 2013, 17:19
Variables don't have types, only values do.
You are absolutely right. I meant it that way but I did not use the right words. I wanted to say 'after assigning a literal numeric expression without decimal point to a variable, I can use the variable where an integer is required' (the variable can hold different types later or before).
Gavino
7th December 2013, 17:32
The reason I mentioned it is this line in your code:
iMinLuma = max( RT_VarExist("fgY_yMax") ? fGY_yMax : 18, iMinLuma)
If fgY_yMax holds a float value, then iMinLuma will be assigned a float, and can't be used as an index in RT_ArraySet(). Is that possibly the source of the error?
martin53
7th December 2013, 22:22
That line was one suspect for me, too. The name of the variable suggests it. But it comes from RT_YStats(), which allows a prefix to write several statistics variables in one pass; and the suffix yMax is for YPlaneMax() - nothing with a fractional part.
StainlessS
8th December 2013, 04:23
That line was one suspect for me, too. The name of the variable suggests it. But it comes from RT_YStats(), which allows a prefix to write several statistics variables in one pass; and the suffix yMax is for YPlaneMax() - nothing with a fractional part.
I've just taken a look at source and think not possible that RT_YStats returns anything other than Int for yMax, flags really should be checked for valid return results, I dont like the checking for VarExist instead. Perhaps fGY_yMax is assigned float elsewhere in script.
EDIT: Suggest removal of 'round()' temp fixes, as they will hide cause of error, better to find than hide.
Also suggest m53 uses same version AWB as users to avoid confusion with line numbers.
martin53
8th December 2013, 12:36
I share the opinion about round().
Also agree to change to flags check. Please review if this is OK, 'cause I currently have little time to test the script, can only edit and believe.
flags = RT_YStats(cU, mask=cGamutMask, delta=delta, threshold=MINMAX_THRESH, flgs=$17, prefix="fGU_", MaskMin=18, MaskMax=255)
iMinLuma = max( BitAnd(flags, 2)>0 ? fGY_yMax : 18, iMinLuma)
Was my fault to use my proceeding script instead of published version for checks. I promise amendment :)
StainlessS
8th December 2013, 13:21
Should be BitAnd(flags, 1)>0 or RT_BitTST(flags,1) (returns bool)
EDIT: ABOVE is RUBBISH, you were correct with
BitAnd(flags, 2)>0 (or RT_BitTST(flags,1) OK)
I must have a brain tumor (or a hangover) :)
Flgs_Bit_Number Add_To_Flgs Equivalent_Function Local_Var_Set_Excluding_Prefix
0 1($01) RT_YPlaneMin() "yMin" (0->255)
1 2($02) RT_YPlaneMax() "yMax" (0->255)
2 4($04) RT_YPlaneMinMaxDifference() "yMinMaxDiff" (0->255)
3 8($08) RT_YPlaneMedian() "yMed" (0->255)
4 16($10) RT_AverageLuma() "yAve" (0.0->255.0)
5 32($20) RT_YPlaneStdev() "yStdev" (0.0->255.0)
6 64($40) RT_YInRange() "yInRng" (0.0->1.0)
7 128($80) RT_YPNorm() "yPNorm" (0.0->??? depends upon d and u)
Flags arg $17 is yMin, yMax, yMinMaxDiff, yAve. yMax is bit 1.
maybe easier:
flags = RT_YStats(cU, mask=cGamutMask, delta=delta, threshold=MINMAX_THRESH, flgs=$17, prefix="fGU_", MaskMin=18, MaskMax=255)
iMinLuma = max( flags !=0 ? fGY_yMax : 18, iMinLuma)
so long as the flags arg bit is set for returning fGY_yMax, the version you gave is more rigidly correct, but compare non zero can be
same for whatever (valid) var you are interested in.
Assuming all flags arg bits valid, and pixels found in mask within maskmin, maskmax, then will return same as flags arg.
martin53
8th December 2013, 14:19
If the symbol fGY_yMax does not exist in AviSynth's variables stomach - is the code safe then?
You know, I'm talking about the classical problem
if(var_does_not_exist || var>0) {
...
}
which is only safe because there's the agreement that the evaluation is immediately stopped after the result is final (which is true for one true term in an or clause).
In some languages, one must code
if (var_does_not_exist)
...
else
if (var>0)
...
endif
endif
StainlessS
8th December 2013, 14:52
If flags arg had bit 1 set and returned flags is non zero then the yMax variable (with whatever correct prefix) has been set and DOES exist.
However if returned flags is zero then you cannot assume anything about the ymax variable (it may or may not exist, it was not created nor changed).
is the code safe then?
Presume you mean this
flags !=0 ? fGY_yMax : 18
Yes, above is safe.
Ternary/Conditional Operator, [ (condition) ? result1 : result2 ] evaluates one or other result, not both.
You did at one point ask if anyone objected to "%%%" sequence, I meant to answer but forgot. "%%" is used as escaped "%"
in RT_DebugF() and RT_String() to embed a percent symbol into string without being interpreted as a variable insertion point.
Using "%%%" might at some point in future be a problem (although perhaps unlikely). Suggest maybe change to eg "@@@"
or similar (not backslash '\\\').
EDIT:
this
if(var_does_not_exist || var>0) { ... }
about same as this
if(flags==0 || var>0) { ... } # failed
although it dont seem to make sense that non existent var judged same as var>0.
this makes more sense
if(flags==0 || var<=0) { ... } # failed
2nd part only evaluated if first part false.
or
if(flags!=0 && var>0) { ... } # passed, var exists and is greater than zero
2nd part only evaluated if first part true.
EDIT: You use eg this
dPurple = Round(Default(dPurple, 3)) #where dPurple is function arg type Int
It is not necessary to use Round() on Int function args, they are guaranteed to be type Int (or perhaps Undefined if optional).
It would only be necessary if you coded something like this
dPurple = Round(Default(dPurple, 3.0))
eg
amount = Float(Default(amount, 1.0)) #where amount is function arg type Float
is a good idea to avoid Integer math as client supplied Int arg would be accepted as valid Float (but actually be type Int),
[as Gavino said, IsFloat() really means IsNumeric()].
In an earlier post you put results of IsInt() and IsFloat() on frame, you would want to check IsInt() first and only after failure
test for IsFloat().
An Int is a Float, but a Float is NOT an Int (or rather, an Int is accepted as a Float).
alexx7777
5th January 2014, 01:21
AWB будет работать с setmtmode (x) - . То, что x-это лучше?
Setmtmode(5)
DirectShowSource("video")
Setmtmode(x)
SeparateFields()
AWB()
Weave()
Bernardd
9th March 2014, 12:29
Hello,
I try AWB function, but il get error "invalide arguments to function MT_expand"
I have load this plugins
mt_masktools-26.dll
CallCmd.dll
dither-1.25.1\win32\dither.dll
GRunT101\GRunT.dll
GScript_11\GScript.dll
RT_Stats_25&26_dll_v1.30_20131219\Avisynth26\RT_Stats26.dll
and import this scrip
dither-1.25.1\dither.avsi
I tried this poor script
LoadPlugin("C:\masktools-v2.0a48\mt_masktools-26.dll")
AVISource("C:\my video.avi")
mt_expand(chroma="process")
The MT_expand's arguments are good
I do not find where is the problem in AWB script script version 1st December. Thanks for help.
yup
12th March 2014, 18:22
Hi Bernardd!
Avisynth version?
mt_maaktools-26 version?
Date dll.
Some time ago I am using AWB and all was O'k.
yup.
Bernardd
15th March 2014, 01:01
Thanks yup,
but i think is not problem with avisynth and plugin version. The AWB script line is mt_expand(chroma="process"). This call is ok with my poor script and not inside AWB script. Why ?
Bernard
yup
15th March 2014, 06:04
Hi Bernardd!
I also have problem with AWB script, but last version work for me.
You can also can try
http://forum.doom9.org/showthread.php?p=1622598#post1622598
It is work better than built-in ColorYUV.
yup.
Overdrive80
23rd March 2014, 18:23
Hi, get a error: http://s30.postimg.org/kjokoa4yp/Captura_de_pantalla_2014_03_23_18_13_03.png
I use this script:
A=Import("E:\Dragon Ball\DB_1\001\1. Deinterlacing.avs").\
subtitle(string("Original")).Crop(0,0,-360,0)
B=Import("E:\Dragon Ball\DB_1\001\1. Deinterlacing with AutoWhite.avs").\
subtitle(string("Modificado"),align=9).Crop(362,0,0,0).AddBorders(2, 0, 0, 0,$00FFFF00)
Stackhorizontal(A,B)
1. Deinterlacing.avs is:
DGDecode_mpeg2source("E:\Dragon Ball\DB_1\001\VideoFile.d2v", info=3)
ColorMatrix(hints=true, threads=0,interlaced=true)
assumebff()
tfm(order=0,pp=6,mode=4).tdecimate(mode=1)
1. Deinterlacing with AutoWhite.avs is:
DGDecode_mpeg2source("E:\Dragon Ball\DB_1\001\VideoFile.d2v", info=3)
ColorMatrix(hints=true, threads=0,interlaced=true)
assumebff()
tfm(order=0,pp=6,mode=4).tdecimate(mode=1)
AWB(amount=1.0,dither=true,ThSc=20,show=1).tweak(bright=0,cont=1.1)
This error merge when I set show=1.
martin53
25th March 2014, 23:49
Thanks to other members who help - I am on a far east trip with no spare time. Will try to help after return.
foxyshadis
26th March 2014, 22:45
cPre can be replaced by cRGB in the avsi.
goorawin
1st December 2014, 04:48
I have been trying to load AWB into avisynth 2.6 without success.
I'm think everything has been loaded as it should be.
The error that occurs is: MT Expand : Unsupported colorspace, masktools only supports planar YUV colospaces. Regardless of the conversions I apply (YV12,YV16,YV24 or any other) the same error occurs.
Anyone have a solution?
I have installed the latest masktools v2.0a48
Thanks
Reel.Deel
1st December 2014, 14:29
I have been trying to load AWB into avisynth 2.6 without success.
I'm think everything has been loaded as it should be.
The error that occurs is: MT Expand : Unsupported colorspace, masktools only supports planar YUV colospaces. Regardless of the conversions I apply (YV12,YV16,YV24 or any other) the same error occurs.
Anyone have a solution?
I have installed the latest masktools v2.0a48
Thanks
The reason you get that error is that either you installed mt_masktools-25.dll which is for AviSynth 2.5 (should work with YV12), that's not the case so more than likely you installed mt_masktools-26.dll which is no longer compatible with the latest AviSynth 2.6 Alpha 4/5.
So what should I do? Download and install MaskTools v2.0b1 (https://github.com/tp7/masktools/releases), make sure to download masktools2-x86.zip.
goorawin
1st December 2014, 21:57
Thanks for that information. The new masktools version solved the error.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.