Log in

View Full Version : Resize to 1 pixel wide?


um3k
26th February 2011, 07:22
Is it possible to resize a frame to 1 pixel wide in avisynth? I'm getting the error "source image too small for this resize method". The frame I'm starting with is 704 pixels wide. The goal is to average the pixels horizontally, so if there is an alternative way to do that, I'd be fine with it.

Gavino
26th February 2011, 12:18
For technical reasons, the final width must be greater than the number of 'taps' on the resize filter used, so a width of 1 is not possible with any of the resizers (except of course PointResize).

But even if it was permitted, it wouldn't give you a plain average anyway - it would be a weighted average, with different weights for each pixel depending on its distance from the centre.

One way to get what you want is a horizontal box blur with a box whose width is equal to the image width, and then crop out the centre column as your final result. However this only works exactly if your initial image width is an odd number.

jmac698
26th February 2011, 13:19
Hi,
That's not too hard. First, you can get a full pixel average in masktools,

a=blankclip(pixel_type="YV12",color_yuv=$808080)
b=blankclip(pixel_type="YV12",color_yuv=$408080)
stackhorizontal(a,b)
mt_lutf(last,last,mode="avg",yexpr="x")

The test video has squares of 64 and 128 brightness, the average should be 96. The lutf command returns the pixel average, which is 96 everywhere. But now you need to work only on each line, so use my split lines function with it:

#Line Average v0.1 by jmac698
#Averages each horizontal line. Doesn't change video resolution.
#Requires GRunT, MaskTools v2
#set your source here
splitlines(1)
mt_lutf(last,last,mode="avg",yexpr="x")
MergeLines(480)

function SplitLines(clip c, int n) {#duplicates then crops each copy in a different spot
Assert(c.height%n == 0, "Clip height not a multiple of 'n'")
Assert(!(c.IsYV12() && n%2==1), "'n' must be even for YV12 clips")
nStrips = c.height/n
c = c.ChangeFPS(nStrips*Framerate(c)).AssumeFPS(c) # Repeat each frame 'nStrips' times
BlankClip(c, height=n) # template for ScriptClip result
GScriptClip("c.Crop(0, (current_frame%nStrips)*n, 0, n)", args="c, nStrips, n")
}

function MergeLines(clip c, int n) {MergeLines2(c,n,n)}

function MergeLines2(clip c, int n,int i) {
i<2?c.SelectEvery(n):stackvertical(MergeLines2(c,n,i-1),c.SelectEvery(n, i))
}

Gavino
26th February 2011, 14:11
splitlines(1)
mt_lutf(last,last,mode="avg",yexpr="x")
Catch 22! :(
Single lines are only possible with RGB, but mt_lutf requires YV12 (or planar).

However, you could do this:
h = height
PointResize(width, h*2).ConvertToYV12()
splitlines(2)
mt_lutf(last,last,mode="avg",yexpr="x")
MergeLines(h)
ConvertToRGB().PointResize(width, h)
# or use PointResize(1, h) to get 1-pixel wide result

um3k
26th February 2011, 16:59
Thanks, guys. I found a workaround on my own, simply resizing to the minimum (8 pixels), then using HorizontalReduceBy2 three times to get it down to 1 pixel. I'm sure it's nowhere near exact, but the results were satisfactory for my purposes. If you're interested, here's a peek:

https://lh6.googleusercontent.com/_MCBF9vFZLks/TWipsgF78tI/AAAAAAAAQyQ/na4QTN80IXc/s640/20s_MovieBarcode.png (https://lh6.googleusercontent.com/_MCBF9vFZLks/TWipsgF78tI/AAAAAAAAQyQ/na4QTN80IXc/s1280/20s_MovieBarcode.png)
Based on this website: http://moviebarcode.tumblr.com/

Script:
function MovieBarcode(clip src, int w, int h)
{
GScript("""
src = src.ConvertToRGB24()
comp = src.BlankClip(width=1, height=h)
for(f=1,w,1)
{
fc = src.framecount
cf = (fc / w) * f
s = src.SelectEvery(fc, cf).BilinearResize(8, h).HorizontalReduceBy2().HorizontalReduceBy2().HorizontalReduceBy2()
comp = StackHorizontal(comp, s)
}
return comp.Crop(1, 0, 0, 0).Trim(0,1)
""")
}
Thanks, Gavino, for GScript. I probably could have done this particular script with recursion, but the for loop was much more straightforward.

The movie is the one in my sig...

Gavino
27th February 2011, 13:00
Thanks, guys. I found a workaround on my own, simply resizing to the minimum (8 pixels), then using HorizontalReduceBy2 three times to get it down to 1 pixel. I'm sure it's nowhere near exact, but the results were satisfactory for my purposes.
Actually, the minimum for BilinearResize is 2 pixels, so you could have got away with a single HorizontalReduceBy2. What you have certainly isn't a mathematical average (as pixels nearer the centre of the frame will have more weight), but if you're just looking for some way to blur all pixels horizontally into one, that's fine.

BTW If the idea was to create a single frame result, the Trim(0,1) at the end of your code should be Trim(0, -1). (The second frame consists of repeated strips from the last frame of the source.)

spoRv
3rd March 2017, 17:26
I made a small revision; now each single bar is not a resized frame, but its average color; result is really similar, but now it's "smoother", and, when saved as image, takes a lot see space.

function MovieBarCode2(clip clip, int "w", int "h")
{
GScript("""
w=default(w,1280) h=default(h,480)

clip.ConvertToYV24
src = mt_lutf(last, mode="avg", expr="x", u=3, v=3)
comp = src.BlankClip(width=1, height=h)

for(f=1,w,1)
{
fc = src.framecount
cf = (fc / w) * f
s = src.SelectEvery(fc, cf).PointResize(1, h)
comp = StackHorizontal(comp, s)
}

return comp.Crop(1, 0, 0, 0).Trim(0,-1)

""")

}

martin53
3rd March 2017, 17:42
just because ...

mt_luts(last,last,mode="avg",pixels=mt_rectangle(0,height),expr="y",u=3,v=3)

wonkey_monkey
3rd March 2017, 22:44
"Fingerprinting" was previously covered here:

http://forum.doom9.org/showthread.php?t=167769

There's a plugin in there somewhere, though it probably won't work for very long clips.

spoRv
4th March 2017, 00:47
Thanks, it was pointed also in my other thread here: https://forum.doom9.org/showthread.php?t=174358 - a very nice plugin indeed!

@martin53: I'm a bit more than a smart monkey, so the added code was merely a copy&paste I found somewhere here - can't remember where, though... (EDIT: found here (https://forum.doom9.org/showthread.php?p=1552534#post1552534), thanks Gavino!) so, I replaced that line of code with yours, but result is identical...

martin53
4th March 2017, 15:53
After reading your function more thoroughly, I think my post was a bit off topic. There, mt_luts() will make the columns of an already existing frame the average of all pixels in the column. But your script builds a single fingerprint frame from a frame sequence input clip, so it's not the same.