View Full Version : Is it possible to average a frame like photoshop does?
Gargalash
18th January 2012, 20:21
Hello,
I would like to know if it's possible to average a frame like Photoshop does with the Blur - Average filter. The result is a solid color image.
I have tried to use Averageblur(), Spatialsoften() and Blur() without success.
Thanks for helping!
Gavino
18th January 2012, 20:42
mt_lutf(last, expr="x", U=3, V=3)
Gargalash
19th January 2012, 03:32
Thanks a lot Gavino! It took me a while to understand that filter, but I have discovered many things by doing so!
redfordxx
19th January 2012, 15:21
Would something like following also work?
ScriptClip("mt_lut(y=-AverageLuma, u=-AverageChromaU, v=-AverageChromaV")
But I am not sure about the exact syntax...
Might be faster? I don't know... Or using BlankClip instead of mt_lut even faster?
Gavino
19th January 2012, 17:05
ScriptClip("mt_lut(y=-AverageLuma, u=-AverageChromaU, v=-AverageChromaV")
It would in any case have to be y=-Round(AverageLuma), etc, since the parameters are ints and AverageLuma, etc, return floats.
But it would be a lot slower, since a separate instance of mt_lut would be created for every frame.
redfordxx
20th January 2012, 23:27
Separate instance...this is how Scriptclip works for every filter?
Gavino
20th January 2012, 23:45
Separate instance...this is how Scriptclip works for every filter?
Yes, see here (http://avisynth.org/mediawiki/The_script_execution_model/Evaluation_of_runtime_scripts) and here (http://avisynth.org/mediawiki/The_script_execution_model/Performance_considerations#What_not_to_include_in_runtime_scripts).
chueps
26th March 2025, 13:42
mt_lutf(last, expr="x", U=3, V=3)
Thank you Gavino !
I needed this trick to radically blur certain areas of videos.
If my function can be useful to someone…
function AveragePos(clip src, int "xA", int "yA", int "xB", int "yB") {
w =src.Width
h =src.Height
xA=default(xA,0)
yA=default(yA,0)
xB=default(xB,int(round(w/2)))
yB=default(yB,int(round(h/2)))
Assert(xA >= 0 || xB >= 0 || yA >= 0 || yB >= 0,"Wrong values!")
Assert(xB != 0 || yB != 0,"Wrong values!")
Assert(xA <= w || xB <= w,"Wrong values!")
Assert(yA <= h || yB <= h,"Wrong values!")
Assert(xA != int(round(float(xA)/2)) || xB != int(round(float(xB)/2)) || yA != int(round(float(yA)/2)) || yB != int(round(float(yB)/2)),"Wrong values!")
Assert(xA < xB,"Wrong values!")
Assert(yA < yB,"Wrong values!")
(xA==0 && yA==0 && xB==w && yB==h)
\?\
mt_lutf(src,src,expr="x",U=3,V=3)
\:\
src
t=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h) || (yA==0))
\?\
src
\:\
src.Crop(0,0,-0,-(h-yA))
ml=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h))
\?\
src
\:\
((xA!=w-xB && xA+xB==w) || (xA==0 && xB==w))
\?\
src
\:\
(xA==0 || (xA==0 && xB!=w && yA==0 && yB==h))
\?\
mt_lutf(src.Crop(0,yA,-(w-xB),-(h-yB)),src.Crop(0,yA,-(w-xB),-(h-yB)),expr="x",U=3,V=3)
\:\
(xB==w || (xA!=0 && xB==w && yA==0 && yB==h))
\?\
src
\:\
(xB!=w || (xA!=0 && xB!=w && yA==0 && yB==h))
\?\
src.Crop(0,yA,-(w-xA),-(h-yB))
\:\
(xA!=0 && xB!=w && yA!=0 && yB!=h)
\?\
src.Crop(0,yA,-(w-xA),-(h-yB))
\:\
src
mc=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h))
\?\
src
\:\
((xA!=w-xB && xA+xB==w) || (xA==0 && xB==w))
\?\
mt_lutf(src.Crop(0,yA,-0,-(h-yB)),src.Crop(0,yA,-0,-(h-yB)),expr="x",U=3,V=3)
\:\
(xA==0 || (xA==0 && xB!=w && yA==0 && yB==h))
\?\
src.Crop((w-xB),yA,-0,-(h-yB))
\:\
(xB==w || (xA!=0 && xB==w && yA==0 && yB==h))
\?\
src.Crop(0,yA,-(w-xA),-(h-yB))
\:\
(xB!=w || (xA!=0 && xB!=w && yA==0 && yB==h))
\?\
mt_lutf(src.Crop(xA,yA,-(w-xB),-(h-yB)),src.Crop(xA,yA,-(w-xB),-(h-yB)),expr="x",U=3,V=3)
\:\
(xA!=0 && xB!=w && yA!=0 && yB!=h)
\?\
mt_lutf(src.Crop(xA,yA,-(w-xB),-(h-yB)),src.Crop(xA,yA,-(w-xB),-(h-yB)),expr="x",U=3,V=3)
\:\
src
mr=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h))
\?\
src
\:\
((xA!=w-xB && xA+xB==w) || (xA==0 && xB==w))
\?\
src
\:\
(xA==0 || (xA==0 && xB!=w && yA==0 && yB==h))
\?\
src
\:\
(xB==w || (xA!=0 && xB==w && yA==0 && yB==h))
\?\
mt_lutf(src.Crop(xA,yA,-0,-(h-yB)),src.Crop(xA,yA,-0,-(h-yB)),expr="x",U=3,V=3)
\:\
(xB!=w || (xA!=0 && xB!=w && yA==0 && yB==h))
\?\
src.Crop(xB,yA,-0,-(h-yB))
\:\
(xA!=0 && xB!=w && yA!=0 && yB!=h)
\?\
src.Crop(xB,yA,-0,-(h-yB))
\:\
src
m=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h))
\?\
src
\:\
((xA!=w-xB && xA+xB==w) || (xA==0 && xB==w))
\?\
mc
\:\
(xA==0 || (xA==0 && xB!=w && yA==0 && yB==h))
\?\
StackHorizontal(ml,mc)
\:\
(xB==w || (xA!=0 && xB==w && yA==0 && yB==h))
\?\
StackHorizontal(mc,mr)
\:\
(xB!=w || (xA!=0 && xB!=w && yA==0 && yB==h))
\?\
StackHorizontal(ml,mc,mr)
\:\
src
b=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h) || (yB==h))
\?\
src
\:\
src.Crop(0,yB,-0,-0)
v=((xA==0 && yA==0 && xB==w && yB==h) || (xA!=w-xB && yA!=h-yB && xA+xB==w && yA+yB==h))
\?\
src
\:\
(yA==0 && yB==h)
\?\
m
\:\
(yA==0 && yB<h)
\?\
StackVertical(m,b)
\:\
(yA>0 && yB==h)
\?\
StackVertical(t,m)
\:\
StackVertical(t,m,b)
return v
}
But maybe there is a simpler solution? :confused:
DTL
26th March 2025, 17:57
With latest version of AVS 3.7.4 you can apply single very large radius Gauss blur to the frame and use masking and overlay to combine output frame where you need original and blurred areas.
Large radius gauss blur is about GaussResize(width, height, p=0.01, s=0, force=3) . Hope it is enough.
chueps
26th March 2025, 18:51
With latest version of AVS 3.7.4 you can apply single very large radius Gauss blur to the frame and use masking and overlay to combine output frame where you need original and blurred areas.
Large radius gauss blur is about GaussResize(width, height, p=0.01, s=0, force=3) . Hope it is enough.
Thank you for this suggestion. :) (I haven't tried AviSynth+ yet, but I'll probably give it a try. :o)
Are you sure the last GaussResize() really allows absolute blurring like Photoshop does, resulting in an average color? Or is it a close solution?
DTL
26th March 2025, 19:04
It not return mean value of all samples like Average* . But in the post was "radically blur " - not simple mean value. Average* is faster in computing but filling area with single value of RGB or YUV will give completely flat field.
Also currently Mask/Overlay mixer filter do not yet provide service for filtering of hard transients if you will insert simply rectangles so you need to think how to create more smoothed transients from original content of the frame to highly blurred or even averaged areas. It may be more scripting with something like blurred alpha mask or even full implementation of current AddBorders filter with extracting 4 or 8 areas around new inserted rectangle to filtering and inserting back (using MultiOverlay new filter to make things somehow faster). You can make a script-function with only x1,y1,x2,y2 (RECT() structure) coordinates of the area to blur and it will serve all required operations to return blurred and somehow more correctly integrated back area. If you leave hard transients from original to blurred area it will cause ringing artifacts with some resamples at some displays or some next processing stages with sinc-based interpolation.
StainlessS
26th March 2025, 22:34
EDIT: @chueps, perhaps of use. [I dont know what Photoshop does]
ConcealRange.avs [Link to original post in BLUE below]
[EDIT: or here]:- https://forum.doom9.org/showthread.php?p=1911260#post1911260
/*
https://forum.doom9.org/showthread.php?p=1911260#post1911260
Req:- Masktools2, FastBlur, Avs+
YUV444 or RGB Only, 8 -> 32 bit.
*/
Function EllipMsk(clip c,int W, Int H,Bool "Soft") {
# Req mt_tools_2, Returns frame WxH same FPS as c and without audio
Soft=Default(Soft,False)
# INFIX_H="(((x-.5)^2 +(y-.5)^2) < .25 ? 255 : 0"
# Elliptical Disk (Hard Edge) :: mode = "relative", Radius=0.5, Rad^2=0.25 :: SOFT_Inner rad:(Rad*0.9)^2~=0.2
# INFIX_S="((x-.5)^2+(y-.5)^2)<.2?255 : (((x-.5)^2+(y-.5)^2)<.25 ? ((.25-((x-.5)^2+(y-.5)^2))*5100):0)"
# Elliptical Disk (Soft Edge) :: (.25-0.2)*5100=255 : (.25-.25)*5100=0.0] :: mode="relative", 0.25~=1.0 : 0.2~=0.9
rpn = (!Soft)
\ ? "x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.25 < 255 0 ? scalef" [* RPN: EllipMsk [Hard Edge] *]
\ : "x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.2 < 255 x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.25 < 0.25 x 0.5 - 2 ^ y 0.5 - 2 ^ + - 5100 * 0 ? ? scalef" [* RPN: EllipMsk [Soft Edge] *]
c.Blankclip(width=W,height=H,Length=1).Killaudio
return Last.mt_lutspa(mode = "relative", expr = rpn, chroma = "-128" )
}
Function RectMsk(clip c,int W, Int H,Bool "Soft") {
# Req mt_tools_2, Returns frame WxH same FPS as c and without audio
Soft=Default(Soft,False)
# INFIX_H = "255"
# INFIX_S = "max(abs(x-.5),abs(y-.5)) < .45 ? 255 : ((.5 - max(abs(x-.5),abs(y-.5)) ) * 5100)"
rpn=(!Soft)
\ ? "255 scalef"
\ : "x 0.5 - abs y 0.5 - abs max 0.45 < 255 0.5 x 0.5 - abs y 0.5 - abs max - 5100 * ? scalef"
c.Blankclip(width=W,height=H,Length=1).Killaudio
return Last.mt_lutspa(mode = "relative", expr = rpn, chroma = "-128" )
}
Function Conceal(clip c,clip bc,clip msk, Float x, Float y,Float Opacity) {
mw = Msk.Width mh=Msk.Height
ow=c.width-2*mw oh=c.Height-2*mh
xc = Round(min(max(0.0,x),ow))
yc = Round(min(max(0.0,y),oh))
x = xc + mw/2
y = yc + mh/2
bc=bc.crop(x,y,mw,mh)
c.Overlay(bc,mask=msk,x=x,y=y,opacity=Opacity)
}
Function ConcealRange(clip c,clip bc,clip msk,Int "S", Int "E",float "sx",Float "sy",Float "ex",float "ey", Float "sop",Float "eop") {
/*
Blur/Conceal clip c with blurred clip clip bc using Mask msk, frames S to E, sx,sy start coords, ex,ey end coords.
Start and End Args S & E, are similar but not exactly like trim.
ConcealRange(c,bc,msk, 0,0) # Entire clip
ConcealRange(c,bc,msk, 100,0) # Frame 100 to End of Clip
ConcealRange(c,bc,msk, 0,-1) # Frame 0 Only
ConcealRange(c,bc,msk, 1,1) # Frame 1 Only
ConcealRange(c,bc,msk, 1,-1) # Frame 1 Only
ConcealRange(c,bc,msk, 1) # Frame 1 Only [Not same as Trim()], E defaults to -1 ie single frame.
ConcealRange(c,bc,msk, 1,-3) # Frames 1 to 3 (ie 3 frames)
ConcealRange(c,bc,msk, 100,200) # 101 Frames, 100 to 200
ConcealRange(c,bc,msk, 100,-50) # Frames 100 to 149 ie 50 frames
X coords sx, and ex, allowed range 0 -> c.width.
Y coords sy, and ey, allowed range 0 -> c.height.
sop, eop, Start and End Opacity, Default 1.0, range 0.0 -> 1.0
*/
FMX=c.FrameCount-1
S = Min(Max(Default(S,0),0),FMX) E = Default(E,-1)
sx=Default(sx,0.0) sy=Default(sy,0.0)
ex=Default(ex,0.0) ey=Default(ey,0.0)
sop=min(max(Default(sop,1.0),0.0),1.0) eop=min(max(Default(eop,1.0),0.0),1.0)
E = (E==0) ? FMX : E
E = Min(((E < 0) ? S-E-1 : E),FMX) # S <= E <= FMX : E is +ve END Frame number (may be 0)
Empty = c.BlankClip(Length=0)
CS = (S==0) ? Empty : c.Trim(0,-S)
C2E = (E==0?FMX:E)
mw=Msk.Width mh=Msk.Height
cpad = c.Addborders(mw,mh,mw,mh)
bcpad = bc.Addborders(mw,mh,mw,mh)
CM = cpad.Animate(S,C2E,"Conceal", bcpad,Msk,sx,sy,sop, bcpad,Msk, ex,ey,eop)
CM = CM.Trim(S,C2E).crop(mw,mh,-mw,-mh)
CE = (E==FMX) ? Empty : c.Trim(E+1,0)
CS ++ CM ++ CE
}
ConcealRange_Demo.avs
##############################################################
AviSource("D:\Parade.avi").Trim(100,0) # Source Clip
# Either YUV444 or RGB
ConvertToYV24.ConvertBits(10)
#ConvertToPlanarRGB.ConvertBits(10)
#ConvertToRGB24
#ConvertToRGB48
#ConvertToRGB64
####### CONFIG ###############################################
DISK = True # True = Disk/Ellipse, Else Rectangle
PIXELATE = False # True = PIXELATE, False=Blur
PIXSZ = 8 # PIXELATE Size, (maybe 8 or 4)
# FastBlur args # Fastblur args if PIXELATE=False
FBlurRad = 5
FBlurIter = 3
# Mask args [size of concealed area]
MWidth = 128
MHeight = MWidth
MSOFT = True # Soft Edge mask
# Opacity of Overlay'd concealment. [You are unlikely to want anything less than about 0.9, usually 1.0]
sop = 1.0 # Start frame Opacity
eop = sop # End frame opacity same as start opacity.
# OTHER STUFF
BLANKFG = false # Show ForeGround(blurred/concealed Area) in Blue [see path traveled better].
BLANKBG = False # Show BackGround in Pink
####### END CONFIG ########################
Assert(IsRGB || (Height==ExtractU.Height&&Width==ExtractU.Width),"YV444 or RGB ONLY")
W = Width
H = Height
BPC = BitsPerComponent
#
BClip = (PIXELATE) ? BilinearResize(W/PIXSZ,H/PIXSZ).PointResize(W,H) : Last.Fastblur(FBlurRad,iterations=FBlurIter) # Concealing clip
MSK = Last.BlankClip(pixel_type="Y8").ConvertBits(BPC) # Mask
MSK = (DISK) ? MSK.EllipMsk(MWidth,MHeight,Soft=MSOFT) : MSK.RectMsk(MWidth,MHeight,Soft=MSOFT)
#
Last = (BLANKBG) ? Last.BlankClip(Color=$C04060) : Last
BCLip= (BLANKFG) ? BCLip.BlankClip(Color=$0000FF) : BClip
#################################################################################
# Demo coords, Start,End Frm, Start X,Y End X,Y Start,End Opacity(default 1.0 if not supplied)
ConcealRange(Last,BClip,Msk, 0, 499, 0, 0, W, H, sop, eop)
ConcealRange(Last,BClip,Msk, 500, 999, W, H, W, 0, sop, eop)
ConcealRange(Last,BClip,Msk, 1000, 1499, W, 0, 0, H, sop, eop)
ConcealRange(Last,BClip,Msk, 1500, 1999, 0, H, 0, 0, sop, eop)
ConcealRange(Last,BClip,Msk, 2000, 2499, 0, 0, W/2,H/2, sop, eop)
#################################################################################
ConvertToRGB32 # For viewing HBD
Original
https://i.postimg.cc/ydDcfC6B/Conceal-Range-00.jpg (https://postimages.org/)
Pixelated
https://i.postimg.cc/jqJyPTk4/Conceal-Range-01.jpg (https://postimages.org/)
Blurred
https://i.postimg.cc/3J5mZqs5/Conceal-Range-02.jpg (https://postimages.org/)
Pixelated/BlankBG
https://i.postimg.cc/28Qv9wYq/Conceal-Range-03.jpg (https://postimages.org/)
Blurred/BlankBG
https://i.postimg.cc/htMmctfc/Conceal-Range-04.jpg (https://postimages.org/)
StainlessS
27th March 2025, 01:01
Also, FredAverage might be useful, ColorSpace, YV12, YV16, YV24, YV411, Y8, YUY2, RGB24, RGB32, only.
https://forum.doom9.org/showthread.php?t=174520
FredAverage(), by StainlessS @ Doom9
Requires VS2008 CPP runtimes.
Prompted by this post here:- https://forum.doom9.org/showthread.php?p=1803528#post1803528
dll's for avs v2.58 + avs v2.60/+ x86 & x64.
A simple average filter for Avisynth standard colorspaces, only.
Returns a clip where each return frame is a single color average of input frame, same size and colorspace as input.
Does an invert on result if Bool Invert==true.
FredAverage, (not to be confused with RedAverage):- https://forum.doom9.org/showthread.php?t=174520
Requires VS2008 CPP runtimes.
dll's for avs v2.58 & avs v2.60/+ x86 & x64.
A simple average filter for Avisynth v2.60 standard colorspaces, only. (v2.58 colorspaces for v2.58 dll)
Returns a clip where each return frame is a single color average of input frame, same size and colorspace as input.
Does an invert on result if Bool Invert==true.
ColorSpace, YV12, YV16, YV24, YV411, Y8, YUY2, RGB24, RGB32, only.
Return clip Y, U and V, or R, G and B, will be channel averages, unless Invert==True, where channels averages will be inverted.
FredAverage(clip c, Bool "Invert"=false,Bool "TV_YUV"=False)
Invert, Default false == sampled average. Otherwise Inverted average.
TV_YUV, Default false, If True(And YUV), then photo negative invert around TV levels mid Y(125.5), rather than 127.5.
Returns clip same colorspace and size as input.
StainlessS.
FredAverageTest.avs
# WHEN YUV, RHS IMAGE, outer = Average : Left Inner = PC Levels Invert : Inner Right = TV Levels Invert
AviSource("D:\Parade.avi")
Crop(0,0,Width/8*8,Height/8*8)
ConvertToYV12
ORG=LAst
AVE=ORG.FredAverage
I_PC=ORG.FredAverage(Invert=true,TV_YUV=False).BilinearResize(ORG.Width/4,ORG.Height/2)
I_TV=ORG.FredAverage(Invert=true,TV_YUV=true ).BilinearResize(ORG.Width/4,ORG.Height/2)
I=Stackhorizontal(I_PC,I_TV)
AVE=AVE.Overlay(I,x=ORG.Width/4,y=ORG.height/4)
StackHorizontal(ORG,AVE)
https://i.postimg.cc/QFHyKz6k/Fredaverage-Test.jpg (https://postimg.cc/QFHyKz6k)
EDIT: P.S.,
average a frame like photoshop does
I have no idea what Photoshop does.
EDIT: Looks like I added FredAverge v0.04Beta, new Beta version:- https://forum.doom9.org/showthread.php?p=1939974#post1939974
where Mask arg added, [Direct LINK https://www.mediafire.com/file/5n8wt219aro4gxb/FredAverage_25%252626_x86_x64_dll_v0.04Beta_20210405.zip/file ]
Mask, Default Undefined.
If Mask Supplied,
Source clip c cannot be subsampled, ie YV24 or Y8 or RGB24 or RGB32, ONLY.
Mask must be same size as source clip c, and must be 8 bit planar with Y [only Y used].
Where Mask Y value is 128 or more, then corresponding pixel from clip c is included in the average,
where 127 or less, corresponding pixel from clip c is ignored.
Mask can be single frame affecting all frames of source clip c, or same length as clip c
with potentially different mask for each source frame. [Final mask frame will be used if shorter than c clip].
FredAverage_MaskTest.avs in AVS folder
FN = "D:\Parade.avi" # Some Clip [bigger than about 320x320, not too long]
BLOCK_AVE=False # True Show Average of BLOCK, False Show average of NON BLOCK [UnComment RETURN ORG to show RED block overlay]
CONVRGB24 = True # True convert Source To RGB24 : Else convert To YV24
###############
X=32
Y=32
W=256
H=256
###
AviSource(FN)
(CONVRGB24) ? ConvertToRGB24 : ConvertToYv24
RED=Last.BlankClip(Length=1,color=$FF0000)
WHITE=Last.BlankClip(Length=1,color=$FFFFFF)
BLACK=Last.BlankClip(Length=1,color=$000000)
WHITE.Loop(100)+Last+BLACK.Loop(100) # White 100 frames + Clip + Black 100 Frames
Last.OVERLAY(RED.crop(0,0,W,H),x=X,y=Y)
ORG=Last
#RETURN ORG # SHOW Clip with BLOCK if UNCOMMENT
##############
M=Last.BlankClip(Length=1,width=W,height=H,color=$FFFFFF)
M=M.AddBorders(X,Y,Width-X-W,Height-Y-H,$000000).ConvertToYV12
#return M
M = (BLOCK_AVE) ? M : M.Invert
FredAverage(Last,Mask=M)
Subtitle(BLOCK_AVE?"BLOCK - AVERAGE":"NOT BLOCK - AVERAGE")
StackHORIZONTAL(ORG,Last)
return last
RHS, is average of the NOT red block area on LHS.
https://i.postimg.cc/Tw5DHSKN/Fred-Average-Mask-Test-00.jpg (https://postimages.org/)
chueps
29th March 2025, 02:56
I dont know what Photoshop does
It does something like that:
https://i.postimg.cc/P5G9Xq9R/ezgif-4-2b82446fa5.webp (https://i.postimg.cc/P5G9Xq9R/ezgif-4-2b82446fa5.webp)
All pixels in the selected surface merge into a single average color (if uncompressed, of course).
Anyway, I really like your idea of masking according to a disk. :cool:
VoodooFX
29th March 2025, 04:51
It does something like that:
Why would you want this ugly one color square?
Maybe you want something like that -> https://forum.doom9.org/showthread.php?t=184881
chueps
29th March 2025, 06:18
Why would you want this ugly one color square?
This was just an animated example in WebP for this forum to answer StainlessS. The animation, although very ugly, allows us to see what the Photoshop filter actually consists of. I think a simple PNG image would not have been explicit.
But in H264, HEVC or lossless, the result is clean.
VoodooFX
29th March 2025, 13:59
This was just an animated example in WebP for this forum to answer StainlessS. The animation, although very ugly, allows us to see what the Photoshop filter actually consists of. I think a simple PNG image would not have been explicit.
But in H264, HEVC or lossless, the result is clean.
Picture, video, whatever, it's ugly everywhere.
Uglier than StainlessS' plugin UglyArm. :D
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.