Log in

View Full Version : AutoLevels v0.10


Pages : 1 [2]

Dogway
5th May 2021, 03:10
How feasible would it be to apply an average to a whole scene?

That seems impossible to do as per StainlessS words, read here (https://forum.doom9.org/showthread.php?p=1941393#post1941393).

takla
5th May 2021, 06:57
That seems impossible to do as per StainlessS words, read here (https://forum.doom9.org/showthread.php?p=1941393#post1941393).

I doubt it is impossible. And neither does the comment you linked imply so. But maybe it is not feasible with what ever the current plug-in is based on.

Unless I'm completely wrong, but on paper, all you'd have to do is
- Index frames until scene change detection is triggered
- Calculate total luminance values (of each color channel) by adding together all indexed frames
- Calculate average by dividing the total luminance by the number of frames of the scene
- Apply average to each indexed frame with clipping protection for 235/255

The only tricky part I see is getting scene change detection accurate enough.

kedautinh12
17th July 2021, 12:16
I created a "lighten up" LUT you could apply to the video:
https://www.dropbox.com/s/98kmi7sl008fs7o/LightenUp_1.Mbangun%20Desa%20%20Nggayuh%20Kamukten.cube?dl=0

I seldom use LUTs myself but I think you can apply a 3D LUT in Avisynth,

Link error, can you re-up link again??

Dogway
1st September 2021, 12:49
I wanted to add this to ex_retinex for more performant automatic levels when I found out it doesn't support HBD(!). Not only that but it's way slower than my (although primitive) scriptclip code block (131 vs 53 fps). Hope this plugin gets a much needed update.

StainlessS
1st September 2021, 14:01
+1 on that. So do I.

magiblot
3rd December 2021, 20:12
This filter doesn't seem to benefit from Prefetch(). Is there a solution to this?

StainlessS
3rd December 2021, 20:30
Is there a solution to this?
Nope, and I also doubt it for Autogain.
To be MT compatible, plug needs to be re-entrant, with no variables re-used between frames [cant remember the MT term - somebody do tell],
and as scans well before [EDIT: autogain default I think 21 frames before] and also after current frame, gathering eg min and max luma and scene change and such,
then is not prefetch compatible. Making it work so it scans for a number of frames both before and after current, for min/max etc all within same GetFrame()
would mean it re-does everything over and over again [in each thread],
probably making it actually much slower. [same I think for autogain - no source available].
Unless anybody out there knows how to do it, I dont.

EDIT: The only real sane place to put AutoLevels [or autogain, I think] is very close to or immediately after the frame accurate source filter.
From my SetMTMode.avsi

SetFilterMTMode("RoboCrop", MT_SERIALIZED)
SetFilterMTMode("AutoLevels", MT_SERIALIZED)

RoboCrop Pre-scans [by default] 40 sample frames spread across entire clip, you DONT want it doing that AFTER any MT stuff, Prefetch number of times on eg motion compensated frames,
rather slow startup before anything at all is output.
[but if RoboCrop 1st after source filter, then STDEF clip probably takes less than 1.0 second for pre-scan - during frameserving, RoboCrop just crops as already decided by pre-scan]

EDIT: Also, RoboCrop would ideally be done before Autolevels/Autogain, ie crop borders before measure luma min/max etc.

EDIT: And all source filters so far as I am aware are also MT_SERIALIZED, and so where possible, best have those MT_SERIALIZED filters together,
and after those, Prefetch() can scatter process in whatever order it likes [so long as mt mode properley set in dll or SetMTMode.avsi and not MT_SERIALIZED]

takla
7th March 2024, 12:36
Levels aren't clamped to 235
Why is that? Is that a bug or intended? With TV input&output, whites still boost to 255.

Jenyok
31st March 2025, 16:47
StainlessS,
.
Why does function AutoLevels() works so ?
See attached image.
1. Image with NO filters (original image) - left top.
2. Image with AutoLevels() filter - right top.
3. Image with GamMac() filter - left bottom.
4. Image with AutoLevels() + GamMAc() filters - right bottom.
.
Is it work correct ?
.
See code at bottom of post.
.
https://sun9-64.userapi.com/impg/ZFk0WRUZSHDVzwCogXWPgORserQQ4EAasYE5NA/hetcwhjm5S4.jpg?size=1280x1000&quality=95&sign=86c5efcb4f3d0cce987a63fa37617932&type=album
.

SetMemoryMax(32768)

LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\GScript_11\GScript.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\AutoLevels_25&26_x86_x64_dll_v0.12Beta3_20190711\Avisynth+_x86\AutoLevels_x86.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\RT_Stats_25&26_dll_v1.43_20141008\Avisynth26\RT_Stats26.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\GamMac_x86_x64_v1.10_dll_20180615\Avisynth+_x86\GamMac_x86.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\GamMatch_25&26_x86_x64_dll_v0.05_20190106\Avisynth+_x86\GamMatch_x86.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\GRunT101\GRunT.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\FredAverage_25&26_x86_x64_dll_v0.04Beta_20210405\Avisynth+_x86\FredAverage_x86.dll")
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\RgbAdapt_25&26_x86_x64_dll_v0.05_20190303\Avisynth+_x86\RgbAdapt_x86.dll")

function AutoLevelsGamMac(clip c,
\ Bool "DoAutoLevels", bool "DoGamMac",
\ int "filterRadius", int "sceneChgThresh", String "frameOverrides",
\ float "gamma", bool "autogamma", float "midpoint",
\ bool "autolevel",
\ int "input_low", int "input_high", int "output_low", int "output_high",
\ bool "coring",
\ float "ignore", float "ignore_low", float "ignore_high",
\ int "border", int "border_l", int "border_r", int "border_t", int "border_b",
\ bool "debug",
\ int "sc2th", Float "sc2Perc",
\ Float "GamMaxA", Float "GamMinA",
\ int "minRng",
\ int "LockChan", int "Scale",
\ Float "RedMul", Float "GrnMul", Float "BluMul",
\ Float "Th", Float "loTh", Float "hiTh",
\ Float "LockVal", int "RngLim", Float "GamMax",
\ Clip "dc",
\ int "x", int "y", int "w", int "h",
\ int "omin", int "omax",
\ Bool "Show", int "Verbosity", Bool "Coords",
\ Bool "Dither")
{
c

DoAutoLevels = Default(DoAutoLevels, True)
DoGamMac = Default(DoGamMac, True)

filterRadius = Default(filterRadius, 5)
sceneChgThresh = Default(sceneChgThresh, 20)
frameOverrides = Default(frameOverrides, "")
gamma = Default(gamma, 1.0)
autogamma = Default(autogamma, false)
midpoint = Default(midpoint, 0.5)
autolevel = Default(autolevel, true)
coring = Default(coring, false)

input_low = Default(input_low, undefined) #[* measured by statistics *]
input_high = Default(input_high, undefined) #[* measured by statistics *]
output_low = Default(output_low, IsYUV() ? 16 : 0) #[* 16 for yuv, 0 for rgb *]
output_high = Default(output_high, IsYUV() ? 235 : 255) #[* 235 for yuv, 255 for rgb *]

ignore = Default(ignore, 1.0 / 256.0)
ignore_low = Default(ignore_low, ignore)
ignore_high = Default(ignore_high, ignore)
border = Default(border, 0)
border_l = Default(border_l, border)
border_r = Default(border_r, border)
border_t = Default(border_t, border)
border_b = Default(border_b, border)
debug = Default(debug, false)

sc2th = Default(sc2th, 12)
sc2Perc = Default(sc2Perc, 33.33) #[* Where scene change via sceneChgThresh, and % of pixels different by at least sc2th is less than sc2Perc, then Override to NOT scene change *]
GamMaxA = Default(GamMaxA, 1.5) #[* Max limit on Gamma correct *]
GamMinA = Default(GamMinA, 1.0 / GamMaxA) #[* Min limit on Gamma correct *]
minRng = Default(minRng, 100) #[* If dynamic range of luma is less than minRng then corrections are reduced *]

(DoAutoLevels)
\ ? Autolevels(last,
\ filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMax=GamMaxA, GamMin=GamMinA,
\ MinRng=MinRng)
\ : NOP

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

(DoGamMac)
\ ? GamMac(last,
\ LockChan=LockChan, Scale=Scale,
\ RedMul=RedMul, GrnMul=GrnMul, BluMul=BluMul,
\ Th=Th, loTh=loTh, hiTh=hiTh,
\ LockVal=LockVal, RngLim=RngLim, GamMax=GamMax,
\ dc=DC,
\ x=x, y=y, w=w, h=h,
\ omin=omin, omax=omax,
\ Show=Show, Verbosity=Verbosity,
\ dither=DITHER)
\ : NOP
return last
}

function Sub(clip c, string Tit, Bool "ShowFrameNo", int "first_frame", int "last_frame", string "font", float "size", int "text_color",
\ int "halo_color", int "align", int "spc", float "font_width", float "font_angle", Int "BackColor")
{
/*
Stack Overhead Subtitle Text, with optional FrameNumber shown
http://forum.doom9.org/showthread.php?p=1813402#post1813402
Title bar is Round(size+2) pixels hi (default 20).
Dont use align=4,5,6, better use 1,2,3,7,8,or 9.
*/

ShowFrameNo=Default(ShowFrameNo,False)
first_frame=Default(first_frame,0)
last_frame=Default(last_frame,c.FrameCount-1)
font=default(font,"Ariel")
size=Default(size,18.0)
text_color=Default(text_color,$00FFFF00)
halo_color=Default(halo_color,$00000000)
align=default(align,7)
spc=Default(spc,0)
font_width=Default(font_width,0)
font_angle=Default(font_angle,0.0)
BackColor=Default(BackColor,$00000000)

c.BlankClip(height=round(size+2.0),Color=BackColor)

(ShowFrameNo)
\ ? ScriptClip("""Subtitle(String(current_frame,"%.0f] ")+"""+Chr(34)+Tit+Chr(34)+String(first_frame,",first_frame=%.0f")+
\ String(last_frame,",last_frame=%.0f,font=")+Chr(34)+font+Chr(34)+String(size,",size=%.3f")+String(text_color,",text_color=%.0f")+
\ String(halo_color,",halo_color=%.0f")+String(align,",align=%.0f")+String(spc,",spc=%.0f")+String(font_width,",font_width=%.3f")+
\ String(font_angle,",font_angle=%.3f)"))
\ : Subtitle(Tit,first_frame=first_frame,last_frame=last_frame,font=font,size=size,text_color=text_color,halo_color=halo_color,align=align,
\ spc=spc,font_width=font_width,font_angle=font_angle)

return StackVertical(c).AudioDubEx(c)
}

AviSource("95-SpotLess_Obitel_Poznaniya_95.avi")

ConvertToRGB32()

O=Last
DC=Last

#Average = last.ScriptClip(""" u=round(AverageChromaU()) v=round(AverageChromaV()) BlankClip(last, color_yuv=65536*128 + 256*u + v) """).invert().ConvertToRGB32()
Average = last

# AutoLevels()
#
filterRadius = 3
sceneChgThresh = 20
frameOverrides = ""
coring = false
gamma = 1.00
autogamma = false
#autogamma = true
midpoint = 0.50
autolevel = true
coring = false

input_low = undefined #[* measured by statistics *]
input_high = undefined #[* measured by statistics *]
output_low = IsYUV() ? 16 : 0 #[* 16 for yuv, 0 for rgb *]
output_high = IsYUV() ? 235 : 255 #[* 235 for yuv, 255 for rgb *]

ignore = 1.0 / 256.0
ignore_low = ignore
ignore_high = ignore

border = 0
border_l = border
border_r = border
border_t = border
border_b = border
debug = false

sc2th = 12
sc2Perc = 33.33 #[* Where scene change via sceneChgThresh, and % of pixels different by at least sc2th is less than sc2Perc, then Override to NOT scene change *]
GamMaxA = 1.5 #[* Max limit on Gamma correct *]
GamMinA = 1.0 / GamMaxA #[* Min limit on Gamma correct *]
minRng = 100 #[* If dynamic range of luma is less than minRng then corrections are reduced *]

# GamMac()
#
LockChan = -3 # 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)
Scale = 3
RedMul = 0.80 # 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.350 # Same for Blue channel. Allows tinkering/fine tuning.
Th = 1.00
loTh = -1.00
hiTh = 1.0
LockVal = 128.0 # Only valid if LockChan == -1
RngLim = 11
GamMax = 10.0
Show = True # Metrics
Verbosity = 5 # Verbocity FULL
SHOWCOORDS = True # Show Original with Coords
#DITHER = FALSE
DITHER = TRUE
x = 4 # Coords (for dc Detection Clip)
y = 4
w = -4
h = -4

#SZ = 196
#DBLK = DC.Crop(x, y, w, h).FredAverage(true).BilinearResize(SZ, SZ)
#DC = DC.Overlay(DBLK, x=(Dc.Width - DBLK.Width) / 2 + 48, y=(Dc.Height - DBLK.Height) / 2 +64)

omin = 5 # Output channels minimum (footroom for manual editing).
omax = 250 # Output channels maximum (headroom for manual editing).

##Return GamMac(DC,x=x,y=y,w=w,h=h,Coords=True) # Show Coords only

Scale = 2
DoAutoLevels=true
DoGamMac=false
A_TEXT = RT_String("Scale=%d rMul=%.2f gMul=%.2f bMul=%.2f filters=%s",Scale,RedMul,GrnMul,BluMul, "Autolevels()")
A = AutoLevelsGamMac(last,
\ DoGamMac=DoGamMac, DoAutoLevels=DoAutoLevels,
\ filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=true,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMaxA=GamMaxA, GamMinA=GamMinA,
\ MinRng=MinRng,
\ LockChan=LockChan, Scale=Scale, RedMul=RedMul, GrnMul=GrnMul, BluMul=BluMul,
\ Th=Th, loTh=loTh, hiTh=hiTh,
\ LockVal=LockVal, RngLim=RngLim, GamMax=GamMax,
\ dc=Average,
\ x=x, y=y, w=w, h=h,
\ omin=omin, omax=omax,
\ Show=Show, Verbosity=Verbosity, dither=DITHER)

Scale = 2
DoAutoLevels=false
DoGamMac=true
#BluMul = 1.05
B_TEXT = RT_String("Scale=%d rMul=%.2f gMul=%.2f bMul=%.2f filters=%s",Scale,RedMul,GrnMul,BluMul, "GamMac()")
B = AutoLevelsGamMac(last,
\ DoGamMac=DoGamMac, DoAutoLevels=DoAutoLevels,
\ filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMaxA=GamMaxA, GamMinA=GamMinA,
\ MinRng=MinRng,
\ LockChan=LockChan, Scale=Scale,
\ RedMul=RedMul, GrnMul=GrnMul, BluMul=BluMul,
\ Th=Th, loTh=loTh, hiTh=hiTh,
\ LockVal=LockVal, RngLim=RngLim, GamMax=GamMax,
\ dc=Average,
\ x=x, y=y, w=w, h=h,
\ omin=omin, omax=omax,
\ Show=Show, Verbosity=Verbosity, dither=DITHER)

Scale = 2
DoAutoLevels=true
DoGamMac=true
#BluMul = 0.95
C_TEXT = RT_String("Scale=%d rMul=%.2f gMul=%.2f bMul=%.2f filters=%s",Scale,RedMul,GrnMul,BluMul, "AutoLevels() + GamMac()")
C = AutoLevelsGamMac(last,
\ DoGamMac=DoGamMac, DoAutoLevels=DoAutoLevels,
\ filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMaxA=GamMaxA, GamMinA=GamMinA,
\ MinRng=MinRng,
\ LockChan=LockChan, Scale=Scale,
\ RedMul=RedMul, GrnMul=GrnMul, BluMul=BluMul,
\ Th=Th, loTh=loTh, hiTh=hiTh,
\ LockVal=LockVal, RngLim=RngLim, GamMax=GamMax,
\ dc=Average,
\ x=x, y=y, w=w, h=h,
\ omin=omin, omax=omax,
\ Show=Show, Verbosity=Verbosity, dither=DITHER)

COORDS = O.GamMac(dc=DC, x=x, y=y, w=w, h=h, Coords=True)
ODC = ((SHOWCOORDS) ? COORDS.Spline64Resize(width, Height) : O) # Resize COORDS (Not necessarily the same sizse as source clip)
ODC_TEXT = (SHOWCOORDS) ? "Detect Clip with Coords, No filters" : "Original, No filters"

TOP = StackHorizontal(Sub(ODC, ODC_TEXT, true), Sub(A, A_TEXT))
BOT = StackHorizontal(Sub(B, B_TEXT), Sub(C, C_TEXT))
StackVertical(TOP, BOT)

ConvertToYV12()

.

VideoMilk78
3rd April 2025, 23:43
I never really looked into how GamMac works I just took it for granted but when I use AutoLevels it's something like this:
Function AutoLevelsRGB(clip c,int "filterRadius",int "sceneChgThresh",String "frameOverrides",bool "coring",float "gamma",bool "autogamma",float "midpoint",
\int "input_low",int "input_high",int "output_low",int "output_high",float "ignore",float "ignore_low",float "ignore_high",
\int "border",int "border_l",int "border_r",int "border_t",int "border_b",bool "autolevel",bool "debug")
{

R=c.ExtractR().Autolevels(filterRadius=filterRadius,sceneChgThresh=sceneChgThresh,frameOverrides=frameOverrides,
\ coring=coring,gamma=gamma,autogamma=autogamma,midpoint=midpoint,
\ input_low=input_low,input_high=input_high,output_low=output_low,output_high=output_high,
\ ignore=ignore,ignore_low=ignore_low,ignore_high=ignore_high,
\ border=border,border_l=border_l,border_r=border_r,border_t=border_t,border_b=border_b,
\ debug=debug)
G=c.ExtractG().Autolevels(filterRadius=filterRadius,sceneChgThresh=sceneChgThresh,frameOverrides=frameOverrides,
\ coring=coring,gamma=gamma,autogamma=autogamma,midpoint=midpoint,
\ input_low=input_low,input_high=input_high,output_low=output_low,output_high=output_high,
\ ignore=ignore,ignore_low=ignore_low,ignore_high=ignore_high,
\ border=border,border_l=border_l,border_r=border_r,border_t=border_t,border_b=border_b,
\ debug=debug)
B=c.ExtractB().Autolevels(filterRadius=filterRadius,sceneChgThresh=sceneChgThresh,frameOverrides=frameOverrides,
\ coring=coring,gamma=gamma,autogamma=autogamma,midpoint=midpoint,
\ input_low=input_low,input_high=input_high,output_low=output_low,output_high=output_high,
\ ignore=ignore,ignore_low=ignore_low,ignore_high=ignore_high,
\ border=border,border_l=border_l,border_r=border_r,border_t=border_t,border_b=border_b,
\ debug=debug)

RGB=MergeRGB(R,G,B)
return rgb
}

I find that I get better results by doing each channel individually then merging, sometimes better results than when I use GamMac which works on a similar principle (I believe but don't quote me).

Jenyok
4th April 2025, 06:01
I never really looked into how GamMac works I just took it for granted but when I use AutoLevels it's something like this:

I find that I get better results by doing each channel individually then merging, sometimes better results than when I use GamMac which works on a similar principle (I believe but don't quote me).
.
Correct function AutoLevelsRGB() .
Add needed arguments to function.
.

# 64 bits x64
#
LoadPlugIn("C:\Program Files (x86)\AviSynth+\plugins\AutoLevels_25&26_x86_x64_dll_v0.12Beta3_20190711\Avisynth+_x64\AutoLevels_x64.dll")

function AutoLevelsRGB(clip c,
\ int "filterRadius", int "sceneChgThresh", String "frameOverrides",
\ float "gamma", bool "autogamma", float "midpoint",
\ bool "autolevel",
\ int "input_low", int "input_high", int "output_low", int "output_high",
\ bool "coring",
\ float "ignore", float "ignore_low", float "ignore_high",
\ int "border", int "border_l", int "border_r", int "border_t", int "border_b",
\ bool "debug",
\ int "sc2Th", float "sc2Perc",
\ float "GamMax", float "GamMin",
\ int "MinRng")
{
R=c.ExtractR().Autolevels(filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMax=GamMax, GamMin=GamMin,
\ MinRng=MinRng)
G=c.ExtractG().Autolevels(filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMax=GamMax, GamMin=GamMin,
\ MinRng=MinRng)
B=c.ExtractB().Autolevels(filterRadius=filterRadius, sceneChgThresh=sceneChgThresh, frameOverrides=frameOverrides,
\ gamma=gamma, autogamma=autogamma, midpoint=midpoint,
\ autolevel=autolevel,
\ input_low=input_low, input_high=input_high, output_low=output_low, output_high=output_high,
\ coring=coring,
\ ignore=ignore, ignore_low=ignore_low, ignore_high=ignore_high,
\ border=border, border_l=border_l, border_r=border_r, border_t=border_t, border_b=border_b,
\ debug=debug,
\ sc2Th=sc2Th, sc2Perc=sc2Perc,
\ GamMax=GamMax, GamMin=GamMin,
\ MinRng=MinRng)

RGB = MergeRGB(R, G, B)
return RGB
}

.

Busty
10th June 2025, 22:10
Why is it that if I just apply autolevels(borders=8), there are visible blown out highs, while the displayed max value for luma is fine?

I thought default values should work for most sources?

VideoMilk78
11th June 2025, 00:40
Why is it that if I just apply autolevels(borders=8), there are visible blown out highs, while the displayed max value for luma is fine?

I thought default values should work for most sources?

Use an image hosting site like https://postimages.org as direct uploads can take many centuries to be approved by mods :p

Busty
11th June 2025, 09:59
before: https://postimg.cc/dLzr9PNK
after: https://postimg.cc/BXtBgCq2

before: https://postimg.cc/560WSDYp
after: https://postimg.cc/WtsvhdsB

thanks VideoMilk:-)

Sharc
12th June 2025, 06:33
Where do you see "blown out highs"? Use the waveform monitor instead of the histogram.
Autolevels may have produced some out-of-gamut RGB though (?) (means more clipped RGB) which you experience as color distortion and "blown out brights". Would need further analysis though .....

Sidenotes:
- Note that your histogram includes the text in the picture in its statistics
- Autolevels expanded the luma, hence introducing gaps in the histogram. Any good reason why applying autolevels for this video?

Busty
12th June 2025, 19:59
It's more visible on the still with the keyboard. When you look at the lower keyboard, the keys right from the shadowed keys can't be distinguished anymore, they blend together. Also on the upper keyboard there is a white area right above the keyboarder's hand. This white area loses some lines, too. I would also say the forehead of the guitar player in the other example is distorted.

I don't judge the distortion by the shown values but by my eyes. The video is YUV and should stay that way, so I guess out-of-gamut-RGB is not applicable here.

No specific reason to use autolevels on this video. I just wanted to try something. While trying to process this video, I tried a script that gives me undesired Luma values from 0 to 255 (while still looking good compared to this example here) and I wanted to see if autolevels fixes this. While it fixes the values into 16 / 235, it looks distorted after autolevels. To rule out any issues that may be introduced by my processing script, I only applied autolevels on this example.

More important would be that my processing script does not give me luma values from 0-255 to begin with, but I would still like to know why autolevels is seemingly damaging my video.

Sharc
12th June 2025, 22:27
... The video is YUV and should stay that way, so I guess out-of-gamut-RGB is not applicable here.
That's a common misunderstanding. One cannot "see" YUV. YUV are not real colors. Your monitor does not show YUV, but RGB. So latest for viewing on a monitor or TV your player or renderer will convert the YUV data to real RGB colors. This silent conversion "under the hood" is inevitable.

It seems like your original already had some clipped RGBs, means slight (usually harmless) color distortions. Autolevels may have "amplified" it further, possibly resulting in some loss of details in the brights. Just a thought. Would have to be analyzed in detail.

P.S. Your excessive luma (0,255) may be single harmless pixels due to filter overshoots (ringing) or halos from oversharpening. Check with the waveform monitor rather than using ColorYUV(analyze=true) which will catch and report these local but usually harmless min/max values.

rgr
7th November 2025, 19:22
While searching for a plugin for automatic brightness adjustment, I came across this one and have two questions:

1. Does it work in 16-bit resolution since any version?

2. I need to reduce (actually just reduce) the luma (linearly) in certain parts of the video (it exceeds 235 down to 255).
So I need a conversion like this for Y:
16-240 -> 16-235
16-255 -> 16-235
Will this script:
MergeChroma(AutoLevels(filterRadius=100,input_low=16,output_low=16,output_high=235,debug=true),last)
do something like that?
I understand that darker scenes (where the luma doesn't exceed, for example, 200) will also brighten to 235? Okay, that works for me too.

Emulgator
8th November 2025, 00:43
Manipulating Y without taking Cb, Cr into equation will lead to oversaturation while lowering Y, and undersaturation while rising Y.
If your source asks for that, well. If not then you incur damage. BTW, just inspecting the histogram will tell if the source needs it.

Your goal "So I need a conversion like this for Y: 16-240 -> 16-235, 16-255 -> 16-235" would mean hard Y clipping.
You won't want that, me guessing.
If you need to get from DV-like +109 IRE (Y=254) down to Ymax=235 I can suggest SmoothCurves in 16bit and build curves that suits the source.

Touching Y alone does not represent a neutral brightness change, although it may seem so at first glance.
Lower Y down to 50..16 and you will see chroma tinting through until you get illegal triplets not representable in the final RGB presentation.

I wouldn't ask for anything auto, for larger changes above 10% even a nicely tailored curve won't cut it. It would take a LUT to do that right.
It will boil down to go 16bit at least, then to RGB, manipulate brightness there (it is easy as pie there), and finally back to YUV.

rgr
23rd November 2025, 18:02
Manipulating Y without taking Cb, Cr into equation will lead to oversaturation while lowering Y, and undersaturation while rising Y.
If your source asks for that, well. If not then you incur damage. BTW, just inspecting the histogram will tell if the source needs it.

https://gcdnb.pbrd.co/images/CMeynDUX78vR.png?o=1
left - org, right - MergeChroma(AutoLevels(filterRadius=200,input_low=16,output_low=16,ignore_high=1.0/256.0,debug=false),last)

Left - without MergeChroma, right - with MergeChroma
https://gcdnb.pbrd.co/images/svAIRRFoluxS.png?o=1

Left - with MergeChroma, right - without
https://gcdnb.pbrd.co/images/6qTO1gVxCmes.png?o=1

Your goal "So I need a conversion like this for Y: 16-240 -> 16-235, 16-255 -> 16-235" would mean hard Y clipping.
You won't want that, me guessing.
If you need to get from DV-like +109 IRE (Y=254) down to Ymax=235 I can suggest SmoothCurves in 16bit and build curves that suits the source.

This is from some Sony camera (not DV), the maximum brightness is sometimes 254, sometimes I see it go down to ~235.

For DV recordings I use ApplyGradationCurves (for Y), I see it works the same as SmartCurve.

Emulgator
27th November 2025, 13:11
The MergeChroma() idea is nice, but keeps the source chroma matched to the broader source Y range.
Maybe welcome here and there. But the AutoAnything() in 8-bit damages any histogram.
Not being Auto is the problem, but any 8-bit manipulation will add visible damage;
and so I suggest to move on from the AviSynth-usual 8-bit plugin algo thinking and make use of at least a 16-bit, better 32-bit pipeline.

(Note to myself: And finally put the proper chain which any serious NLE uses into daily AviSynth use:
Decode anything to 32-bit RGB first, process/compose in linear light, enjoy the finer AI plugins keeping the 32-bit pipeline and only as a last step encode to the final destination.
Well, I have that partly implemented already, around the feisty2 and the RIFE blocks, still Smooth() wants to sit in 16-bit stacked environment
BUT I shall reorder these and apply that globally...)

The horizontal histogram lines every n steps tell of higher density which wasn't there before, meaning that neighboring pixel values had been hard-mapped together every n steps.
I would suggest to process in at least 16-bit, better 32-bit with dithering.
Here is a YDe-DV suggestion in 16-bit, would fit to Sony PC-100 and siblings, trying to lift shadows a tiny bit, and de-blow highlights without taking Chroma into equation.
For HDV cams like HC-1 or FX-1 this may need different values, whatever the gain stage saw fit per scene/setting, and I suggest to hand-adjust chroma anyway.

#[* # +++++ Start of Block "SmoothAdjust" +++++
smoothdebug=0 #0 no Diagram, 1 = Curve only, 2: Tweak only, 3: Both
ShowSmoothCurveDiagram=smoothdebug==1 ? true : smoothdebug==3 ? true : false
ShowSmoothTweakDiagram=smoothdebug==2 ? true : smoothdebug==3 ? true : false
smoothdebugwidth=1920
smoothdebugheight=1080
linear3="0-0;32767-32767;65535-65535"
linear5="0-0;16383-16383;32767-32767;49151-49151;65535-65535"
linear9="0-0;8191-8191;16383-16383;24575-24575;32767-32767;40959-40959;49151-49151;57343-57343;65535-65535"
#DV cams bring Y 16..254 (4096..65024), we need 4096..60159
YDeDV="0-0;3000-4500;8191-9000;16383-16500;24575-24000;32767-31000;40959-37000;49151-43000;63000-54000;65535-60159"
SmoothCurve16(ConvertTo16bit().ConvertToStacked(), \
Ycurve=YDeDV, \
Ucurve=linear3, \
Vcurve=linear3, \
limiter=false, HQ=true, interp=100, debug=ShowSmoothCurveDiagram, screenW=smoothdebugwidth, screenH=smoothdebugheight)
#SmoothTweak(HQ=true, brightness=0, contrast=1.0, saturation=1.0, hue1=-0, hue2=+0, limiter=false, debug=ShowSmoothTweakDiagram, screenW=smoothdebugwidth, screenH=smoothdebugheight)
ConvertFromStacked()
return(last)
#*] # -------- End of Block "SmoothAdjust" --------

rgr
27th November 2025, 13:31
Yes, I know about 8 bits, that's why I asked if AutoLevels works in 16 bits, but I didn't get an answer.
Nevertheless, I use 16 bits:

convertbits(16)
AutoLevels(filterRadius=200,input_low=16,output_low=16,ignore_high=1.0/2000.0,debug=false)
(But the gains even with 8-bit processing outweigh the losses in this clip, where the maximum brightness varies between 235 and 254.)

But which of these screenshots do you think is better? The sky seems to have a more accurate color without MergeChroma (screenshot 2), while the field looks better with MergeChroma, but the grass in front of the field doesn't necessarily (screenshot 3).

(I see one drawback of AutoLevels -- Y values ​​exceeding 235 are hard-clipped to 235 on the output. I would prefer to leave them as is (e.g. for debugging purposes), because I can always use the Limiter function if I need to reduce them to 235.)

rgr
29th November 2025, 01:03
int "minRng" = 100 [* If dynamic range of luma is less than minRng then corrections are reduced *]

What does "dynamic range of luma" mean?
Range between lowest and highest luma value in percent? With or without ignore_low/high? 100% is between 16 and 235 for YUV (or output low/high) and between 0-255 for RGB?

Emulgator
2nd December 2025, 20:19
http://avisynth.nl/index.php/Autolevels
Supported color formats: RGB24, RGB32, YUY2, Y8, YV12, YV16, YV24

rgr
2nd December 2025, 21:33
http://avisynth.nl/index.php/Autolevels
Supported color formats: RGB24, RGB32, YUY2, Y8, YV12, YV16, YV24

This is a color format, not a bit depth. YV24 can be 8-bit or 32-bit.
And this is old version...

real.finder
2nd December 2025, 22:23
YV24 can be 8-bit or 32-bit.


YV24 is 8-bit, I think you mean YUV444

rgr
2nd December 2025, 22:42
YV24 is 8-bit, I think you mean YUV444

Yes, you're right. I never use that notation. However, the manual is for an archived version.

rgr
19th December 2025, 19:57
Comparison:

AutoLevels without MergeChroma // org from digital camera (with superwhites) // AutoLevels with MergeChroma

Busty
4th April 2026, 22:53
Can someone help me out to find the right settings on this example? The levels change a lot when this speaker is changing pages and it kind of flickers on other parts as well. I guess the desired values are those when changing the page, at least the contrast seems to be better. Is there a way to keep this more constant?

Example with autolevels: https://limewire.com/d/Az2ci#nyJNn1bbJI

Example without autolevels: https://limewire.com/d/UeKwX#pBtTFmZHFJ


I'm just using AutoLevels (filterRadius=25, autogamma=false)

Hoping on your expertise, folks:-)