View Full Version : Adjust luma of pixels within a specific luma range


Katie Boundary
7th May 2022, 23:27
Goal: increase overall brightness of a video clip but don't obliterate detail in bright areas.

Desired method:

- For all pixels with luma 0-127, increase luma by 50% (so 0 stays 0 and 127 becomes 191)
- For all pixels with luma 128-255, cut luma in half and add 127.5 (so 128 becomes 192 and 255 stays 255)
- Obviously don't do them in that order. Either do them at the same time or brighten the already-bright pixels first.

What filters can do this?

poisondeathray
7th May 2022, 23:57
- For all pixels with luma 0-127, increase luma by 50% (so 0 stays 0 and 127 becomes 191)
- For all pixels with luma 128-255, cut luma in half and add 127.5 (so 128 becomes 192 and 255 stays 255)
- Obviously don't do them in that order. Either do them at the same time or brighten the already-bright pixels first.

What filters can do this?


Probably with expr for the math operations, mt_binarize for the mask selection (there probably is a more elegant way for the range selection), and mt_merge or overlay for the mask


BlankClip(length=1, width=256, height=256, pixel_type="YV12")
mt_lutspa(mode="relative closed", expr="x 255 *", U=128,V=128)
src=last

a=src.expr("x 1.5 *", "x", "x") #multiply by 1.5
b=src.expr("x 2 / 127.5 +", "x", "x") #divide by 2, add 127.5

lowmsk=src.mt_binarize(127, upper=true) #0-127
himsk=src.mt_binarize(127) #128-255

mt_merge(src, a, lowmsk, luma=true)
mt_merge(last, b, himsk, luma=true)

wonkey_monkey
8th May 2022, 01:57
Goal: increase overall brightness of a video clip but don't obliterate detail in bright areas.

Desired method:

- For all pixels with luma 0-127, increase luma by 50% (so 0 stays 0 and 127 becomes 191)
- For all pixels with luma 128-255, cut luma in half and add 127.5 (so 128 becomes 192 and 255 stays 255)
- Obviously don't do them in that order. Either do them at the same time or brighten the already-bright pixels first.



Why would you choose this when you could have a smooth function instead?

And bear in mind this is going to change your black and white levels from the expected 16 and 235 respectively.

Probably with expr for the math operations, mt_binarize for the mask selection (there probably is a more elegant way for the range selection), and mt_merge or overlay for the mask

You can just take the min of the two clips, or even better just combine the two exprs instead of using mt_binarize and mt_merge:

src.expr("x 1.5 * x 2 / 127.5 + min", "x", "x")

Though as I say, why anyone would use a non-smooth function for this purpose is beyond me...

Katie Boundary
8th May 2022, 02:35
All right... I don't fully understand what you did there, but I'm trying to learn how some of those tools work and build something similar. So far, I have:

dark=mpeg2source("hp07.d2v").tweak(cont=1.5)
lite=mpeg2source("hp07.d2v").tweak(cont=0.5,bright=127.5)
mask=mpeg2source("hp07.d2v").mt_binarize(127)

mt_merge(dark,lite,mask)

Unfortunately, it's not having quite the desired effect. Here's something similar to what I'm trying to achieve:

https://i.imgur.com/1fDVrHj.jpg

And here's what I'm getting:

https://i.imgur.com/kbcdzAd.jpg

What am I doing wrong?

ook ook! ook?

lol silly monkey.

poisondeathray
8th May 2022, 03:41
All right... I don't fully understand what you did there, but I'm trying to learn how some of those tools work and build something similar. So far, I have:

dark=mpeg2source("hp07.d2v").tweak(cont=1.5)
lite=mpeg2source("hp07.d2v").tweak(cont=0.5,bright=127.5)
mask=mpeg2source("hp07.d2v").mt_binarize(127)

mt_merge(dark,lite,mask)

Unfortunately, it's not having quite the desired effect. Here's something similar to what I'm trying to achieve:

<snip>

And here's what I'm getting:

<snip>

What am I doing wrong?




It's probably helpful to look at a waveform (histogram in avisynth, you can turn it so looks like a more traditional one)

The test clip given earlier is a perfect gradient, each x-coordinate is the Y' value. eg. So at xpos = 128, the Y channel value is 128

Tweak without coring=false, will clip to 16-235

Those tweak parameters that you are using are not a smooth function at the inflection point when you merge it

https://i.postimg.cc/L871HJnj/stack000000.png (https://postimages.org/)


This was the original expr , that you asked for, it's mathematically correct for what you asked, it would still look ugly on real video, because the angle is not smooth

https://i.postimg.cc/rw6TMcqK/expr000000.png (https://postimages.org/)


As W_M alluded to, most masks for real video usage have gradations, or feathering , so you don't get sharp transitions.

It might be easier to use levels, using the waveform to guide your adjustments. Curves are helpful too, because of non linear adjustments and interpolation between control points. Dogway has a luma mask function so instead of a binary mask , transitions are smoother

Katie Boundary
8th May 2022, 04:25
Well, I found what looks like a winner:

dark=mpeg2source("hp07.d2v").converttorgb().generalconvolution(matrix="0 0 0 0 384 0 0 0 0",divisor=256,auto=false).converttoYV12()
lite=mpeg2source("hp07.d2v").converttorgb().generalconvolution(matrix="0 0 0 0 128 0 0 0 0",divisor=256,auto=false,bias=127).converttoYV12()
mask=mpeg2source("hp07.d2v").mt_binarize(127)

mt_merge(dark,lite,mask)

So I think the "Cont" parameter in the Tweak filter was just not described correctly.

Katie Boundary
8th May 2022, 04:28
Tweak without coring=false, will clip to 16-235

Bloody hell, that turned out to be the problem. Thanks!

wonkey_monkey
8th May 2022, 14:02
Well, I found what looks like a winner:

dark=mpeg2source("hp07.d2v").converttorgb().generalconvolution(matrix="0 0 0 0 384 0 0 0 0",divisor=256,auto=false).converttoYV12()
lite=mpeg2source("hp07.d2v").converttorgb().generalconvolution(matrix="0 0 0 0 128 0 0 0 0",divisor=256,auto=false,bias=127).converttoYV12()
mask=mpeg2source("hp07.d2v").mt_binarize(128)

mt_merge(dark,lite,mask)




Apart from the fact that this doesn't do what you originally asked for in your question, have you seen the jagged mess it makes of the levels?

https://i.imgur.com/hUg45O8.png

There's a seemingly unavoidable discontinuity in there due to the lack of precision.

wonkey_monkey
8th May 2022, 14:15
This is 6x faster and gives a much less jagged (and continuous) result:

conv = convertbits(32).converttoplanarrgb.expr("x 1.5 * x 0.5 * 0.5 + min").convertbits(8).converttoyv12

https://i.imgur.com/KChGZ7V.png

StainlessS
8th May 2022, 14:31
Dont think Katie yet uses Avs+, so all that there convertBits and expr stuff is probably just being ignored. (one never can tell with Katie) :)

poisondeathray
8th May 2022, 15:25
I think smoothcurve from smoothadjust is avs classic compatible

smoothcurve(Ycurve="0-0;127-191;255-255")
https://i.postimg.cc/QMTm6XGJ/smoothcurve-Ycurve-0-0-127-191-255-255.png

Katie Boundary
8th May 2022, 18:13
Smoothadjust shall make a fine addition to my collection, though I'm happy with the results of Tweak and Masktools.

The big issue here isn't whether the "waveform" is sharp or smooth. The big issue is a phenomenon that I can best describe as "the law of conservation of contrast". It's not possible to increase the brightness and contrast in dark areas without destroying contrast in some other luma range, so depending on the exact luma ranges selected and how much they are expanded or crushed, it's very easy to accidentally make your video footage look like a deep-fried abortion, or like your graphics card is running in 256-color mode. Since fine details are already very difficult to see in bright areas, I've adjusted my method so that pixels with a luma of 0-63 get boosted by 50%, pixels with a luma of 64-191 get cut to 75% and then boosted by a flat 48, and the 192-255 luma range is untouched. This requires two masks and the results are pretty good.

I plan to experiment with using variations of this approach to reverse the effects of film-bleaching, as mentioned in another thread.

Dont think Katie yet uses Avs+, so all that there convertBits and expr stuff is probably just being ignored. (one never can tell with Katie) :)

I think it has more to do with the fact that Wonkey is literally on my ignore list so I can't see his posts.

Sharc
8th May 2022, 18:33
What filters can do this?

Did you try VirtualDub's "Gradation Curves" filter (gradation.vdf)?
https://neosol.at/vdub/index.html

Edit:
Also available for AviSynth+:
https://github.com/magiblot/gradation
https://github.com/magiblot/gradation/releases/tag/r50

StainlessS
9th May 2022, 12:17
I think it has more to do with the fact that Wonkey is literally on my ignore list so I can't see his posts.
Bad choice, self inflicted injury.
Also, PDR used Expr() in post #2, without any received comment.

Katie Boundary
9th May 2022, 12:58
Bad choice, self inflicted injury

Wonkey was given plenty of chances to prove that his posts weren't a complete waste of time, brain cells, and CPU cycles, and he failed. But let's stay on topic. This isn't about him.