I) Disclaimer ------------- This plugin is released under the GPL license. You must agree to the terms of 'Copying.txt' before using the plugin or its source code. You are also adviced to use it in a philantropic state-of-mind, i.e. not "I'll keep this secret for myself" II) What it is -------------- This Avisynth 2.5 YV12-only plugin offers several functions manipulating clips as masks: - EdgeMask will build a mask of the edges of a clip, applying no thresholding - Inflate will 'expand' the high values in a picture, by putting in the output picture the maximum in a 3x3 neighbourhood around the input pixel - Invert will invert the pixel (ie out = 255 - in); this can be also used to apply a 'solarize' effect to the picture - Binarize will binarize the input picture depending on a threshold and a command. - MaskedMerge will take 3 clips and apply a weighed merge between first and second clips depending on the mask represented by clip3 In addition, all functions take 3 parameters: Y, U and V. Depending on their value, different operations are applied to each plane: - value=3 will do the actual process of the filter, - value=2 will copy the input plane to the output corresponding plane, - value=1 will not process it (ie, most often, left with garbage) - value=[-255...0] will fill the output plane with -value (ie to have grey levels, use U=128,V=128) This is intended for modularity and atomical operations (or as usefull as possible), not speed. The examples of IV) are much faster applied with the original filters. III) Revisions -------------- 1.0.2 (last version - projct dropped): - Fix the shift for edgemask using sobel and roberts (misplaced MMX instruction) - MaskMerge now works (mask cleared before being used... check with MaskMerge(clip3,clip3) for instance) 1.0.1: Initial release IV) Developper walkthrough --------------------------- Skip to V) if you're not interested in developping the tools available. The project is a VC++ 6 basic project. Each filter has its own folder which stores the header used by the interface, the source for the function members, the source for processing functions and its header. Let's look at EdgeMask: - EdgeMask.h is included by the interface to know what the filter 'looks like'. - EM_func.h describes the different processing functions (they should all have the same prototype/parameters): . Line_MMX and Line_C . Roberts_MMX and Roberts_C . Sobel_MMX and Sobel_C - EM_func.cpp, as all _func.cpp, stores the implementation of the processing functions, and sometimes their MMX equivalents. - EdgeMask.cpp implements the class; the constructor select the appropriate processing function (MMX? C? Roberts? Line? Sobel?) and uses it to fill the generic protected function pointer used in GetFrame Interface.cpp stores the export function and all of the calling functions (AVSValue ... Create_). ChannelMode.cpp defines the Channel operating modes. There could be added the equivalent of a debugprintf. This quick walkthrough won't probably help most developpers, as the examples of V) for users, but that's the best I've come with so far. It will improve of course over time depending on the success of the idea, which main drawback, speed, will probably make it scarcely used, if ever. V) Functions description (Y,U,V parameters described above) ------------------------- 1) EdgeMask(threshold=[0..255],string): - string: which differential convolution to use: . "line" will try to spot darker thin (1 pixel-wide) lines in the picture (it has an incorporated anti-noise floor which makes it ignore any variation lower than 3) . "roberts" will apply a pseudo-roberts 2x2 kernel: 2 -1 -1 0 . "sobel" will apply a pseudo-sobel 3x3 kernel: 0 -1 0 -1 0 1 0 1 0 - threshold: the typical value to set for those kernels. NOT USED EXCEPT FOR "line" BECAUSE ONLY FOR TESTING PURPOSE, BINARIZE WILL DO MOST OFTEN THE JOB 2) Inflate Replace the pixel value by the maximum in the 9 pixels of a 3x3 neighbourhood. 3) Invert Replace pixel's value by 255-pixel's value Binarize(upper=false) could be seen (but isn't processed as) Binarize(upper=true) 4) Binarize(threshold=[0..255],upper=true/false) - threshold is the limit to tell when to put 0 or 255 - upper = true will replace values higher than threshold by 0 and lower or equal by 255 upper = false simply does the opposite 5) MaskedMerge(clip2, clip3) output = (255-clip3)*input + clip3*clip2 Therefore, it isn't perfectly weighed/normalized, but will still do the job VI) Some practical uses (not tested extensively) ---------------------- Those won't produce the exact same results as the original filters they try to mimic, in addition to be far more slower. 1) MSharpen . Build EdgeMask of clip1, Binarize it and store the result into clip3 . Apply any sharpening filter to clip1 and store it into clip2 . return MaskMerge(clip1,clip2,clip3): The sharpened edges of clip2 higher than the treshold given to Binarize will be sharpened and used to replace their original value in clip1 You could also write a filter with a particular Look-up table (best would look like a bell), replace Binarize by it, and have a weighed sharpening depending on the edge value. clip2 = clip1.() #U and V planes don't need filtering, Y needs it #EdgeMask(<...>,"roberts",Y=3,U=-128,V=-128) for greyscale map clip3 = clip1.EdgeMask(,"roberts",Y=3,U=1,V=1).Binarize(,upper=false,Y=3,U=1,V=1) return MaskedMerge(clip1,clip2,clip3) 2) MSoften Replace EdgeEnhancer by a spatial softener (cascaded blurs? spatialsoftenMMX?) and use upper=true to select near-flat pixels. 3) Rainbow reduction (as described here: http://forum.doom9.org/showthread.php?s=&threadid=48167) Warning, this isn't a miracle solution either clip2 = clip1 soften at maximum (using deen("m2d") or edeen for instance) #Get luma edgemap and increase edges by inflating -> wider areas to be processed, clip3 = clip1.EdgeMask(6,"roberts",Y=3,U=1,V=1).Inflate(Y=3,U=1,V=1) #Now, use the luma edgemask as a chroma mask clip3 = YtoUV(clip3,clip3).reduceby2().Binarize(15,upper=false,Y=1,U=3,V=3) #We have to process pixels' chroma near edges, but keep intact Y plane return MaskedMerge(clip1,clip2,clip3,Y=2,U=3,V=3) 4) Supersampled fxtoon Not tested . Use tweak to darken picture or make a plugin that scales down Y values -> clip2 . Build edge mask, Supersample this mask, Binarize it with a high threshold (clamping sounds better), Inflate it -> clip3 . Apply the darker pixels of clip2 depending on the values of clip3 5) Warpsharp for dark luma Not tested . Apply warsharp -> clip2 (replacement pixels) . Create a clamping filter or a low-luma bypass filter -> clip3 (mask pixels) 6) pseudo-deinterlacer (chroma will still be problematic) Not tested . clip2 = clip1.SeparateFields().SelectEven().Resize() . clip3 = clip1.() . return MaskedMerge(clip1,clip2,clip3,Y=3,U=3,V=3) (<- chroma even more problematic) 7) Non rectangular overlays (won't probably work considering the non-linearity of YUV colorspaces) . create a filter reading a picture to use as mask and outputs it as a clip3 . Use merge(clip1,clip2,clip3); this will also perform a blending/antialiasing to the overlay 8) Replace backgrounds . create a filter reading a picture to use as background replacement and outputs it as clip2 . create a filter that loads a picture (the static background) and outputs how much difference there is between the input clip1 and this picture; Invert it or Binarize it and stores as clip3 . Use merge(clip1,clip2,clip3) to replace the background. Binarizing reduces ghost look (or purposely do it :), while Inverting may help give a more regulat look. It's all a matter of what kind of difference the filter will output and how you manipulate the mask (clamping filter addition would be usefull here). VII) TODO --------- Nothing, ConditionnalFilter has the favour of users.