Log in

View Full Version : New Script: Drawrect 0.6


Pages : [1] 2

jmac698
12th October 2011, 01:37
Another one of my utility functions.

#Drawrect 0.6 by jmac698
#a script to draw rectangles on top of existing video or create masks
#requires masktools v2a45+ (mode in lutspa) and yv12 video
#0.6: Added even faster option, bool "chroma", simplified code
#0.5: Speedup and simplification (thanks Gavino, yet again), new function createrect, improved code
#0.4: Added calculated named colors, removed old system
#0.3: Added fully opaque with opacity=256, now default. Fixed bugs. Added colors. Refactored. Outline option.
# Bugs: When opacity=256, the color edges are not merged,
#todo: finish deepcolor support, more shapes

#Usage:
#createrect(clip c, int x1, int y1, int x2, int y2, int "foreclr" (255), int "backclr" (0), bool "filled" (true), bool "chroma" (false)) This is meant to create a simple rect mask
#drawrect(clip c, int x1, int y1, int x2, int y2, int foreclr, int "opacity" (256), bool "filled" (true), bool "chroma" (false)) This is meant to draw on existing video
#opacity=256 for fully opaque

colorbars(pixel_type="YV12")
createrect(400,400,419,419)#Great for making masks, a square with luma=255 and 0 elsewhere. Note that chroma is untouched by default!
#Now use drawrect to add more shapes onto the first mask
drawrect(1,1,150,200,named_color("cyn"), filled=false)#a cyan square
drawrect(1,40,300,40,named_color("red"))#a red horizontal line (notice at y=41 the color overwrites the background)
drawrect(280,300,319,340,$FF8080,opacity=127)#transparent white rectangle
drawrect(-10,-10,5,5,$B08080)#test behavior with offscreen coordinates


function drawrect(clip c, int x1, int y1, int x2, int y2, int foreclr, int "opacity", bool "filled", bool "chroma"){
#A function to draw a filled rectangle into a YV12 clip, can also draw lines as (1,1,1,200,$7E8080): a vertical line 1 pixel wide from 1,1 to 1,200
#Note that chroma will be merged with the input clip; draw on mod 2 boundaries if you need to ensure the given pixel chroma
#The opacity formula is (opacity*foreground+(255-opacity)*background+128)/256
#You can't draw 255 on black unless you use opacity=256, because opacity=255 gives output=254
#If create=true, it just draws a single rectangle
#chroma=true (default) to process chroma. This function is meant to draw colored rect's on video, so defaults to true.
#(It can also build masks with more than one rect)
filled=default(filled, true)
opacity=default(opacity,256)
chroma=default(chroma, true)
y=getword(foreclr, 2)#byte=2 (n*65536), wordsize=1
u=getword(foreclr, 1)
v=getword(foreclr, 0)
#"x>=xl & x<=xh & y>=yl & y<= yh ? Y : 16" y is the coordinate y, Y is a variable which is substituted as a number in the expression
#"x x1 >= & x xh <= y yl >= & y yh <= & & Y 16 ?"
xl=min(x1, x2)#sort the coordinates
xh=max(x1, x2)
yl=min(y1, y2)
yh=max(y1, y2)
xl2=ceil(xl/2)#chroma coordinates in YV12 are half, ensure they are just larger than luma size
xh2=ceil(xh/2)
yl2=ceil(yl/2)
yh2=ceil(yh/2)
m=opacity==256?255:256
chromamode=chroma?3:1
drawstry2=drawstr(y, m)
drawstru2=drawstr(u, m)
drawstrv2=drawstr(v, m)
drawstrm=buildrectexp(xl, yl, xh, yh, opacity, 0, filled)
drawstrmuv=buildrectexp(xl2, yl2, xh2, yh2, opacity, 0, filled)
mask=mt_lutspa(c, mode="absolute", yexpr=drawstrm, uexpr=drawstrmuv, vexpr=drawstrmuv, u=chromamode, v=chromamode)
mt_lutxy(c, mask, yexpr=drawstry2, uexpr=drawstru2, vexpr=drawstrv2, u=chromamode, v=chromamode)
}

function createrect(clip c, int x1, int y1, int x2, int y2, int "foreclr", int "backclr", bool "filled", bool "chroma"){
#chroma=true (default) to process chroma. This function is meant to draw white rect's for masks, so defaults to false.
foreclr=default(foreclr, $FF8080)
backclr=default(backclr, $008080)
filled=default(filled, true)
chroma=default(chroma, false)
yf=getword(foreclr, 2)#byte=2 (n*65536), wordsize=1
uf=getword(foreclr, 1)
vf=getword(foreclr, 0)
yb=getword(backclr, 2)
ub=getword(backclr, 1)
vb=getword(backclr, 0)
xl=min(x1, x2)#sort the coordinates
xh=max(x1, x2)
yl=min(y1, y2)
yh=max(y1, y2)
xl2=ceil(xl/2)#chroma coordinates in YV12 are half, ensure they are just larger than luma size
xh2=ceil(xh/2)
yl2=ceil(yl/2)
yh2=ceil(yh/2)
chromamode=chroma?3:1
drawstry=buildrectexp(xl, yl, xh, yh, yf, yb, filled)
drawstru=buildrectexp(xl2, yl2, xh2, yh2, uf, ub, filled)
drawstrv=buildrectexp(xl2, yl2, xh2, yh2, vf, vb, filled)
mt_lutspa(c, mode="absolute", yexpr=drawstry, uexpr=drawstru, vexpr=drawstrv, u=chromamode, v=chromamode)
}

function drawstr(int clr, int m) {
return string(clr)+ " y * "+string(m)+" y - x * + "+string(m)+" /"
}

function buildrectexp(int xl, int yl, int xh, int yh, int clrfore, int clrback, bool filled){
#if x=x1 or x=x2 and y>=y1 and y<=y2 or y=y1 or y=y2 and x>=x1 and x<=x2
#x x1 = x x2 = or y y1 >= y y2 <= and and y y1 = y y2 = or x x1 >= x x2 <= and and or
filled?"x "+string(xl)+" >= x "+string(xh)+" <= & y "+string(yl)+" >= y "+string(yh)+" <= & & "+string(clrfore)+" "+string(clrback)+" ?" \
:"x "+string(xl)+" = x "+string(xh)+" = | y "+string(yl)+" >= y "+string(yh)+" <= & & y "+ \
string(yl)+" = y "+string(yh)+" = | x "+string(xl)+" >= x "+string(xh)+" <= & & | "+string(clrfore)+" "+string(clrback)+" ?"
}

function named_color(string color, string "colorspace", float "luma", float "sat", bool "deepcolor") {
#calculate the named color in yuv with specificed saturation and luminance
color=lcase(color)
colorspace=default(colorspace, "rec601")
luma=default(luma, .75)
sat=default(sat, .75)
deepcolor=default(deepcolor, false)
#Define in rgb, it's easiest
colorrgb="111"
colorrgb=color=="yel"||color=="yellow"?"110":colorrgb
colorrgb=color=="cyn"||color=="cyan"?"011":colorrgb
colorrgb=color=="grn"||color=="green"?"010":colorrgb
colorrgb=color=="mag"||color=="magenta"?"101":colorrgb
colorrgb=color=="red"?"100":colorrgb
colorrgb=color=="blu"||color=="blue"?"001":colorrgb
colorrgb=color=="blk"||color=="black"?"000":colorrgb
r=midstr(colorrgb, 1, 1)=="1"?1:0
g=midstr(colorrgb, 2, 1)=="1"?1:0
b=midstr(colorrgb, 3, 1)=="1"?1:0
#Need to be more flexible. -I=(-.956295,.272558,1.104744)
kr601=.299
kg601=.587
kb601=.114
kr709=.2125
kg709=.7154
kb709=.0721
y=colorspace=="rec601"?kr601*r+kg601*g+kb601*b:kr709*r+kg709*g+kb709*b
pb=colorspace=="rec601"?(b-y)/(1-kb601):(b-y)/(1-kb709)
pr=colorspace=="rec601"?(r-y)/(1-kr601):(r-y)/(1-kr709)
scale=deepcolor?65536:256
yscale=deepcolor?219*219:219*luma
cscale=deepcolor?112*112:112*sat
round(y*yscale+scale/16)*scale*scale+round(pb*cscale+scale/2)*scale+round(pr*cscale+scale/2)
}

function hexbyte(int n) {
#This function returns the lower byte of n as a 2 digit hex string.
#For example, hex(257)="01".
n=n%256
h=floor(n/16)
l=n%16
s1=h>9?h+65-10:h+48
s2=l>9?l+65-10:l+48
chr(s1)+chr(s2)
}

function hex(int n){
#returns 3 byte hex string
d2=floor(n/65536)
n=n-d2*65536
d1=floor(n/256)
n=n-d1*256
hexbyte(d2)+hexbyte(d1)+hexbyte(n)
}

function getword(int n, int p, int "size"){
#Get word p of size size from n, limited to p=(0,2)
#Warning: due to a bug in avisynth 2.6a3, p=2 and size=2 gives wrong answrs
size=default(size, 1)
f=pow(2,8*size*2)
p2=floor(n/f)
n=n-p2*f
f=pow(2,8*size*1)
p1=floor(n/f)
n=n-p1*f
p0=n
int(p==0?p0:p==1?p1:p2)
}

jmac698
12th October 2011, 05:24
Ugh, bug upon bug... first of all, avspmod 2.05 doesn't show the correct YUV values in the status line.
Now that I'm using 2.20 which works, I find that there's a bug in my function, due to the way mt_merge works.
It calculates (x*256+128)/256 and when x=255 (when drawing a white line), I'm getting 254 back, so it's NOT drawing the correct colors.
I'll have to investigate later.

Dogway
15th October 2011, 20:03
Yes, I was just about to tell you that, but looks you already realised. Can I suggest you a few additions? for example the ability to make white and black colors, if I write $ffffff then I get 235, and sometimes I may want 255 so I can use it as a mask. For the same reason it would be good if you could also add a background color, that is empty (bypass to clip) or some color like black for masks. Thanks for these tools!

tin3tin
15th October 2011, 22:39
For inspiration I like to mention some drawing scrips I did some time ago(lines and circles):
http://forum.doom9.org/showthread.php?t=158194

jmac698
15th October 2011, 23:42
Ahh, thanks. I've done those two in assembly before :) There's an integer way to draw lines. I might look into layer...

jmac698
15th October 2011, 23:44
Here's a quick fix, change this line:

opacity==256?mt_lutxyz(c, mask, last,expr="y 255 = z x ?"):mt_merge(c, last, mask, luma=true)

For a background, just start with a blankclip.

Dogway
16th October 2011, 00:17
It takes some loading time but thanks it works! for background I will be using mt_lut("0",u=-128,v=-128), I want to switch everything related to layer, overlay, and blankclip to masktools, there comes my interest in your tools

jmac698
23rd October 2011, 05:29
It's updated, with a bunch of new features.

jmac698
25th October 2011, 23:41
bump
ver 0.4

jmac698
26th October 2011, 00:00
dogway,
bad news, this approach is incredibly slow, for more than a few shapes. It's building big tables for every call at initialization time. Back to blankclip and layer (avoid overlay, too many conversions).

edit
ok, anyone have a suggestion for a better way to draw shapes in script, or am I going to have to write a plugin? I don't have time to write a plugin now though.

If I had to draw a shape, I would have to use layer to draw 4 lines. The docs are a little sketchy, talking about "these only seem to work in yuy2".

Dogway
26th October 2011, 09:52
Well I have been using the next code relating to boxes, I just thought your procedures were more "right to the spot", orthodox to call it some way.

function BoxMask(clip clip, int "x1", int "y1", int "x2", int "y2", bool "show"){

w=clip.width()
h=clip.height()
x1=default(x1,0)
y1=default(y1,0)
x2=default(x2,w)
y2=default(y2,h)
show=default(show, false)

clip.mt_lut("255",u=-128,v=-128).crop(w-(x2-x1),h-(y2-y1),0,0)
AddBorders(x1,y1,w,h).crop(0,0,w,h)
show?mt_merge(clip,last,mt_lut(" x 17 < 0 x 2 / - ?"),u=-128,v=-128):mt_lut(" x 17 < 0 x ?")}

Gavino
26th October 2011, 12:19
function BoxMask(clip clip, int "x1", int "y1", int "x2", int "y2", bool "show"){
...
clip.mt_lut("255",u=-128,v=-128).crop(w-(x2-x1),h-(y2-y1),0,0)
AddBorders(x1,y1,w,h).crop(0,0,w,h)
show?mt_merge(clip,last,mt_lut(" x 17 < 0 x 2 / - ?"),u=-128,v=-128):mt_lut(" x 17 < 0 x ?")}
Can't you do it all with a single call to mt_lutspa with an expression which tests to see if x and y are within the relevant ranges?

Dogway
26th October 2011, 12:53
I don't know... I don't understand mt_lutspa, even the example provided doesn't work for me.

jmac698
26th October 2011, 14:07
It's easy

mt_lutspa(c, mode="absolute", yexpr="x 0 >= x 91 <= & y 0 >= y 239 <= & & 180 16 ?")

mode="absolute" means that, if you use x/y in the yexpr, it's the coordinates from x=(0,width-1) and y=(0, height-1).
Next is just what to draw,
if x>=0 & x<=91 & y>=0 & y<=239 then 180 else 16
Which fills in a box from (0,0)-(91, 239) with 180. The outside of the box is 16.

And actually Gavino gave me an idea, if I could collect all the shapes at once, I can make a huge expression to plot them all at the same time, which would be faster ;) The problem is overlap, I don't know how it would react to that.

Dogway
26th October 2011, 16:48
yeeeeee, so cool, this is very straight forward. Now that it works it's easy to understand. But in the documentation the code provided didn't work:
mt_lutspa(relative = true, "x y + 256 * 2 /", chroma = "128" )

Gavino
26th October 2011, 17:04
The example is wrong, it should be:
mt_lutspa(relative = true, expr="x y + 256 * 2 /", chroma = "128" )

EDIT: The latest documentation has the example correct.
The earlier one possibly worked before the parameter order got changed in v43.

Dogway
26th October 2011, 18:22
Yes, I found it, it was a bit hidden (masktools-v2.0a48.zip\masktools\documentation) so I was using another old html from somewhere. Thanks!

jmac698
26th October 2011, 22:25
I've asked manano to update his webpage, it's happened many times that people are confused by the outdated documentation.
And beware of using expr with chroma="process", because the chroma resolution is different, the expression will be 2x bigger than you are intending, for example the box, will have a double sized box in chroma with color=180, certainly not what you intend!

redfordxx
3rd November 2011, 09:09
Well I glanced on the script...I think you can do pretty much anything with one mt_lutspa call... so why all these lutxyz and merges...unless there is a lenght limit on expr in masktools;-)

jmac698
3rd November 2011, 13:12
Because I'm not just creating an image, I want to draw on top of an existing image. If you just want to create an image, of course you can skip the rest.

redfordxx
4th November 2011, 09:20
OK, i see:
Few options how to deal with this issue depending how much rounding errors bother you...
Let's clear

#drawsrtm5=lutexpression resulting in mask (0,255)
#drawsrtm6=lutexpression resulting in mask (0,256)
#drawsrtm16=lutexpression resulting in 16bit mask (0,65535)
#if no better idea
#drawsrtm16=drawsrtm5 256 * drawsrtm5 128 - 2 *
#drawsrtm6=drawsrtm16 256 /

How about few variants of this

picture=mt_lutspa(c,drawstry+ " 256 "+drawstrm6+" - * 128 + 256 /")
mask=mt_lutspa(c,drawstrm5)
mask2=mt_lutspa(c,drawstrm5+" 128 - 2 *")
result1=mt_lutxy(c,mask,"x y * 128 + 256 /") #i don't know any faster multiplication algorithm axcept my modification of AverageM
result2=mt_lutxy(c,mask2,"x y * 128 + 256 /")
Average(picture, 1.0, result1, 1.0, result2, 1/256)
Which should be ok for your opacity issue but not so good for rounding errors

redfordxx
4th November 2011, 09:37
Unless you want to go to higher bitdepth like dither...
...this should be fast and precise

picture=mt_lutspa(c,drawstry)
#4 masks (I still believe, lutspa runs only once per clip, so this should be no efficiency issue, otherwise and in case of real masks we might need binarize...)
m_up=mt_lutspa(c,drawstrm6+" 128 -")
m_dn=mt_lutspa(c,drawstrm6+" 128 max")
m_n_up=mt_lutspa(c,"256 "+drawstrm6+" - 128 -")
m_n_dn=mt_lutspa(c,"256 "+drawstrm6+" - 128 max")

AverageM(c, m_up, c, m_dn, picture, m_n_up, picture, m_n_dn)
iirc AverageM does in this case
(c*m_up+c*m_dn+picture*m_n_up+picture*m_n_dn+128)/256
which should be quite fast. Definitely faster than your script.
Moreover, you can use (0;256) masks this way...
But, it does all color planes always...

Average thread (http://forum.doom9.org/showthread.php?t=100626)

TheFluff
8th November 2011, 15:30
Excuse me for being the devil's advocate, but why exactly have you written 100 lines of masktools voodoo to do something that is trivially accomplished with blankclip, crop, stack(vertical|horizontal) and overlay? There are also several other existing ways to do this using various plugins (textsub and assrender allow you to render arbitrary vector drawings with subpixel precision, for example), most of which render roughly an order of magnitude faster.

I understanding coding for the sake of coding and being fascinated with one's own cleverness, but this does seem a bit excessive.

jmac698
8th November 2011, 17:15
I looked at textsub and assrender, and see no way that they can draw arbitrary shapes. They both take a special text file. About the only way to draw anything might be with spaces, and even then it would be very difficult to specify they're exact coordinates. Finally, they don't accept any kind of yuv colors.

We both tried blank/crop/stack, it's slow and quite awkward (not trivial), but the killer is overlay is not precise enough. It converts everything to it's internal format, mixes, then converts back again, making it slow and imprecise.

Both crop and overlay have bugs:
http://avisynth.org/mediawiki/Known_Issues

By all means, if you can think of a way to draw a few types of shapes in YUV directly, quickly and accurately, please let me know! Most people seem to want only masks of 255 in certain shapes, but I want to make specifically test patterns (not just colorbars).

AzraelNewtype
8th November 2011, 19:48
I looked at textsub and assrender, and see no way that they can draw arbitrary shapes. They both take a special text file. About the only way to draw anything might be with spaces, and even then it would be very difficult to specify they're exact coordinates.


So what you're saying here is, you looked at textsub and assrender as plugins, but did not spend any time whatsoever looking at the ass format that they, er... render before coming to this conclusion? Because you're completely wrong, and TheFluff is absolutely correct.

jmac698
8th November 2011, 20:20
No, I did look at the formats. Here's a link: http://matroska.org/technical/specs/subtitles/ssa.html
I also read both manuals. No mention of drawing shapes. You don't have to use such wording; if there's no obvious mention of how to do this, it's understandable that I can't find it. I looked again and still can't find it.

I didn't explain a further point of using text files; this doesn't help me draw shapes programaticaly (sp?). As I mentioned I'm making test images; some of them I'd like to animate as well.

redfordxx
8th November 2011, 22:02
I have one more thing for you.
Whenever you use lutspa, put .trim(1,1).Loop(c.framecount,0,0) after it. It will boost your speed extremely.

Gavino
8th November 2011, 22:09
We both tried blank/crop/stack, it's slow and quite awkward (not trivial), but the killer is overlay is not precise enough. It converts everything to it's internal format, mixes, then converts back again, making it slow and imprecise.

Both crop and overlay have bugs:
http://avisynth.org/mediawiki/Known_Issues
These bugs only affect RGB.
Also, conversion in Overlay doesn't change luma of YUV inputs, though yes, YUY2 and YV12 chroma will be internally upsampled to 4:4:4.

But in any case, your function is far more complex than it need be. There is no need to use (the extremely slow loading) mt_lutxyz, or to have a special case opacity=256. You can use opacity=255 to mean full opacity, and implement the function as follows:
1. Create the (luma and chroma) mask using mt_lutspa, setting pixels to 'opacity' in the drawing region and zero elsewhere.
2. Use mt_lutxy to process the input clip and mask to set
output = (mask*color + (255-mask)*input)/255
where color is the specified drawing color (for each of y, u, v).
That's it!

TheFluff
8th November 2011, 22:38
No, I did look at the formats. Here's a link: http://matroska.org/technical/specs/subtitles/ssa.html
I also read both manuals. No mention of drawing shapes. You don't have to use such wording; if there's no obvious mention of how to do this, it's understandable that I can't find it. I looked again and still can't find it.

You're looking at a very brief summary of an incomplete document that not only is well over a decade old, it was also a very inaccurate description of what actually was implemented in subtitle renderers even when it was brand new.

The least incomplete documentation of the ASS format that I am aware of is available in the manual for the Aegisub subtitle editor (http://docs.aegisub.org/manual/ASS_Tags). There are several modified versions of VSFilter (textsub) floating around that add their own custom extensions, but the stuff that the Aegisub manual covers is supported in all known modern versions. For the vector drawing stuff, take a look at the bottom of the page linked above, under the heading "Drawing commands".

That said, even the ancient document I was referring to above (http://moodub.free.fr/video/ass-specs.doc), which the Matroska page you mention links to, mentions the drawing tags. The syntax is still the same despite the format being old enough to drive a car in some jurisdictions.

A feature of the ASS format's vector drawing mode that is worth mentioning is that since it's actually a real vector drawing system, it uses its own internal coordinate system that is completely unrelated to the video pixel grid, which means that any shapes you draw with it can be rendered at any video resolution without modification of the drawing commands. It also means you can actually render with subpixel precision if you so desire.

I didn't explain a further point of using text files; this doesn't help me draw shapes programaticaly (sp?). As I mentioned I'm making test images; some of them I'd like to animate as well.

If you want to generate animated shapes programatically, Avisynth script seems like an exceptionally bad choice of programming language. Even if you use grunt and/or gscript it still seems like an incredibly annoying task. ASS subtitle files are plain text; it's rather trivial to generate them programatically in almost any language. There is also a multitude of existing tools for generating such files in various ways already. Aegisub, for example, comes both with a vector drawing tool that outputs ASS drawing commands, as well as with a complete scripting system intended for generating and manipulating ASS subtitle files (and much like with Avisynth scripting, there is a rather big library of user-contributed scripts written for this scripting system).

Gavino
9th November 2011, 09:45
You can use opacity=255 to mean full opacity, and implement the function as follows:
1. Create the (luma and chroma) mask using mt_lutspa, setting pixels to 'opacity' in the drawing region and zero elsewhere.
2. Use mt_lutxy to process the input clip and mask to set
output = (mask*color + (255-mask)*input)/255
where color is the specified drawing color (for each of y, u, v).
Specifically, what I meant was change the default opacity to 255 and change this code:
drawstry=buildrectexp(xl, yl, xh, yh, y, 16, filled)
drawstru=buildrectexp(xl2, yl2, xh2, yh2, u, 128, filled)
drawstrv=buildrectexp(xl2, yl2, xh2, yh2, v, 128, filled)
drawstrm=buildrectexp(xl, yl, xh, yh, opacity, 0, filled)
drawstrmuv=buildrectexp(xl2, yl2, xh2, yh2, opacity, 0, filled)
mergeit(c, drawstry, drawstru, drawstrv, drawstrm, drawstrmuv, opacity)
}

function mergeit(clip c, string drawstry, string drawstru, string drawstrv, string drawstrm, string drawstrmuv, int opacity){
mt_lutspa(c, mode="absolute", yexpr=drawstry, uexpr=drawstru, vexpr=drawstrv, chroma="process")
mask=mt_lutspa(c, mode="absolute", yexpr=drawstrm, uexpr=drawstrmuv, vexpr=drawstrmuv, chroma="process")
opacity==256?mt_lutxyz(c, mask, last, expr="y 255 = z x ?", chroma="process"):mt_merge(c, last, mask, luma=true, chroma="process")
}
to this:
drawstrm=buildrectexp(xl, yl, xh, yh, opacity, 0, filled)
drawstrmuv=buildrectexp(xl2, yl2, xh2, yh2, opacity, 0, filled)
mask=mt_lutspa(c, mode="absolute", yexpr=drawstrm, uexpr=drawstrmuv, vexpr=drawstrmuv, chroma="process")
mt_lutxy(c, mask, yexpr=drawstr(y), uexpr=drawstr(u), vexpr=drawstr(v), chroma="process")
}

function drawstr(int clr) {
return string(clr)+ " y * 255 y - x * + 255 /"
}

jmac698
9th November 2011, 17:48
@Dogway,


sometimes I may want 255 so I can use it as a mask. For the same reason it would be good if you could also add a background color, that is empty (bypass to clip) or some color like black for masks.

I added this feature for you, try createrect(x1,y1, x2,y2) it will do what you want, and it's fast.

@Gavino
Thanks, I used some of your suggestions, and still have a full range for opacity.

@Fluff
I found the documentation for drawing, but it only does lines and splines, so I can't draw a circle. Also it only specifies colors in RGB, so it's impossible to create certain YUV colors. And now my script is almost 170 lines, and that's because Avisynth is missing so many features.

That reminds me of the day I tried Gimp. I wanted to draw a circle, but couldn't figure out how. Then I read a doc which said "Gimp can't draw circles, but that's not what it's for...". I gave up on Gimp on that day.

@any
I'd like to make this deepcolor compatible, but I discovered yet another bug in 2.6a3 which prevents me.

Gavino
9th November 2011, 19:05
@Gavino
Thanks, I used some of your suggestions, and still have a full range for opacity.
I don't see the point of having 0-256 instead of 0-255.
The latter seems more natural to me and the former introduces an unnecessary special case into the code for no appreciable gain. But then it's your function, so please yourself. :)

BTW Your 'higher' and 'lower' functions can be replaced by 'min' and 'max' (since v2.58).

@Dogway,
I added this feature for you, try createrect(x1,y1, x2,y2) it will do what you want, and it's fast.
For mask creation, you normally don't care about the chroma.
Perhaps you want a (faster) option that just sets the luma.

jmac698
9th November 2011, 19:28
Updated. Thanks for the suggestion.

I have to use opacity(0,256) solely for the important case of 128, which gives an exact average. I could even add another special case for that and use averagem which is apparently faster. Oh, how horrid would that be :)

jmac698
9th November 2011, 19:38
Here's my function which will break if floats ever change in avisynth:

x1=getword1(65536*256+256*1+3,0)
x2=getword2(65536*256+256*1+3,0)
messageclip("Result of getword1="+string(x1)+" result of getword2="+string(x2))

function getword1(int n, int p, int "size"){
#Get word p of size size from n, limited to p=(0,2)
#Warning: due to the implementation in avisynth, p=2 and size=2 gives wrong answers
size=default(size, 1)
f=pow(2,8*size*2)
p2=floor(n/f)
n=n-p2*f#This subtraction won't work in some cases when n is int and f is float
f=pow(2,8*size*1)
p1=floor(n/f)
n=n-p1*f
p0=n
int(p==0?p0:p==1?p1:p2)
}

function getword2(int n, int p, int "size"){
#Get word p of size size from n, limited to p=(0,2)
#Warning: only works with an assumed implementation in avisynth
size=default(size, 1)
f=int(pow(2,8*size*2))
p2=n/f
n=n-p2*f
f=int(pow(2,8*size*1))
p1=n/f
n=n-p1*f
p0=n
int(p==0?p0:p==1?p1:p2)
}

Gavino
9th November 2011, 19:50
Here's my function which will break if floats ever change in avisynth:
Surely the right (and implementation independent) way to do this is by successive integer division (equivalent to shifting right)?

TheFluff
9th November 2011, 19:56
@Fluff
I found the documentation for drawing, but it only does lines and splines, so I can't draw a circle. Also it only specifies colors in RGB, so it's impossible to create certain YUV colors. And now my script is almost 170 lines, and that's because Avisynth is missing so many features.

That reminds me of the day I tried Gimp. I wanted to draw a circle, but couldn't figure out how. Then I read a doc which said "Gimp can't draw circles, but that's not what it's for...". I gave up on Gimp on that day.

Your script only draws rectangles, so I have absolutely no idea why you'd cite not being able to draw circles as a drawback of ASS. It's not even a very significant drawback, since you can get within epsilon of a perfect circle with little effort.

I don't see how "certain YUV colors" can be impossible to create either (the YUV<->RGB conversion formula is not exactly rocket science), and I most certainly don't see what the GIMP team's design decision to not make it a vector art tool has to do with all of this.

jmac698
9th November 2011, 20:03
I need to do my computations in the format (float or int) which has enough precision for them; in my case I want to extract words from a packed deepcolor value, which takes 3*16=48 bits. So it turns out I can't do this anyway. It's bad enough that Avisynth doesn't handle deepcolor, but it's a real pain when I can't even store a single color properly. I need much higher precision.

Another problem is trying to pass around multiple values in the language itself; it just can't be done. I guess my problem is the single return value isn't big enough to even pack my answer into.

amtm
9th November 2011, 20:12
The format of "float" will not change. "Float" will always be single-precision.

TheFluff
9th November 2011, 20:25
I need to do my computations in the format (float or int) which has enough precision for them; in my case I want to extract words from a packed deepcolor value, which takes 3*16=48 bits. So it turns out I can't do this anyway. It's bad enough that Avisynth doesn't handle deepcolor, but it's a real pain when I can't even store a single color properly. I need much higher precision.

Another problem is trying to pass around multiple values in the language itself; it just can't be done. I guess my problem is the single return value isn't big enough to even pack my answer into.

I'm sort of astounded that despite repeated proddings you still haven't seemed to realize that perhaps Avisynth scripting and masktools aren't the right tools for this job. What you want to do isn't complicated in the slightest, and I am absolutely astounded that you keep hacking on this bizarre mess rather than trying to find out a saner way to do it.

You could probably write a standalone C program that does what your script does on uncompressed video files in less lines of code than your entire script, even if you included file I/O operations.

jmac698
9th November 2011, 20:29
@Fluff

\1c&H<bb><gg><rr>&

This seems to set the primary fill color which is used by the shapes in draw mode. You'll note it only accepts 8 bits.

Two problems:
I'm afraid you don't understand the finer points of colorspaces. It would take negative rgb components to cover all the normal yuv colors. Here's an example calculation for -Q in Rec601:
y = 16, cb = 158, cr = 95
for rgb [16,235]: r = 219*(-0.2067)+16 = -29, g = 219*0.0588+16 = 29, b = 219*0.2394+16 = 68
From http://avisynth.org/mediawiki/ColorBars_theory
which is actually from a discussion I started years ago, resulting in proper colorbars colors in avisynth 2.56:
http://forum.doom9.org/showthread.php?t=107682&page=2

Yes, not all YUV (or YIQ) values have corresponding RGB values. This is not news.


2. I don't believe 8 bits is enough to cover even the permissible YUV colors, also in 8 bits.

jmac698
9th November 2011, 20:42
@Fluff
You have to realize that Avisynth script is, by far, the easier way for me to create specific videos as I require. I am not a Windows programmer and do not know enough Video For Windows to create a new video, and anyhow I still think it would be tremondously complicated to try to learn that now. Otherwise, I need to research the AVI file format, come up with a header, research the raw format, and do the byte packing or complicated calculations to draw directly into Y1 U Y2 V bytes in 32 bit words, which are then padded. Not simple at all.

The only other way I know of is the math expressions feature in ImageMagick.

Please, tell me even one method which is simpler than what I'm doing, which won't take an hour of reading API's, or file formats.

jmac698
9th November 2011, 20:58
@Fluff
http://msdn.microsoft.com/en-us/library/ms779636.aspx

So I will actually try what you suggest.
I should write :
'RIFF' (filesize) ('AVI')
ckID ckSize
...
I also have to create an AVI index... there really seems to be a lot of research and calculations involved. Do you see my point?

TheFluff
9th November 2011, 21:05
@Fluff
You have to realize that Avisynth script is, by far, the easier way for me to create specific videos as I require. I am not a Windows programmer and do not know enough Video For Windows to create a new video, and anyhow I still think it would be tremondously complicated to try to learn that now. Otherwise, I need to research the AVI file format, come up with a header, research the raw format, and do the byte packing or complicated calculations to draw directly into Y1 U Y2 V bytes in 32 bit words, which are then padded. Not simple at all.

The only other way I know of is the math expressions feature in ImageMagick.

Please, tell me even one method which is simpler than what I'm doing, which won't take an hour of reading API's, or file formats.

When I said an uncompressed video I meant raw containerless RGB or YUV data, not an AVI.

Anyway, while there is certainly no shortage of sane(r) ways to do what you want, the obvious answer here is to write an Avisynth plugin. Blending a single-color rectangle on top of an existing image is literally about fifteen lines of rendering code in addition to the maybe 50 lines of Avisynth boilerplate stuff that you can just copypaste from somewhere. There is not exactly a shortage of good sample code, since most plugins are open source.

The entire GetFrame() function in the original assrender (my first and so far only Avisynth plugin), which essentially blends RGB32 bitmaps with a given opacity and a given pixel offset on top of an RGB32 image, is about 60 lines of code total, which includes about 10 lines of comments. The inner rendering loop is 17 lines. Go take a look at lachs0r's C implementation (which takes care of some really complicated shit like rendering RGB32 on top of YV12 with chroma interpolation and all, without converting the source image like overlay() does) and you're basically almost done already. It's a sunday afternoon project, really.

jmac698
9th November 2011, 21:26
@Fluff
Ok, I tried another possibility. You can draw with imagemagick. Great. But I still can't specify YUV colors:
http://www.imagemagick.org/script/command-line-options.php?#fill

You are right, there's lots of easy ways to draw RGB shapes, but nearly impossible to draw YUV colors.

So I looked into converted raw files.

convert -size 720x480 movie.yuv

Should work. I still haven't found the specification for YUV raw file format, however.


Also, you should obviously write an Avisynth plugin. ... There is not exactly a shortage of good sample code, since most plugins are open source.

Again, I've been trying to do just that. The result is my article, http://avisynthnew.wikinet.org/wiki/Avisynth_Plugin_Development_in_C#Passing_Parameters_to_your_Plugin

I've had to give up, however, as I was not able to modify the sample code to work. The best I'd be able to do is not change a thing except for the pixel calculation part, thus resulting in a plugin with useless parameters. And I really doubt there's a plugin with just enough int's in it's parameters to cover my needs.

It's not as simple as copy & paste, though part of it is. Parsing and checking the parameters takes some though of course.

Perhaps you could write a sample pure C plugin that does nothing but average two clips, then I could base my article off that. Please release as GPL.

Gavino
9th November 2011, 21:32
I've had to give up, however, as I was not able to modify the sample code to work.
What was the problem?
You had this more or less written almost a year ago and I pointed out the errors then.
What more do you need to know?

jmac698
9th November 2011, 21:33
Thanks for asking, I'll start a new thread.

TheFluff
9th November 2011, 21:40
Should work. I still haven't found the specification for YUV raw file format, however.
There is no such "file format", you just dump raw YUV data to disk with any plane order you like. Of course you have to know that order as well as the resolution and subsampling mode in order to make any sense of it afterwards.

Perhaps you could write a sample pure C plugin that does nothing but average two clips, then I could base my article off that. Please release as GPL.
I have no interest in your project (beyond questioning its sanity). Furthermore, I don't have any experience at all with the avisynth_c interface, so you probably know more about it than me.

Dogway
9th November 2011, 21:49
@jmac698, I don't know why you quote me so late in time, as I'm already using the nice lutspa function you gave me.

function BoxMask(clip clip, int "x1", int "x2", int "y1", int "y2", bool "show"){

w=clip.width()
h=clip.height()

x1=string(default(x1,0))
x2=string(default(x2,w))
y1=string(default(y1,0))
y2=string(default(y2,h))
show=default(show, false)

mt_lutspa(clip, mode="absolute", yexpr="x "+x1+" >= x "+x2+" <= & y "+y1+" >= y "+y2+" <= & & 255 0 ?", chroma= "128").trim(0,-1).FreezeFrame(0, FrameCount(clip)-1, 0)
show?mt_lutxy(clip,last,"x 0.75 * y 0.25 * + 2 /"):last}

For fancy shapes masks I normally paint it myself and load it as imagesource.

jmac698
9th November 2011, 21:50
@Fluff
There are pre-determined YUV formats which ImageMagick will accept. I think the convert option only accepts 1:1:1 sampling. I'm still uncertain if it expects planar or interleaved, and/or if I can specify which to use. I certainly do not see any command line options with the words "planar" in them.

Anyhow, thanks for your suggestions. I would desperately love to be able to write plugins, and have started another thread on that topic, perhaps I will figure it out someday. Otherwise, ImageMagick remains hopeful, however I think you will have to admit, that after following all your suggestions, what I have started re: DrawRect is not as insane as it seems. You obviously have a different skillset than me at this point, so please be aware of the differences.

jmac698
9th November 2011, 21:53
@Dogway,
I didn't realize you had finished your function. I would like to create a draw shapes plugin, finally there will be an efficient way to do this. I need to draw some 'fancy shapes' but they aren't things that could be handdrawn, for example Avia type test charts like resolution (which involves various sinewaves).