View Full Version : Cel Foreground -- documentation copy (for forum search)
mg262
8th September 2005, 18:28
This thread is for documentation only. There is a thread for discussion, feedback, error reports, etc in this thread (http://forum.doom9.org/showthread.php?t=99779), which contains a link to the sectioned HTML version of the documentation, which may be easier to read.
Please do not reply to this thread.
____________________________________________________________
Cel is a family of filters designed for treating old-style (pre-CG) animation, although parts of it can be used on modern animation and even on live material.
The principle underlying all the filters is based on the following observation: old animation is built in a very specific way, and consists of two kinds of thing.
Painted background cels which are raster objects. These may pan or zoom but otherwise remain fixed for many frames (typically ~100).
Drawn foreground cels, which are essentially vector objects consisting of blocks of flat colour enclosed in black lines. These change very frequently (typically every 2 frames).
Cel Principle: the natural way to treat this material, both for filtering and for compression, is to try and separate these two kinds of object and represent each in a format that reflects its nature.
The main part of Cel is in two 'structure' filters, a Foreground filter and a Background filter. The first version of the Foreground filter, which attempts to identify foreground components and denoise them, is below. The Background filter has been in construction for much longer but is currently too slow to release. There may also be other much smaller filters which perform auxiliary tasks.
@mg262
@TBL
mg262
8th September 2005, 18:29
Cel Foreground
A denoiser for animation
by @TBL and myself
See above for a general introduction to the Cel filters. The Foreground filter attempts to break up the foreground of a clip into regions of (originally) solid colour, and then denoises each region. Here are some before and after screenshots so you can decide whether to keep reading:
Screenshots
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_14290.jpg
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_2806.jpg
Introduction
These aren't "plug and play" filters; you will have to understand how they work and tailor them to the source at hand to get the best out of them. We've always found that many of the people who understand video best (and certainly better than us) are scripters -- so part of the philosophy of Cel is to try to provide flexible building blocks for scripts rather than providing opaque stand-alone functions. But that does mean these filters aren't built quite like most other filters and will take some work to learn to use... this will become clearer as you read through.
This is a first attempt at a (for us) very ambitious project... so please go easy on us. It will take a lot of work (on the code and on scripts) before it's usable on real projects. As well as potential bugs, everything is currently slow -- in some places very slow. Were currently replacing algorithms to speed things up rather than playing with scripts; so you will find things which could definitely be improved in script... but some of you are capable of coming up with better solutions than we ever could, so we're leaving it to you!
There is one important feature which we didn't have time to implement, namely turning the foreground regions (and associated black lines) into splines or other vector objects of the kind used in drawing programs, which would help with anti-aliasing, sharpness, resizing and ultimately compression. Until this is done it's quite hard to deal with the black lines, so we are currently aiming to pass them through the filter unchanged.
We know there are plenty of features that break the simple model in use (e.g. semitransparent foreground objects). The general approach for dealing with this will be explained after Cel Background is released, but for the moment, we're trying to find a solution that works in most cases. And, one last warning: this filter was designed for Western animation and will probably need adjustment for anime (see notes below).
The filter itself is linked to from the last post on this thread.
Independence of Tasks
Denoising of the foreground and separation of foreground from background are completely separate tasks; this is best explained by a picture (compare this with the previous (http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_2806.jpg) screenshot):
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_2806_Independence.jpg
The foreground denoising has damaged the background of the picture, but this is entirely irrelevant as the masks produced by separation can be used to preserve the background.
The primary task of this filter (and this thread) is foreground denoising. This is because proper separation involves both background and foreground models, and so requires both the Background and the Foreground filters. So the next posts will first describe in detail the intended method of foreground denoising in Cel, and then give some methods of separation using Foreground alone, meant as placeholders until Cel Background is available.
mg262
8th September 2005, 18:30
Denoising
0. Before Starting
For convenience, before beginning, we will put the source in a variable:
source = AVIsource(...)
And we will also define coloured clips of the same size as the source:
white = blankclip(source, color=$ffffff).converttoYV12
black = blankclip(source, color=$000000).converttoYV12
red = blankclip(source, color=$ff0000).converttoYV12
The source should be progressive YV12. If the source is essentially progressive but has been encoded as interlaced, do not separate the fields (but IVTC if necessary).
1. A Simple Script
The simplest denoising script looks like this:
seeds = source.Blur(1).Blur(1).LocalMaxima
simplegraph = source.ConstructFloodGraph(seeds, 25, 120) #strong settings... see below
source.ColourGraphByAverage(simplegraph, source)
The key line is: simplegraph = source.ConstructFloodGraph(seeds, 25, 120). It attempts to cut the frame up into regions of similar colour. What is stored in simplegraph is a list of regions (or 'graph'). (Since this is a purely spatial filter, we will speak of one frame at a time to avoid having to say "list of regions per frame", "graph per frame", etc.) It is not a video clip; if you try and view it directly you will see nonsense. There are several ways to visualise it, including:
red.ColourGraphAtRandom(simplegraph)
This colours every region in the graph with a random colour, which makes it easier to distinguish regions. Like every other function in this filter, it paints on top of its first argument -- in this case, red.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_26208_regions.jpg
We could instead have coloured each region according to its area, perimeter, etc. The graph is a object in its own right, quite independent of the clip which we derived it from. But there are also colouring methods which take a graph and a "reference" frame, and colour each region according to some property of the reference frame in that region. For example:
red.ColourGraphByAverage(simplegraph, source)
This colours every region in simplegraph with the average colour of source in that region. Some pixels do not belong to any region, and they show up as red "holes" in the picture.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_26208_averageonred.jpg
The first and simplest denoising method is this:
source.ColourGraphByAverage(simplegraph, source)
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_26208_averageonsource.jpg
The weaknesses of this method will be discussed below, but first...
2. How it Works
Time to explain the underlying method. ConstructFloodGraph(seeds, 25, 120) works by taking a "seed point", flood filling out from that pixel to find a region of similar colour, taking a new seed point that is not in any existing region, flood filling to make a new region, and continuing until there are no seed points left. (Flood filling works exactly like the Magic Wand tool in Photoshop when 'contiguous' is switched on.)
The crude method used to generate the seeds for all these examples is this:
seeds = source.Blur(1).Blur(1).LocalMaxima #LocalMaxima returns white if the pixel is at least as bright as its 4 neighbours
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_26308_seeds.jpg
Luma >= 128 defines a seed point. The seed clip should
Exclude the black lines (very important)
Include as much foreground as possible (quite important)
Preferably exclude the background (not important at all)
The simple seed clip used here is definitely very suboptimal, and is clearly causing leakage and creating holes (both explained in the next section). See section 7 for an improved seed clip.
3. Thresholds
Back to ConstructFloodGraph(seeds, 25, 120) again. The 25 and 120 are luma and chroma thresholds defining how similar to a seed point a pixel has to be to be included in a region. They are not on the same scale -- the chroma threshold needs larger adjustments.
If the thresholds are set too high, regions of dissimilar colour may be merged or the flood fill may 'leak' through thin boundaries separating regions of similar colour.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21679_highthresholds.jpg
If the thresholds are set too low, the regions will be small, there will be many of them (slowing down later operations) and if the seeds clip is as crude as it is above, more holes will be created.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21679_lowthresholds.jpg
In some situations, when the colour of the source that shows through the hole differs substantially from the average colour of a region, the holes can show up as discoloured spots in the output. (Some of these were present in this (http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_2806.jpg) screenshot.) Improving the seeds clip will remove some holes. Another option is to try and remove them by postprocessing using masks... see the notes below.
4. A Better Method
Warning: the absorb operations now used are currently very slow. You may want to wait for the next version which should speed them up.
Depending on the complexity of the animation, adjusting ConstructFloodGraph thresholds may not be enough to denoise well without leakage. The problem is that a few stray pixel values may cause the flood fill to e.g. leak through an edge. We can deal with that using this script:
graph = ConstructFloodGraph(source, seeds, 11, 30)
sizemask = black.ColourGraphBySize(graph, 0, 40)
graph = graph.MaskGraph(sizemask)
absorbedgraph = graph.AbsorbByColour(source, 6, 25, 400000)
The central idea is that we use ConstructFloodGraph to divide the picture up into very small regions, using low thresholds to avoid leakage, and then join those regions together with AbsorbByColour to make bigger regions. Even though the regions output by ConstructFloodGraph are small, they are much larger than single pixels and so are much less susceptible to noise.
Back to the script. The purpose of the MaskGraph is simply to reduce the number of regions to speed up the subsequent Absorb call. MaskGraph takes a graph and a reference frame in which each region of the graph is coloured in a single colour. It then throws away all the regions which have luma below 128, and returns the slimmed down graph. (The reasons for building MaskGraph in this way are in the notes below.)
So in this example, we take a graph and colour each region with a colour representing its size:
sizemask = black.ColourGraphBySize(graph, 0, 40) #size 0 is coloured black (luma 0) and size 40 white (luma 255)
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21337_sizemask.png
And then we throw away the regions which have luma below 128 (i.e. regions of size less than 20):
graph = graph.MaskGraph(sizemask)
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21337_maskedbysize.png
(Sometimes using Tweak after ColourGraphAtRandom make the result a little easier on the eyes...)
This will of course increase the number of holes. (See notes below on dealing with holes.) Increasing the 40 would speed up the script at the cost of more holes; decreasing it would mean fewer holes but a slower script. As noted above, the absorb code will be sped up, which may remove the need for MaskGraph here.
absorbedgraph = graph.AbsorbByColour(source, 6, 25, 400000)
AbsorbByColour repeatedly merges neighbouring regions in the following way: two regions may be merged if their average luma and chroma are sufficiently 'close' -- where 'close' is defined by a luma threshold (here 6) and a chroma threshold (here 25). AbsorbByColour repeatedly takes the smallest region with at least one 'close' neighbour and merges it with its 'closest' neighbour (of any size). It only stops when none of the regions smaller than a size limit (here 400000) is close enough to a neighbour to be merged.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21337_initialandabsorbed.png
5. All about [I]DrawGraph...
In order to use the absorb functions effectively, you will need to understand the structure of the graph -- not just what the regions are, but also which regions touch which other regions. This is the kind of script used to do that:
sizemask = black.ColourGraphBySize(graph, 0, 400) #400 will have to be adjusted depending on ConstructFloodGraph arguments
smallgraph = graph.MaskGraph(sizemask) #remove regions with size below 200
black.ColourGraphByAverage(smallgraph, source).DrawGraph(smallgraph)
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_23445_drawgraph.jpg
DrawGraph puts a blue dot at the 'centre' of each region and draws a blue line between every two regions that touch. Because (with very curved regions) it can be hard to tell which pixel belongs to which region, we have this:
black.ColourGraphByAverage(smallgraph, source).DrawGraphShowingAverage(smallgraph, source)
Every region is marked with a little square showing its average value in source.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_21288_drawgraphshowingaverage.jpg
A particularly useful combination is:
black.ColourGraphAtRandom.(smallgraph).DrawGraphShowingAverage(smallgraph, source)
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_24474_drawgraphshowingaverageonrandom.jpg
Finally, we can colour the lines to show which pairs of touching regions are close enough to be absorbed (red if they cannot be absorbed and blue if they can):
graph = ConstructFloodGraph(source, seeds, 11, 30)
sizemask = black.ColourGraphBySize(graph, 0, 150)
smallgraph = graph.MaskGraph(sizemask) #remove regions with size below 200
black.ColourGraphByAverage(smallgraph, source)
last.ShowColourCriterion(smallgraph, source, 6, 25) #luma threshold is 6, chroma threshold is 25
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_10885_showcolourcriterion.jpg
Regions are only merged/absorbed when they are close in both luma and chroma. So by using the above function with one threshold set very high, you can ascertain which of the two thresholds should be changed to allow/prevent two regions being merged.
The information about which regions touch is just as much a part of the Graph as the regions themselves. In order to understand how the filter works, and especially to be prepared for future advanced Graph functions, it's worth playing with these commands extensively.
6. Gradients and AbsorbByTouchingColour
Sometimes in the process of transfer from the original Cel to the final medium, blocks of flat colour will have become shallow gradients. Often these gradients are relatively hard to see by eye (especially as they are overlaid with a good deal of noise); but when they are passed through the script in the previous section, they produce a kind of banding:
Setting the AbsorbByColour thresholds high enough to prevent these will often result in unwanted side-effects. The average colour of the regions is simply too different for this to work. A better solution is to fuse these banded regions together by looking not at their average colour, but at their colour in the areas where they touch, i.e. near the shared border of a pair of touching regions:
AbsorbByTouchingColour(source, 2.5, 5, 40, 400000) 2.5 is the 'radius' out from the borders to consider.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_5393_absorbtouchingcolour.png
AbsorbByTouchingColour is currently extremely slow and should probably not be used until the next release of the filter. If you do use it, it is best to remove small regions before using it; an example that does this is given in the next section.
7. A Complex Script
The previous sections have outlined the building blocks... now something to give you an idea of how they might be put together. Bear in mind that this is not the only way; it's not the best way; it's just one possible way to do things.
seeds1 = source.Blur(1).Blur(1).LocalMaxima.inflate.inflate.inflate.binarize(40, upper=false)
seeds2 = source.Blur(1).Blur(1).Blur(1).LocalMaxima.inflate.inflate.inflate.binarize(40, upper=false)
seeds = logic(seeds1, seeds2, "or") #uses MaskTools
graph = ConstructFloodGraph(source, seeds, 11, 30)
sizemask = black.ColourGraphBySize(graph, 0, 40)
#graph = graph.MaskGraph(sizemask) #uncomment for better, very slow, results
absorbedgraph = graph.AbsorbByColour(source, 6, 25, 400000)
sizemask = black.ColourGraphBySize(absorbedgraph, 0, 1500)
largegraph = absorbedgraph.MaskGraph(sizemask)
gradientabsorbedgraph = largegraph.AbsorbByTouchingColour(source, 2.5,5, 40, 400000)
source.ColourGraphByAverage(absorbedgraph, source)\
.ColourGraphByAverage(gradientabsorbedgraph, source) #remove bands
source.overlay(last, mask = seeds) #postprocessing
The colouring has been done in two steps in order to make sure AbsorbByTouchingColour is only called with large regions (those with size 750 or above). First the source is coloured in with banding present, and then the bands are removed by painting the output of AbsorbByTouchingColour over them. This avoids the large holes that would have resulted if the output of AbsorbByTouchingColour were painted directly over the source.
This script contains an improved seed clip; this clip is also used after filtering to preserve certain parts of the picture unchanged. This prevents jaggedness where two blocks of foreground colour meet. Postprocessing like this can deal with a variety of problems... more about this in the notes below.
http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundDocumentation_7899_postprocessing.jpg
Don't expect to cut and paste this script into an .avs and obtain sensible results. A different source will require a different script. Build up your script line by line and examine the results of each line visually to make sure you're on track. The concepts and the basic building blocks have been covered above, and there are more functions to play with in the reference section below... so go and experiment!
mg262
8th September 2005, 18:31
Separation
Having made you read that much on denoising, I don't want to make this section too long -- especially since (as noted above) the proper method will require Cel Background. The method used for the initial screen shots is quite simplistic; we take an absorbed graph produced in denoising and obtain a separation mask like this:
white.ColourGraphByLumaVariance(graph, save, 0, 50)
invert
foregroundgraph = graph.MaskGraph(last)
[Then the final masked denoising is simply done by source.ColourGraphByAverage(foregroundgraph, source).]
In other words, regions whose luma variance is less than 25 are kept as foreground. (Variance is a mathematical measure of how different the values in a region are... so flat blocks of a single colour have very low variance.)
Now this can definitely be improved upon... for example, we can distinguish small regions and large regions and use different thresholds for the two. This can be done by using ColourGraphBySize to produce a mask of small regions, using this to combine the results of two calls to ColourGraphByLumaVariance, and then passing the result into MaskGraph. (We can write out such a script on request.) In fact, the reason MaskGraph exists as a single function (as opposed to MaskedGraphBySize, etc.) is to emphasise the fact that thresholding on a single property is generally suboptimal.
Here is another example which relies on edges being excluded from the graph well, so that foreground regions do not touch background regions. (This can be done by preprocessing or by improving seeds... but if someone can build a really good edge mask, we can easily adjust the algorithm to ensure exclusion of edges from the graph.)
collapsedgraph = graph.AbsorbByColour(white, 1000000, 1, 1)
variance = white.ColourGraphByLumaVariance(graph, save, 0, 50)
white.ColourGraphByAverage(collapsedgraph, variance, 0, 255)
invert
foregroundgraph = graph.MaskGraph(last)
The idea here is is to compute the average variance of each group of touching regions (weighted by the size of the region). This means that small foreground regions will generally end up being included (because they typically touch larger foreground blocks of flat colour) and background regions of flat colour will be excluded (because they touch many other background regions).
mg262
8th September 2005, 18:32
Notes
Postprocessing
It may often be cheaper (in time) or simpler to remove certain kinds of artefacts by postprocessing than to prevent these artefacts from occurring in the first place. Postprocessing can also be used to compensate for the fact that certain features are not yet implemented. The masking to preserve colour boundaries in the last section is a good example; eventually, with vectorisation, comparison/matching of vector objects over time and anti-aliasing, this will not be necessary... but for the time being it's very convenient.
Since postprocessing involves standard use of AVISynth, we will run through a few examples very succinctly -- the list is definitely not meant to be exhaustive. The first example is removing holes. Finding the holes is easy... we can get a mask showing which pixels are not in any region of a graph like this (see the reference section to understand why this works):
black.ColourGraphBySize(simplegraph, 0, 1)
Now the holes we are interested in are not just the black areas of this mask, but the small black areas which are surrounded by white. We can get hold of these by e.g. applying a blur or (using MaskTools) Inflate a few times, then applying Binarize with a high threshold and upper = true, then removing the original mask. There are now plenty of ways to remove these small holes; one way is to use the mask just obtained in a masked overlay with the filter output and a blurred version of the filter output.
The second example is applying some kind of temporal smoothing. This is particularly useful to remove residual temporal flickering in solid blocks of colour (but first make sure gradients are being properly removed). Again this is a case where a future version of the filter will contain a 'proper' solution, namely vectorising the foreground regions and then comparing them across time to see which correspond to the same object, followed by equalising colours appropriately. But for the time being, applying a temporal denoiser is the best option, for example one of the kind that removes temporal fluctuations.
The third example is to reduce the risk of damaging the background by limiting the change made by the filter in some way. kassandro's (Temporal)Repair ( http://www.removegrain.de.tf/ ) provides an extensive set of operations for this. Another alternative is to take the difference between the original and the filter version and high pass filter it before adding it back to the original. And so on...
Preprocessing
Where possible, rather than trying to adjust the filter to cope with a particular kind of distortion, it is better to remove that distortion before filtering. For example, in material I am working with, original blocks of solid colour have become gradients due to a kind of diffuse "luma bleeding". Rather than (or as well as) using AbsorbByTouchingColour, we could try to remove these by prefiltering, for example like this (link).
One general problem with doing this before processing is that it can increase the noise level, affecting the segmentation. You can get around this as follows: generate a list of regions using the original picture, with glow present, then use absorb on that list using the *filtered picture. Because the second command looks at groups of pixels in the filtered picture, it is much less affected by noise than a direct approach would be.
Thresholds
All the thresholds work in the same way; they take two colours (e.g. the colour of the seed pixel and another pixel, or the colours of two regions) and say those colours are 'close' precisely when:
-- The luma values differ by less than the luma threshold AND
-- The sum of the squares of the difference in U and the difference in V is less than the chroma threshold.
This is unnatural in the following sense: suppose we take the luma threshold as 6 and the chroma threshold as 30. Now two pixels whose luma differs by 5 and whose sum-squared chroma differs by 29 are 'close'... but pixels with identical luma whose luma differs by 30 are not 'close'.
A more natural measure would be to (e.g.) add some function of the luma difference to some function of the chroma difference. The reason we have chosen this method instead is that it is possible to see which threshold is 'blocking' two regions from being close, which makes it much easier to adjust the thresholds to appropriate values (using ShowColourCriterion).
Having said that, the code is built in such a way that new thresholds can be added very quickly...
Properties and Criteria
It should be pretty obvious from the reference section that the code is built in a way that allows properties like size, perimeter, average colour, variance, etc to be interchanged quickly. New properties can also be added very easily... but bear in mind that the properties listed are just building blocks. All the graph functions which take 'properties' as inputs are designed to take video frames in which (e.g.) the brightness of a region represents the property value. So by using MaskTools and built-in AVISynth functions, you can very easily build up complex properties from the basic ones.
It may be less obvious that the merger/absorption criteria are also easily interchangeable... but they are. At the moment, they are restricted to comparing colours (average colours either in the two regions or on the touching areas of the two regions), but we may add more... but as above, bear in mind that you can absorbed depending on complex criteria by synthesising appropriate clips.* One particular change we might make is to modify the criteria to match perceptual colour differences better; in particular, colours very close to white seem like they might need special treatment (cf the eye of the man in this screenshot (http://people.pwf.cam.ac.uk/mg262/posts/Foreground/ForegroundA_2806.jpg) above).
*(We may also modify the absorb code so rather than size being used as a limit, a mask or possibly two masks can be passed in as limits. Actually, we may or may not implement a number of more general graph functions like splitting and merging graphs to allow this kind of behaviour... but it's not yet clear whether that's the right thing to do.)
The point we are trying to make, in relation to properties and criteria but also much more generally, is that a graph is independent of any picture and that you can interpret a graph with respect to several different reference pictures.
Anime
These filters are being designed very specifically for Western animation from the 80s or so, since that is the main kind of content I (@mg262) work with. I think they might need a fair bit of adjustment in order to deal with anime... but I have never attempted to work with it, so I can't be very concretely helpful. (Sorry! I am of course very willing to participate in discussions about this in threads.) There is one particular feature I have noted, although I don't know whether its general: in (old) Western animation it is relatively uncommon for two blocks of different foreground colours to touch without there being a black line between them, although it certainly does happen; in anime this effect seems to be very frequent. If this effect is general, it would need to be dealt with carefully (cf the postprocessing at the end of the denoising section)... but there is surely more to do.
mg262
8th September 2005, 18:33
Reference
(To be completed shortly. Do post in the discussion thread if you want descriptions for particular functions.)
Closeness and Thresholds
Many of the functions below use a common notion of closeness with respect to a luma threshold luma and a chroma threshold chroma. Two colours (Ya, Ua, Va) and (Yb, Ub, Vb) are said to be 'close' if both |Ya-Yb| < luma and (Ua-Ub)^2 + (Va-Vb)^2 < chroma. The notion of 'closer' is derived in the obvious way from the distance function max(|Ya-Yb|/luma, ((Ua-Ub)^2 + (Va-Vb)^2)/chroma).
Graph Functions
Graph GraphAbsorbByColour(Graph, clip ref, int luma, int chroma, int maximumsize)
Graph AbsorbByTouchingColour(Graph, clip ref, int radius, int luma, int chroma, int maxsize)
Graph ConstructFloodGraph(clip ref, clip seeds, int luma, int chroma)
Graph MaskGraph(Graph, clip mask)
Graph MergeGraphByColour(Graph, clip ref, int luma, int chroma)
Colouring Functions
All these functions paint on top of canvas.
clip ColourGraphAtRandom(clip canvas, Graph)
clip ColourGraphByAverage(clip canvas, Graph, clip ref)
clip ColourGraphByAverageLuma(clip canvas, Graph, clip ref)
clip ColourGraphByLumaVariance(clip canvas, Graph, clip ref, int min, int max)
clip ColourGraphByLumaVarianceBySize(clip canvas, Graph, clip ref, int min, int max)
clip ColourGraphByPerimeter(clip canvas, Graph, int min, int max)
clip ColourGraphBySize(clip canvas, Graph, int min, int max)
clip ColourGraphByVariance(clip canvas, Graph, clip ref, int min, int max)
Colour each region in the graph by the value of the appropriate property in the region (evaluated on ref where applicable). Properties which are not colours are displayed as grayscales, with property value min giving luma 0 and property value max giving luma 255. Values outside the range min-max give black or white appropriate. min and max do not actually have to be legal property values, and can be negative if desired.
ColourGraphByNearbyAverage(clip canvas, Graph, float radius, clip ref)
ColourGraphByNearbyAverageLuma(clip canvas, Graph, float radius, clip ref)
ColourGraphByNearbyLumaVariance(clip canvas, Graph, float radius, clip ref, int min, int max)
ColourGraphByNearbyPerimeter(clip canvas, Graph, float radius, int min, int max)
ColourGraphByNearbySize(clip canvas, Graph, float radius, int min, int max)
ColourGraphByNearbyVariance(clip canvas, Graph, float radius, clip ref, int min, int max)
Colour each region in the graph by the value of the appropriate property (evaluated on ref where applicable) in the pixels which are within radius of the region, but not in the region itself. Properties which are not colours are displayed as gray scales, with property value min giving luma 0 and property value max giving luma 255. Values outside the range min-max give black or white appropriate. min and max do not actually have to be legal property values, and can be negative if desired.
Graph Drawing Functions
All these functions draw on top of canvas.
DrawGraph(clip canvas, Graph, bool showlines = true)
Draws a blue dot at the centre of each region in the graph, and (if showlines = true) draws a blue line between the centres of every pair of touching regions.
DrawGraphShowingAverage(clip canvas, Graph, clip ref, bool showlines = true)
DrawGraphShowingAverageLuma(clip canvas, Graph, clip ref, bool showlines = true)
DrawGraphShowingSize(clip canvas, Graph, int min, int max, bool showlines = true)
DrawGraphShowingVariance(clip canvas, Graph, clip ref, int min, int max, bool showlines = true)
DrawGraphShowingLumaVariance(clip canvas, Graph, clip ref, int min, int max, bool showlines = true)
DrawGraphShowingPerimeter(clip canvas, Graph, int min, int max, bool showlines = true)
Draws a small coloured square at the centre of each region in the graph, and (if showlines = true) draws a blue line between the centres of every pair of touching regions. The square is coloured with the value of the appropriate property in the region (evaluated on ref where applicable). Properties which are not colours are displayed as grayscales, with property value min giving luma 0 and property value max giving luma 255. Values outside the range min-max give black or white appropriate. min and max do not actually have to be legal property values, and can be negative if desired.
ShowColourCriterion(clip canvas, Graph, clip ref, int luma, int chroma)
Draws a blue dot at the centre of each region in the graph, and (if showlines = true) draws a line between the centres of every pair of touching regions.
Auxiliary Functions
BoxBlur(clip, int radius)
LocalMaxima(clip)
mg262
8th September 2005, 18:38
Versions
CelForeground, 08 September 2005 (http://people.pwf.cam.ac.uk/mg262/posts/Foreground/CelForeground_08Sep05.dll)
The absorb code in this version is very slow. A faster version will be coming out.
mg262
8th September 2005, 18:55
____________________________________________________________
This thread is for documentation only. There is a thread for discussion, feedback, error reports, etc here (http://forum.doom9.org/showthread.php?t=99779).
Please do not reply to this thread.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.