View Full Version : Remove Logos Again
Spuds
17th February 2008, 04:03
After I made changes to the delogo script I promised myself I would not work on any other delogo functions BUT the avsinpaint (http://forum.doom9.org/showthread.php?p=1101283#post1101283)function just called me back. :devil:
In the Avsinpaint thread Reuf Toc made a very nice script (wish I could code that nice !) but I wanted to add my own twists that I learned from modding the delogo script and experimenting with avsinpaint. So I did some combining and adding and the result is rm_logo.
Basic usage is as follows:
1) Get a clip with a logo that you want removed.
2) Save a frame from that clip and edit it in your favorite graphics program, paint the logo pure white and everything else black. Save it as a logo.bmp (any name you want)
3) Make a avs script such as:
LOADCPLUGIN("AVSINPAINT.DLL")
Import("rm_logo.avs")
avisource("videowithlogo.avi")
rm_logo(last,logomask=logo.bmp",loc="br",par=4.0/3.0,mode="both",percent=20,pp=1)
Basic things you must supply.
logomask -- the name of the black and white bmp you created
loc -- TR, TL, BR, BL this is the location of the logo on the video, top right, top left, bottom right, bottom left. There is also a tweak you can supply here called cutsize set it to small medium or large. It refines the size of the corner cut somewhat. If you have to much video and not enough logo in a cut you can get less optimal results, you want the logo to fill the cut (within reason).
par -- pixel aspect, no harm if you don't specify it, it just helps grow masks correctly in all directions.
mode -- how to remove the logo, deblend (for purely transparent logos. inpaint for purely solid logos and both for logos with both.
percent -- The percent of total frames to use in computing the masks, the more the better and the slower.
pp -- Post Process, 1,2 or 3. Apply some additional post processing of the removed area to further hide the logo and artifacts.
The first time the script runs expect it to take a little time, it has to compute the color and alpha masks so it will appear to lock up on you for a min or two while its doing this. Once done it will save a analysis bmp file so it will be responsive from then on.
There are other things to tweak, look in the script for more information.
Script in the next post.
Spuds
17th February 2008, 04:04
# rm_logo() Version 0.5 -- 23.04.08
#
# Script to help in the removal of channel logos or other distracting objects
#
# Required filters:
# AVSInpaint: Ver 2008-01-06
# Discussion & Code : http://forum.doom9.org/showthread.php?t=133682
# ExInpaint: Ver 0.1+
# Code http://avisynth.org.ru/exinpaint/exinpaint.html
# mt_masktools: Ver 2.0.32+
# Code http://manao4.free.fr/masktools-v2.0a32.zip
# removegrain: Ver 1.0 (8/2005)
# Code http://www.removegrain.de.tf
# fft3dfilter: Ver 2.1.1 or later
# Code http://avisynth.org.ru/fft3dfilter/fft3dfilter.html
# ttempsmoothf Ver 0.9.4 or later
# Code http://bengal.missouri.edu/~kes25c/
# medianblur Ver 0.8.4
# Code http://www.avisynth.org/tsp/medianblur084.zip
#
function rm_logo( clip clp, string "logomask", string "loc",float "par", string "mode",int "percent",int "deblendfalloff",\
int "AlphaToRepair", float "RepairRadius", float "InpaintRadius", float "InpaintSharpness",\
float "InpaintPreBlur", float "InpaintPostBlur", string "cutsize", bool "lmask", int "pp", bool "debug", \
int "cutwidth", int "cutheight")
{
logomask = default( logomask, "" ) # file location of the logo, the must be masked in pure white
loc = default( loc, "" ) # where is the logo, TR, TL, BR, BL for top right, top left, bottom right, bottom left
cutsize = default( cutsize, "small" ) # how big a cut to make, small, medium, large
cutwidth = default( cutwidth, 0 ) # how wide a cut to make in pixels, -1 for full width of frame
cutheight = default( cutheight, 0 ) # how tall a cut to make in pixels, -1 for full height of frame
par = default( par, 1.0 ) # pixel aspect ratio
mode = default( mode, "both" ) # deblend, inpaint or both
percent = default( percent, 25 ) # how much of the clip to analyse in creating color&alpha masks, more is better but slower
deblendfalloff = default( deblendfalloff, 5 ) # graidient fallout from logo mask
AlphaToRepair = default( AlphaToRepair, 130 ) # what is the luma value of the solid part of the logo
RepairRadius = default( RepairRadius, 1.0 ) # used to expand the mask for none alpha ie solid areas
InpaintRadius = default( InpaintRadius, 6.0 ) # radius around a damaged pixel from where values are taken when the pixel is inpainted. Bigger values prevent
# inpainting in the wrong direction, but also create more blur
InpaintSharpness = default( InpaintSharpness, 25.0 ) # Higher values can prevent blurring caused by high Radius values.
InpaintPreBlur = default( InpaintPreBlur, 1.5 ) # Standard deviation of the blur which is applied to the image before the structure tensor is computed. Higher values
# help connecting isophotes which have been cut by the inpainting region, but also increase CPU usage. PreBlur=0.0
# disables pre-blurring.
InpaintPostBlur = default( InpaintPostBlur, 5.0 ) # standard deviation of the blur which is applied to the structure tensors before they are used to determine the
# inpainting direction. Higher values help gather more directional information when there are only few valid pixels
# available, but increases CPU usage
lmask = default( lmask, true ) # apply post process through a repair mask
PP = default( PP, 1 ) # Post process function 0,1,2 to help reduce damage left behind by logo removal
debug = default( debug, false ) # show mask to help in tunning the output
# set up some values that we need to run
clp_width = width( clp )
clp_height = height( clp )
RGB = isRGB( clp )
RGB32 = isRGB32( clp )
RGB24 = isRGB24( clp )
par = ( par!= 1.0 ) ? float( clp_height ) / float( clp_width ) * par : 1.0
percent = ( percent < 0) ? 25 : (percent > 100) ? 100 : percent
# Get the always fun input error checking done
assert ( logomask != "" , "You have to define a logomask")
assert ( loc != "" , "You must provide a value for Loc UL,UR,LL,LR")
assert ( loc == "TR" || loc == "TL" || loc == "BR" || loc == "BL" , "Loc must be one of TR, TL, BR, BL")
assert ( mode == "both" || mode == "inpaint" || mode == "deblend", "Specified mode doesn't exist.")
# Get our crop locations based on the passed location
loc = UCase( loc )
cutsize = UCase( cutsize )
multi = ( cutsize == "SMALL" ) ? 2.25 : ( cutsize == "MEDIUM" ) ? 2.15 : 2
chunk = ( clp_height > 720 ) ? 2.9 : 3
cutwidth = ( cutwidth == 0 || cutwidth == -1 ) ? cutwidth : m4(cutwidth)
cutheight = ( cutheight == 0 || cutheight == -1 ) ? cutheight : m4(cutheight)
a = ( Rightstr( loc, 1 ) == "L" ) ? 0 : ( cutwidth == 0 ) ? m4( ( clp_width / chunk ) * multi ) : ( cutwidth == -1 ) ? 0 : (clp_width - cutwidth)
b = ( Leftstr( loc, 1 ) == "T" ) ? 0 : ( cutheight == 0 ) ? m4( ( clp_height / chunk ) * multi ) : (cutheight == -1 ) ? 0 : (clp_height - cutheight)
c = ( Rightstr( loc, 1 ) == "R" ) ? 0 : (cutwidth == 0) ? -m4( ( clp_width / chunk ) * multi ) : (cutwidth == -1 ) ? 0 : -(clp_width - cutwidth)
d = ( Leftstr( loc, 1 ) == "B" ) ? 0 : (cutheight == 0 ) ? -m4( ( clp_height / chunk ) * multi ) : (cutheight == -1) ? 0 : -(clp_height - cutheight)
cropped = clp.crop(a,b,c,d)
# Anaylse the entire clip or a percentage for speed.
snipSize = round( framecount( cropped ) / (framecount( cropped ) * (percent / 100.0) ))
analyse = ( percent != 100 ) ? cropped.SelectRangeEvery( snipSize, 1 ) : cropped
# Read in our logo mask, prepare it and crop out the corner of interest
logo_mask = imagesource(logomask,start=0,end=1)
logo_mask = logo_mask.crop(a,b,c,d)
logo_mask = logo_mask.ConvertToYV12(Matrix="PC.601")
logo_mask = logo_mask.DistanceFunction(255/deblendfalloff,PixelAspect=par).Greyscale
# Clean the analyse clip to improve results
analyse = (IsYV12(analyse)) ? analyse : analyse.ConvertToYV12
analyse = analyse.TTempSmoothF(maxr=2,lthresh=256,cthresh=256,scthresh=255).converttoRGB24()
input = ( RGB24 == true ) ? cropped : cropped.converttoRGB24()
# seperate out the directory and logo names so we can save a unique ebmp file
sl = logomask.revstr().findstr("\") - 1
Assert((sl >= 0),"specify a fully qualified directory and logomask name to use")
logo_name = (sl < 0 ) ? "" : rightstr(logomask,sl) # name and extension
s2 = logo_name.findstr(".") - 1 # find the length of the extension
logo_name = leftstr(logo_name,s2) # just the name !
Analyse_Name = logo_name + loc + string(percent) + "AnalyzeResult%06d.ebmp"
# Time to run the analysis on the logo, we want the color map and alpha map out of the file.
try {
# Analyze is a bit slow so we only do it once and store the result in a file, check if it exists or if it has changed
ImageSource(Analyse_Name,0,0)
(Interleave( AssumeFPS(input.FrameRate), input.Trim(0,-2).AnalyzeLogo(logo_mask) ).FrameCount > 3) ? last : last
}
catch( dummy ) {
# Nice catch, we are here since we need to perform our logo analysis as none already exists
analyse.AnalyzeLogo(logo_mask)
# The analysis is complete, save a frame (all frames are the same)
Trim( 0, -1 )
ImageWriter( logo_name + loc + string(percent) + "AnalyzeResult", 0, 1, "ebmp" )
}
# The color map is the top half of the Analyze result, The alpha channel is in the bottom half
AssumeFPS(analyse.FrameRate)
LogoColor = Crop(0,0,0,last.Height/2)
LogoAlpha = Crop(0,last.Height/2,0,0).ConvertToYV12(Matrix="PC.601")
# Create a Deblend mask, this is a mask that falls off the marked logo area, we use this to blend the delogoed area back into the clip
DeblendMask = logo_mask.DistanceFunction( 255.0 / DeblendFalloff, PixelAspect=par )
# Create a repair mask for pixels that cannot be deblended
LogoAlpha.Invert.mt_lut(expr="x " + " " + string(alphatorepair) + " " + "< 255 0 ?").mt_expand.mt_inflate
RepairMask = ( RepairRadius > 0.1 ) ? DistanceFunction( 84.0 / RepairRadius, PixelAspect=par ) : last
# InpaintLogo and DeblendLogo based on user preferance
deblend = ( mode == "both" ) ? input.DeblendLogo(LogoColor,LogoAlpha) \
: ( mode == "deblend" ) ? input.DeblendLogo(logoColor,logoAlpha) : input
repaired = ( mode == "both" ) ? deblend.InpaintLogo(RepairMask, Radius=InpaintRadius, Sharpness=InpaintSharpness, \
PreBlur=InpaintPreBlur, PostBlur=InpaintPostBlur, PixelAspect=par) \
: ( mode == "inpaint" ) ? deblend.InpaintLogo(RepairMask,Radius=InpaintRadius, Sharpness=InpaintSharpness, PreBlur=InpaintPreBlur,\
PostBlur=InpaintPostBlur, PixelAspect=par) : deblend
#repaired = ExInpaint (repaired.converttorgb32, repairmask.converttorgb32, color=$ffffff,xsize=5, ysize=3, radius=36)
output = Layer(input.ConvertToRGB32, repaired.ConvertToRGB32.Mask(DeblendMask.ConvertToRGB32(Matrix="PC.601")))
output = output.converttoyv12
# post processing of the results if requested
postmask = LogoAlpha.Invert.mt_lut(expr="x " + " " + string(alphatorepair) + " " + "< 255 0 ?").mt_expand.mt_inflate
postmask = postmask.DistanceFunction( 64.0 / RepairRadius, PixelAspect=par )
#postmask = (pp > 0 && lmask) ? repairmask.DistanceFunction( 512.0 / DeblendFalloff, PixelAspect=par ) : blankclip(output,color=$000000)
post = ( PP == 1 ) ? output.minblur(1,uv=3).medianblur(3,0,0).removegrain(11) \
: ( pp == 2 ) ? output.fft3dfilter(sigma=16,sigma2=12,sigma3=8,sigma4=4,bt=3,bw=16,bh=16,ow=8,oh=8,plane=4) \
: ( pp == 3 ) ? output.mt_convolution("1 8 28 56 76 56 28 8 1","1 8 28 56 76 56 28 8 1",y=3,v=2,u=2) \
: output
output = ( pp > 0 ) ? mt_merge(output,post,postmask) : output
aa = debug ? stackhorizontal(logo_mask.ConvertToYV12.subtitle("logo mask"),logocolor.ConvertToYV12.subtitle("Logo Color"),logoalpha.ConvertToYV12.subtitle("Logo Alpha")) : nop
bb = debug ? stackhorizontal(deblendmask.ConvertToYV12.subtitle("Deblend Mask"),repairmask.ConvertToYV12.subtitle("Repair Mask"),postmask.ConvertToYV12.subtitle("Post Mask")) : nop
cc = debug ? stackhorizontal(cropped.ConvertToYV12.subtitle("Original"),repaired.ConvertToYV12.subtitle("Repaired"),output.ConvertToYV12.subtitle("Post")) : nop
# Almost done, lets blend in our repair
output = (RGB == true) ? (RGB24 == true) ? output : output.converttoRGB32() : output.converttoYV12()
final = clp.overlay(output,a, b)
RETURN debug ? stackvertical(aa,bb,cc) : final
}
FUNCTION MinBlur(clip input, int r, int "uv")
{
# Nifty Gauss/Median combination
# Taken from MCBob.avs:
uv = default(uv,3)
# process chroma if uv==3, otherwise just luma
uv2 = (uv==2) ? 1 : uv
rg4 = (uv==3) ? 4 : -1
rg11 = (uv==3) ? 11 : -1
rg20 = (uv==3) ? 20 : -1
medf = (uv==3) ? 1 : -200
# make our blur clips, r controls amount
RG11D = (r==1) ? mt_makediff(input,input.removegrain(11, rg11),U=uv2,V=uv2)
\ : (r==2) ? mt_makediff(input,input.removegrain(11,rg11).removegrain(20,rg20),U=uv2,V=uv2)
\ : mt_makediff(input,input.removegrain(11,rg11).removegrain(20,rg20).removegrain(20,rg20),U=uv2,V=uv2)
RG4D = (r==1) ? mt_makediff(input,input.removegrain(4,rg4),U=uv2,V=uv2)
\ : (r==2) ? mt_makediff(input,input.medianblur(2,2*medf,2*medf),U=uv2,V=uv2)
\ : mt_makediff(input,input.medianblur(3,3*medf,3*medf),U=uv2,V=uv2)
DD = mt_lutxy(RG11D,RG4D,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?",U=uv2,V=uv2)
RETURN (input.mt_makediff(DD,U=uv,V=uv))
}
FUNCTION m4(float x) {RETURN( x<16?16:int(round(x/4.0)*4)) }
Adub
17th February 2008, 05:08
*cracks fingers*
Looks like I now have two logo removal functions to add to the wiki.
Will post a link when done.
Note: Typo on line 18. Should say "MedianBlur", not "MediumBlur".
Oh, and finished with the wiki page. Have a look:
http://avisynth.org/mediawiki/Rm_logo
Feel free to upload any new versions there, where they will always be accessible. (Plus, easier to search for.)
K0zi
18th February 2008, 19:45
I've created logomask, but there's something wrong with cropping:
Crop: YUV images can only be cropped by even numbers (left side).
(...\rm_logo.avs, line 63)
Reuf Toc
19th February 2008, 02:08
Change lines
a = ( Rightstr( loc, 1 ) == "L" ) ? 0 : round( ( clp_width / 3 ) * multi )
b = ( leftstr( loc, 1 ) == "T" ) ? 0 : round( ( clp_height / 3 ) * multi )
c = ( Rightstr( loc, 1 ) == "R" ) ? 0 : -round( ( clp_width / 3 ) * multi )
d = ( leftstr( loc, 1 ) == "B" ) ? 0 : -round( ( clp_height / 3 ) * multi )
to
a = ( Rightstr( loc, 1 ) == "L" ) ? 0 : round( ( clp_width / 6 ) * multi )*2
b = ( leftstr( loc, 1 ) == "T" ) ? 0 : round( ( clp_height / 6 ) * multi )*2
c = ( Rightstr( loc, 1 ) == "R" ) ? 0 : -round( ( clp_width / 6 ) * multi )*2
d = ( leftstr( loc, 1 ) == "B" ) ? 0 : -round( ( clp_height / 6 ) * multi )*2
should solve your problem
Spuds
20th February 2008, 03:25
@K0zi -- I updated the script in post #2 (as well as the wiki) it should take of the issue. If it does not what Reuf Toc posted will work very well.
The changes in this version are fixing the above error, adding some basic input checking and validating the color space is valid for certain functions.
Ranguvar
21st February 2008, 02:51
Woot! Thanks man. I gave up on removing a logo from some HD NASA footage I capped a while back, now I can do it. The only semi-easy solution I could find before was through VFW (ick).
R3Z
21st February 2008, 04:38
Can you pretty please provide an example (picture) of this filter in action ?
Cheers,
R3
Adub
21st February 2008, 04:53
I will provide an image sample of the filter in progress this weekend if I have time. I was planning on doing it anyways, as I to am curious of how it will work.
K0zi
21st February 2008, 18:33
Version 0.1 with corrected cropping works. Version 0.2 doesn't work:
ConvertToYV12: invalid "matrix" parameter (RGB data only)
(...\rm_logo_v2.avs, line 104)
Example, v0.1 on default settings, logomask was made in hurry, just to see if it works:
source vs. rm_logo:
http://img88.imageshack.us/img88/1052/rmlogosrckv1.th.png (http://img88.imageshack.us/my.php?image=rmlogosrckv1.png) http://img102.imageshack.us/img102/3362/rmlogocu3.th.png (http://img102.imageshack.us/my.php?image=rmlogocu3.png)
leecoq
21st February 2008, 21:41
can you post the script ?
the crop numbers etc...
Ranguvar
21st February 2008, 23:55
I just get an error saying there's no function "DistanceFunction" :p
At this line (#84): logo_mask = logo_mask.DistanceFunction(255/deblendfalloff,PixelAspect=par).Greyscale
I'm pretty sure I have all the required filters?
EDIT: Alright, sorry, fixed that. I had to load AviSynth_C.dll and then load AVSInpaint.dll externally with LoadCPlugin. Now waiting to see if it works... LOL, if it is indeed working, this puppy takes forever with 1080p footage xD
Ranguvar
22nd February 2008, 15:38
Yep, I get the invalid matrix parameter too.
Spuds
26th February 2008, 17:57
I get the invalid matrix parameter Sorry about that, I've posted a new version 0.3 that should fix that.
takes forever with 1080p footage Oh yeah, of course almost anything else does as well. The best bet to speed that up is use the percent parameter to reduce what it has to analyze on the first pass. Thinking about it some more I can add some checking on the size of the input and change the corner cut size appropriately, should help with 1080 input. I'll work on a 0.4 version :)
In the 0.3 version I added a debug=true option, it will output some screens to help see what the script is doing as well as see what the parameters are changing. Here is an example:
rm_logo(last,logomask=videos+"cbs2.bmp",loc="br",par=4.0/3.0,mode="both",percent=30,debug=true)
http://img246.imageshack.us/img246/2597/rmexampleyf8.th.png (http://img246.imageshack.us/my.php?image=rmexampleyf8.png)
Let me know if you find other problems that need to be addressed and I'll put them in the 0.4 with 1080 improvements.
K0zi
4th March 2008, 10:39
Is it possible to remove more than one logo? (screens above)
If I use this function twice in the script, I guess it'll use the same analyze-results file every time:confused:
Spuds
5th March 2008, 05:33
@K0zi
I posted a 0.4 version, this one saves the analysis.bmp file with a unique name based off the logo name, its location and percentage. This should avoid conflicts if you want to call it twice or more in a single script.
This version also has a small change to help with wide screen clips, by allowing it to cut a smaller area for the analysis.
K0zi
5th March 2008, 21:20
:thanks:
McCauley
31st March 2008, 23:01
Hi Spuds,
just wanted to say thank you for this nice script.
I haven't finished testing it, but it seems to be really effective.
Would it be possible to add MV based image restoration to the function?
It could be something like compute the vectors of the 2 previous and following frames with a radius of the clip height/10 around the logo only in the second pass. When a block moves towards the vector and "disappears" it could be layered over the logo, or be mixed with with the Inpainting. (I hope you understand what i mean). With this limited MV fetching the script shouldn't become unusably slow.
It would also really useful, if the function could do a check if the logo is present. On many sources the logo disapears periodically (due to commercial cuts or whatever). This should be doable with Masktools (?!). If no logo is present the frame will not be taken intoaccount for the first pass and won't be delogoed in the second. Keep in mind that logos reappear (and dissapear) smoothly most times, so it should be thresholded.
Another killer feature to add could be a function to remove hardcoded subs. With masking, Inpainting and MV based restoration it should be doable, but i'm no expert. To prevent the function from removing words in the movie (cast, director etc.) the processed are could be limited to the middle of the bottom, so one must specifiy how much the processed area expands from the middle of the last line in the x and y axis.
But perhaps this should be a separate function.
I really hope i can contribute a few lines of code in the future, but atm, i can only make suggestions what could be improved based on my very limited understanding of avisynth's syntax and it's PlugIns.
Looking forward to some further development. :-)
Regards
McCauley
McCauley
5th April 2008, 21:14
Hi,
it's me again :-)
General hint: It's really important that the logo is present in every analyzed frame (check before!).See below.
Should the pure white logo be very precise or should it be a few pixels bolder than the station logo(like in Didée's delogo Function)?
Keep in mind that there is always some ringing around the logo.
When processing with a clip with sharp black bars, the analyzed area has a soft transition after applying the script. I guess that is caused by the yv12/rgb/yv12 conversion ?!
I fixed that with
overlay(last,src.crop(0,0,-1116,-1050))
not very elegant but it did the trick.
cutsize -- Why not just use the center of the logo and expand the cutsize from that? If the logo is not very big, but closer to center of the frame one clould skip analysing the border areas, since they are uzeless anyway
pp -- Post Process, 1,2 or 3. Apply some additional post processing of the removed area to further hide the logo
and artifacts. What do they in particular?! with my clips changing pp didn't do anything (or almost nothing). Do they only make diffrerence with inpainting? The logos i removed where deblending only. A simple blur(~.3) sometimes does the magic.
I refined the description a bit, maybe you want to add it to your first post:
1) Get a clip with a logo that you want removed.
Prepare your clip for the analysis pass:
check if the logo is at its place from the beginning to the end, often it appears first after a few seconds or even minutes after the movie/series has started (use trim or more advanced functions for that).
Sometimes the logo disappears due to commercial cuts or whatever, to check for that insert "selectevery(300)" (values between 100 and 500 should be appriate, the lower the value, the more frames you have to check!)
at the end of your script, skipp through them to see if the logo is present in every frame. if not you have you have to trim it out manually to get optimal results for the analysis pass.
You can also exclude the credits to save time, because they won't improve the analysis pass. A black background is not very useful to get any alpha information.
2) Save a frame from that clip and edit it in your favorite graphics program, paint the logo pure white and everything else black. Save it as a logo.bmp (any name you want).
To make it easy for yourself you should choose a frame frome the credits, or where the logo has a dark background. That helps with the painting if you're not so experienced with image processing.
3) Make a avs script such as:
#rm_logo(last,logomask=logo.bmp",loc="br",par=4.0/3.0,mode="both",percent=20,pp=1)
If youre using AVSP, i advise you to check the parameters and the prepared clip (see point 1) very carefully, and then remove the # from your script, i hit the preview button more than once and my system was
stuck due to the analysis pass with suboptimal settings. BEFORE you run the analysis you MUST save the script, otherwise the analysis detects a "change" in the script's name and will run again, when loading it into VDub or any other GUI.
After the analysis pass is finished you can remove the trim command(s) and optimize your script with deblocking, denoising, resizing, sharpening or whatever you want to apply, put the filters after the rm_logo call, since this is now your source.
percent -- The percent of total frames to use in computing the masks, the more the better and the slower.
Values above 50% shoulnd't be useful in any case, except maybe with very, very short clips.
The first time the script runs expect it to take a little time, it has to compute the color and alpha masks so it will appear to lock up on you while its doing this.
Depending on the size of your clip and the percentage of anaylysis you set, i can take some time. Up to 2 hours or more for 1080p footage on a C2D!
Regards
McCauley
Spuds
6th April 2008, 03:37
@McCauley
Thanks for all the suggestions on improvements, there are some very good ideas that I'll tinker around with to see what improvements we can get and if there is a way to implement them so they work correctly (ie logo present or not).
When processing with a clip with sharp black bars Do you have an example clip that you can upload somewhere so I can take a look at this, sounds like something is wrong with one of the conversions, probably a color range thing.
Post Process ... What do they in particular? You will notice improvements mostly for logos that you had to inpaint since that will remove the logo but leave a less 'damaged' area behind. These functions just try to blend the delogoed zone back into the frame so its more difficult to find.
Thanks for all the documentation updates, I'll update the readme and wiki with the improvements, I think you listed most of the gotchas with the delogoing process, fun huh :)
Should the pure white logo be very precise or should it be a few pixels bolder than the station logo You can be a little over the edges and it wil not hurt anything, so be close but no need to be exact. The function grows the mask internally to help blend it anyway.
Again thanks for the help and suggestions!
cobo
7th April 2008, 00:39
Avisynth gives me the message: "Analyze: Mask is empty" and cites lines 104 and 109 Of rm_logo.avs. I can't figure out what the problem is.
Spuds
7th April 2008, 21:24
I would check one of the following in order ....
1) It could not find the mask file (wrong path or filename in the function call)
2) The mask was not defined with a pure white color
3) The cut was done in the wrong area of the frame so the logo and therefore the mask were blank
Comatose
10th April 2008, 20:33
Can somebody post a mask and a logo for reference please?
Spuds
11th April 2008, 05:42
Take a close look at post #14 ... in particular the original in the lower left and the mask in the upper left. These are post "cliping" but should help you understand what is required.
If I can find the original clips from that post I'll put a link to them up as well.
manono
5th May 2008, 20:13
Hi-
I'm having a problem that maybe someone can help with. Is there a max size for a logo? I'm having unremoved pieces remaining with this project. Here's the script:
Crop(2,6,-12,-14)
LanczosResize(512,384)
rm_logo(logomask="LogoSmall.bmp",loc="BL",par=1.0/1.0,mode="Inpaint",percent=50,pp=2,Cutsize="Large")
And a series of pics.
Before: http://img141.imageshack.us/img141/7373/beforeml2.th.png (http://img141.imageshack.us/my.php?image=beforeml2.png)
After: http://img141.imageshack.us/img141/7987/afterbe1.th.png (http://img141.imageshack.us/my.php?image=afterbe1.png)
Debug: http://img141.imageshack.us/img141/6237/debugzf2.th.png (http://img141.imageshack.us/my.php?image=debugzf2.png)
The Mask: http://img147.imageshack.us/img147/6609/logosmallwq7.th.png (http://img147.imageshack.us/my.php?image=logosmallwq7.png)
I'm making the Mask.bmp (called here LogoSmall.bmp) on the cropped and resized video. Is there any way to extend the delogoed area to the right, or specify the area to be delogoed? Except for this problem, it does a pretty decent job on solid logos (in this case a song title I'd like to remove).
thetoof
5th May 2008, 20:45
Maybe you could split the logo in 2 and make 2 calls with different .bmp
Reuf Toc
5th May 2008, 21:46
You can also try InpaintFunc and enter your own parameters for "loc" to isolate the logo :
http://avisynth.org/mediawiki/InpaintFunc
Another solution is to hack spud's function to allow this feature (shouldn't be to difficult since InpaintFunc and RM_Logo are almost identical)
manono
5th May 2008, 22:25
Thanks for the responses.
thetoof, I saw yours first and it seems reasonable. Split the video in 2 at a strategic place, run 2 instances of RM_Logo and then reassemble the pieces. Then I decided I shouldn't have to do that. This thing is slow enough as it is, for the Inpaint part of it. There should be an easy way to modify the function to handle a wider logo than normal. But I didn't much want to go messing around with it myself, and still hope Spuds will come along and show me how.
Thanks, Reuf Toc. I'll try out the competition which, as you say, seems to allow locating the logo to be removed very precisely.
Wildly guessing, but it seems to be a bug in the function. Note that the actually processing ends at the very same location where the frame is cut-off for the 'debug' output.
Also, since only a part of the frame is processed and merged-in later on (that's why a "location" has to be specified), I'd expect that the dimensions of the logomask should be evaluated & used somewhere - but there's no evidence of that to be found in the script.
@manono
The way the function was written was that it made predefined cutsizes for the logo based on the position and size attributes. Its a rather dumb approach but generally sufficient for most logos. In your case the logo was wider then any of the presets so it was truncated.
I updated the function to rev 0.5 and posted it on the wiki. It has two new parameters, cutwidth and cutheight. You can set them to -1 (cutwidth=-1) for a full width cut, good for ticker tapes etc, or to a specific width (cutwidth=325) to increase or decrease the built in cut sizes. You can use debug=true and a short clip to fine tune what the values should be.
@Didée ... the function just uses a full frame sized logomask and cuts the mask and video to size based on the input (ie corner of interest). Today the mask and video must be the same size. I could do a cut size based of a mask size (if the mask was not equal to the frame size), that would be slick.
manono
7th May 2008, 07:35
Thanks, Spuds. It seems to work now:
http://img166.imageshack.us/img166/4149/afterfixedly7.th.png (http://img166.imageshack.us/my.php?image=afterfixedly7.png)
rm_logo(logomask="E:\2 Songs\Chap33\LogoSmall.bmp",loc="BL",par=1.0/1.0,mode="Inpaint",percent=50,pp=2,Cutsize="Large",cutwidth=348,CutHeight=280)
The new version also seems to require the full pathname for the BMP, rather than just the name of the BMP, as before.
sidewinder711
30th June 2008, 01:25
I do have an opaque video divided into 2 parts. On the top is a colored ball (green-blue) and below are some letters in white. Using the "inpaint"-mod, I'm nicely able to get rid of the letters.
Regarding the ball there is a problem that some part of the colors are left over (of course, inside the area of the ball, but changing its shape throughout the movie).
My code:
rm_logo(logomask="e:\test\AVSInpaint\america-maske3.png",loc="BR",par=1.0/1.0,mode="Inpaint",percent=50,pp=2,debug=true)
I tried pp from 1-3 but the results are the same. Are there any other possibilities to get rid of it ?
McCauley
31st July 2008, 23:27
Hi Spuds,
i wanted to ask if you implement some sort of deringing into your script?
I dealt with two logos the last time and both of them were alpha only.
The problem with these logos is that the always cause ringing around their edges which doesn't seem to be treated at at all by your function (pp=0,1 or doesn't change anything)
I "fixed" it with:
overlay(last,last.Crop(1486, 84, -124, -958).blur(0.30).fft3dgpu(sigma=1.7,bt=4).AddGrain(4,hcorr=0.3,vcorr=0.3),x=1486,y=84)
not very elegant, nor really pretty, but it looks better than before...
Do you have any idea how to improve the output?
Regards
McCauley
PS: Have you found the issue with the black border in the clip i sent you?
wolli0501
6th October 2008, 13:27
Hi all,
my Problem is then i start the following script with VDM .VDM give no answer(see jpeg) he does´nt work.Has anyone an idea what is wrong.
Sorry for my bad English
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\FFT3dfilter.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\removegrain.dll")
LoadCPlugin("B:\Programme\AviSynth 2.5\plugins\avsinpaint.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\exinpaint.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\masktools2\mt_masktools.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\ttempsmooth.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\medianblur.dll")
LoadPlugin("B:\Programme\AviSynth 2.5\plugins\FFT3dGPU.dll")
Import("j:\gk\rm_logo.avs")
DGDecode_mpeg2source("J:\GK\Blade Trinity.d2v")
Load_Stdcall_Plugin("B:\Programme\megui\tools\yadif\yadif.dll")
Yadif(order=-1)
crop( 2, 120, -2, -120)
rm_logo(last,logomask="j:\gk\logo.bmp",loc="TR",par=4.0/3.0,mode="both",percent=20,pp=1)
#FluxSmoothST(7,7) # Medium Noise
#Spline16Resize(704,320) # Spline16 (Neutral)
manono
6th October 2008, 14:11
Hello and welcome to the forum,
How long did you wait? It can take a very long time just to get the script to open when using the Both or InPaint Modes, perhaps as long as the movie itself. And if you think it takes a long time to open, wait until you see how long it takes to encode.
To see if it is doing a good job on the logo, you might consider trimming off 500 or 1000 frames and checking the results before then applying RM_Logo to the entire movie.
Also, why are you deinterlacing a movie with Yadif?
wolli0501
6th October 2008, 14:33
Hi
I take yadif because Megui Analyse take ít.
Ok I will test a short clip thank you.
Wolli
manono
6th October 2008, 14:36
I take yadif because Megui Analyse take ít.
If you ask me (and I know you aren't), that's a bad reason to deinterlace a movie.
wolli0501
6th October 2008, 14:45
If you ask me (and I know you aren't), that's a bad reason to deinterlace a movie.
Really?
Do have a good alternative?
manono
6th October 2008, 17:34
Do have a good alternative?
My eyes. Why don't you upload a small sample of the source for us to have a look. Use the [ and ] buttons of DGIndex to isolate a small section with movement, upload it to a 3rd party hosting site such as MediaFire, and give us the link.
wolli0501
9th October 2008, 03:04
Hi all
i have a question, is it possible to create an mask like as in Dideé ´s delkogo p.e. 120,60 for the analyse? Or maybe is ist enough for the analyse then i use trim p.e Trim1,15000)
Greats
Wolli:)
sotrry for my bad english
Leinad4Mind
14th April 2010, 19:11
Hi there, i'm trying to use this function, but it gave me error on avsp:
Crop: Destination width is 0 or less.
(rm_logo.avsi, line 85)
(remove logo.avs, line 23)
here is my code:
LOADCPLUGIN("C:\Program Files\AviSynth 2.5\plugins\AVSInpaint.dll")
MPEG2Source("C:\Users\Leinad4Mind\Desktop\Anime_TS_(1440x1080_MPEG2).d2v", info=3, cpu=0, idct=6)
mt("TFM()",threads=2) #Deinterlace
#Remove all comercial frames
mt("Loop(0,5418,7215)",threads=2)
mt("Loop(0,20133,22829)",threads=2)
mt("Loop(0,44500,45846)",threads=2)
#Remove all frames without the logo for better analysis
mt("Loop(0,5387,5422)",threads=2)
mt("Loop(0,20066,20101)",threads=2)
mt("Loop(0,43947,44433)",threads=2)
mt("Loop(0,44210,44223)",threads=2)
#TEST with 100 frames only
mt("Loop(0,100,44210)",threads=2)
#Crop and Resize
mt("crop(2, 0, -4, 0)",threads=2)
lanczos4resize(1280,720)
#Remove Logo
rm_logo(last,logomask="C:\Users\Leinad4Mind\Pictures\24BitsLogo.bmp",loc="TR", \
par=16.0/9.0,mode="both",percent=30,pp=2,Cutsize="Large")
I'm using the 24BitsLogo.bmp you can see in the attachments. And other question, can I use Test1? or Even Test2?
Best Regards!
neuron2
14th April 2010, 20:12
For your crop error, do this:
#Crop and Resize
mt("crop(2, 0, -4, -0)",threads=2)
lanczos4resize(1280,720)
Notice the change in the last parameter to Crop.
Gavino
14th April 2010, 20:25
...
mt("Loop(0,100,44210)",threads=2)
#Crop and Resize
mt("crop(2, 0, -4, 0)",threads=2)
I don't know about the rm_logo problem, but it's pointless using mt with functions like loop and crop, which take virtually zero time to execute(*). You just add the overhead of using mt, with no savings from multithreading.
(*)Loop doesn't process any pixels, it just decides which frames to include. Crop just changes a pointer to the frame dimensions, without copying any pixel data.
For your crop error, do this:
mt("crop(2, 0, -4, -0)",threads=2)
No, -0 is still 0, there's no difference.
The error msg is reported coming from a Crop inside rm_logo.avsi.
Leinad4Mind
14th April 2010, 20:49
I've seen that rm_logo is basically the InpaintFunc. So I have test this, without sucess :( (It gaves me the SAME error):
LOADCPLUGIN("C:\Program Files\AviSynth 2.5\plugins\AVSInpaint.dll")
MPEG2Source("C:\Users\Leinad4Mind\Desktop\Anime_TS_(1440x1080_MPEG2).d2v", info=3, cpu=0, idct=6)
mt("TFM()",threads=2) #deinterlace
#Remove all comercial frames
Loop(0,5418,7215)
Loop(0,20133,22829)
Loop(0,44500,45846)
#Remove all frames without the logo for better analysis
Loop(0,5387,5422)
Loop(0,20066,20101)
Loop(0,43947,44433)
Loop(0,44210,44223)
#TEST with 100 frames only
Loop(0,100,44210)
#Crop and Resize
crop(2, 0, -4, -0)
lanczos4resize(1280,720)
#Remove Logo
#rm_logo(last, logomask="C:\Users\Leinad4Mind\Pictures\24BitsLogo.bmp",loc="TR", \
par=16.0/9.0,mode="both",percent=30,pp=2,Cutsize="Large")
InpaintFunc(last, mask="C:\Users\Leinad4Mind\Pictures\24BitsLogo.bmp", loc="TR", \
AR=16.0/9.0, mode="Inpaint", speed=10, ppmode=3, pp=75)
EDIT: I have tried this kind of crop, without sucesso too:
crop(2, 2, 1436, 1078)
lanczos4resize(1280,720)
Gavino
15th April 2010, 00:05
I have tried this kind of crop, without sucesso too: ...
As I said, I don't think the problem is the Crop in your script.
What line does the error message refer to (and what is on that line) now that you've changed it?
Reuf Toc
15th April 2010, 21:36
I've found the problem (it was so obvious that it took me some time).
The problem here is your logomask. It must be the same resolution than your clip (ie 1280x720)
The cropping values are calculated from your clip and then the cropping is applied on the mask. Since the cropping values are far more higher than the mask resolution, it make sense that avisynth report an error.
Keiyakusha
27th April 2010, 03:15
Hi! Here (http://imgur.com/4K8Cc.png) is the mask. The video has the same size.
I'm calling rm_logo like that: rm_logo(last,logomask="E:\1.bmp",loc="BR",mode="both",cutsize="medium",percent=100)
however not sure if i'm doing it right, so far this function never worked for me...
Input video is yv12, avisynth 2.6, all plugins (the latest versions) are present.
So with this I'm getting an error:
Avisynth open failure: GetPlaneHeightSubsampling not available on Y8 pixel type.
rm_logo.avsi line 86
Any tips?
EDIT: Oops, it seems my bmp was grayscale and it needs to be RGB. It works now!
dansrfe
24th June 2010, 02:32
I'm getting the "Crop: Destination width is 0 or less" pointing to line 269 in InpaintFunc.avs. The mask image is a 24-bit bmp with the same resolution as the input clip.
dansrfe
24th June 2010, 20:16
Anyone?
Frank K Abbott
24th June 2010, 22:00
Yeah, I used to have the same problem too. never figured it out though :(
Reuf Toc
24th June 2010, 22:36
I'm getting the "Crop: Destination width is 0 or less" pointing to line 269 in InpaintFunc.avs. The mask image is a 24-bit bmp with the same resolution as the input clip.
If you post a picture of your source, the mask and the script you are using, I could check it. Without I can't do nothing except speculate...
dansrfe
24th June 2010, 23:51
Source (after cropping) (http://img267.imageshack.us/img267/2127/source.png)
Mask (http://img690.imageshack.us/img690/5717/16292559.png)
Script:
import("F:\inpaintfunc.avs")
Load_Stdcall_Plugin("F:\avsplugins\avsinpaint.dll")
MPEG2Source("F:\dvd.d2v", cpu=0)
Crop(0,96,-0,-98)
Inpaintfunc(mode="inpaint",loc="tc",mask="C:\a.bmp",AR=360.0/143.0)
Reuf Toc
25th June 2010, 01:13
Oups, my fault, bug in the calculation of "loc" value.
New version uploaded on the wiki here (http://avisynth.org/mediawiki/upload/6/61/InpaintFunc.avs)
dansrfe
25th June 2010, 01:38
AvsP hangs up when using it. I removed the loc paramter in my script and it says it needs the loc paramter so that's the only reason why it's hanging probably.
dansrfe
25th June 2010, 07:53
anything?
Reuf Toc
25th June 2010, 16:11
The latence between the script opening and the display in avsp is normal. During this time, AVSInpaint, the plugin used in this function, compute a mask used later in inpainting process.
If you don't change speed or loc value, this mask is only computed one time. The computation of the mask can take a long long time (sometime hours) so be patient !
manono
26th June 2010, 05:07
Right, so to test what the output might look like, I trim off 500-1000 frames or so to have a look, before doing the entire thing.
dansrfe
26th June 2010, 05:20
Source (after cropping) (http://img267.imageshack.us/img267/2127/source.png)
Mask (http://img690.imageshack.us/img690/5717/16292559.png)
After Mask (http://img580.imageshack.us/img580/8618/aftermask.png)
The mask isn't completely removing the logo and it also has this dark grey look afterwards. I wonder what's going on.
Reuf Toc
26th June 2010, 13:06
You need to make your mask a little wider than your logo. I supose this is due to the compression artefacts on the edge of the logo that make inpainting going wrong...
Source (http://img267.imageshack.us/img267/2127/source.png)
Mask (http://img706.imageshack.us/img706/5717/16292559.png)
Result (http://img715.imageshack.us/img715/5292/resultt.png)
And a new version of InpaintFunc is available on the wiki, here (http://avisynth.org/mediawiki/upload/6/61/InpaintFunc.avs), I've spotted another bug in loc calculation, and the previous correction I've done wasn't safe (cropping value could be non mod2)
dansrfe
26th June 2010, 17:55
Wow. I must say this works nicely now with your new mask. But how do I fix the warping type effect in the logo area when it inpaints? What PP option should I select to sort of "stabalize" that area? Again, this is awesome :). Thanks!
Reuf Toc
28th June 2010, 17:50
Wow. I must say this works nicely now with your new mask. But how do I fix the warping type effect in the logo area when it inpaints? What PP option should I select to sort of "stabalize" that area? Again, this is awesome :). Thanks!
Warping is difficult to fix. You can't with my post-processing. But enabling it can't harm, most people use ppmode 2 and pp strength of 100.
The only way to attenuate the warping effect is to tweak the "radius", "sharpness", "preblur" and "postblur" parameters...
BTW a new version of InpaintFunc (http://avisynth.org/mediawiki/upload/6/61/InpaintFunc.avs)is online (bug in creation of .ebmp file).
pbristow
1st July 2010, 19:12
Oops! Wrong thread. Moving it now.
:confused:
BaseballFury
27th April 2011, 20:58
Hello,
I have been using the DeLogo Script a few times in the past. I did get good results, but for some reason it could not delete the "orfeins HD" logo very well.
Now I stumbled upon this thread and that removed cbs logo on page 1 gave me new hope.
So I tried the script... I really liked that everything is done in just one step, only 1 mask is needed, but unfortunately the result is pretty much the same (compared to DeLogo).
http://img709.imageshack.us/img709/9103/rmlogo.th.png (http://img709.imageshack.us/i/rmlogo.png/)
Am I doing something wrong or is this logo hard to remove?
Here is a small sample-clip of the logo:
http://netload.in/dateiX7LA9V73sk/sample.mkv.htm
(3800 Frames, 720p. Everything but the logo area is blurred, to keep the filesize small and for copyright reasons.)
mathmax
29th April 2011, 17:10
Hello
I would like to remove a logo located at the center of my video, but the loc parameter only offer "TL", "TR", "BL", "BR"... what should I do?
Moreover, I get this error "Mask is empty"... looking at the script, it seems that it's because my white part in the mask is in the middle of the picture.
Am I using the right tool in my case to remove a logo? It rm_logo the best tool available for these kind of tasks?
BaseballFury
29th April 2011, 23:30
Hello
I would like to remove a logo located at the center of my video, but the loc parameter only offer "TL", "TR", "BL", "BR"... what should I do?
Moreover, I get this error "Mask is empty"... looking at the script, it seems that it's because my white part in the mask is in the middle of the picture.
add the parameter "cutwidth=-1".
mathmax
30th April 2011, 02:46
add the parameter "cutwidth=-1".
same error.. "mask is empty"
and which value should I use for the loc parameter since my logo is at the middle of the video?
-TiLT-
30th April 2011, 03:21
Am I doing something wrong or is this logo hard to remove?
ORF1 HD is hard to remove. Never really had success myself. Maybe it helps if you separate the logo into different parts. Keep in mind that you need to work with really small falloff or falloff borders then.
I was wondering if the logo changes a little bit on every duplicate frame (25fps input vs 50fps output with dupes), making it so hard to remove as the detection always over or undershoots.
mathmax
30th April 2011, 16:37
I wonder what are the differences between this script and avsinpaint... could anyone tell me?
-TiLT-
1st May 2011, 05:18
As far as I know avsinpaint reconstructs areas of the logo by guessing what could have been under the logo through inspecting pixels around the logo and interpolating them over the logo area.
delogo tries to recalculate the original alpha-channel the TV-station used and apply it in a negative way so the the logo would remove itself, revealing the original pixels at the logo area.
mathmax
1st May 2011, 23:17
As far as I know avsinpaint reconstructs areas of the logo by guessing what could have been under the logo through inspecting pixels around the logo and interpolating them over the logo area.
yes and that works quite nicely, as far as I can see... but I wonder if it also process on the following and previous frames.. when there is motion (especially general motion like a camera move), it would be very relevant to interpolate the pixels of the following and preceding frames. Are there any tools or filter for that?
delogo tries to recalculate the original alpha-channel the TV-station used and apply it in a negative way so the the logo would remove itself, revealing the original pixels at the logo area.
you mean for semi-transparent logos?
Mine is solid, so it won't be useful...
In fact I was more asking about the differences between rm_logo and avsinpaint :)
therealjoeblow
3rd January 2012, 06:04
When I run this script I get an error as follows:
AviSynth script error:
Analyze: Mask is empty
(C:\Program Files (x86)\megui\tools\avisynth_plugin\rm_logo.avs, line 110)
(C:\Program Files (x86)\megui\tools\avisynth_plugin\rm_logo.avs, line 115)
Can anyone point me in the right direction please?
Thanks
The REAL Joe
EDIT: Forget it - figured it out, needed to replace "br" with "tr" in the script call... dopey me.
Toilet
7th January 2012, 16:40
Hi I'm new to this but your program is better than anything I can find out there!
:thanks:
Although the removing ability does vary from episode to episode
Here's the result of my script
http://img833.imageshack.us/img833/388/11885850.th.jpg (http://imageshack.us/photo/my-images/833/11885850.jpg/)
My Script:-
LoadCplugin("C:\Program Files\AviSynth 2.5\plugins\AVSInPaint.dll")
Import("C:\Program Files\AviSynth 2.5\plugins\rm_logo.avs")
DirectShowSource("C:\Documents and Settings\Toilet\My Documents\Downloads\Anime\428 (1280x720 x264 AAC) LOGO.mp4", fps=23.976, audio=false, convertfps=true).AssumeFPS(24000,1001)
rm_logo(last, logomask="C:\Documents and Settings\Toilet\My Documents\Downloads\Anime\One Piece\logo3.bmp", loc="TR", par=4.0/3.0, mode="both", percent=75, pp=2)
It's good enough already but any chance of making it better because you can still clearly see the transparent code. Maybe I'm doing something wrong :scared:
Didée
7th January 2012, 17:08
I've never actually used this script of Spuds, but I know the underlying Delogo-filter halfway good. Looking at those masks, something has gone wrong. The alpha mask should be a light gray with the logo itself appearing dark-ish. The shown alpha mask seems dysfunctional. Also, the shown repair-mask and post-mask seem broken.
Can't tell you what went wrong, sorry. But I'm pretty confident that something went wrong.
Oh, maybe ... when using delogo.vdf directly, it is necessary that the initial logomask is plain black/red (with red being strictly RGB 255/0/0). But the shown logomask is black/white. Make it red instead, and try again?
Toilet
7th January 2012, 21:38
Thanks for the quick reply Didée!
I appreciate your help
I tried using delogo but I can't seem to get it to work!
Looking at the previous posts I can understand what you mean.
I tried everything but the repair-masks and post-masks still look like that
Do i need to load more plugins?
It still does a pretty awesome job but I'm just picky like that :P
Can anyone figure out why?
vBulletin® v3.8.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.