MaskTools

Abstrato

autor: kurosu e Manao
versão: 1.5.1
download: http://www.geocities.com/manao47/Filters/
categoria: Miscelânea de Plugins
requisitos: YV12 Colorspace

Tabela dos conteúdos

I) Sobre MaskTools

1) Versão Simples

Depois do processo, você pode precisar manter só parte da produção. Digamos, você tem um clipe suavizado, resultado de uma suavização (blur() por exemplo) em um clipe fonte.

A maioria do ruído da fonte desapareceu, mas assim tem detalhes. Você pode manter só os píxeis filtrados e descartar os que têm grande diferença de cor ou brilho.

Isso é o que faz MSmooth por D. Graft, por exemplo. Agora considere que você grava em uma imagem de píxeis da suavização e quer manter como píxeis brancos e os outros da fonte como píxeis pretos. Você obtém uma máscara. MaskTools que trata da criação, encarecimento e manipulação de tal máscara para cada componente do colorspace YV12.

2) Descrição

Este plugin YV12 do Avisynth 2.5 oferece várias funções que manipulam clipes como máscaras:

Além disso, todas as funções têm 3 parâmetros: Y, U e V (menos as funções FitPlane onde obviamente o nome diz o que é processado). Dependendo do seu valor, são aplicadas operações diferentes para cada plano:

Um último ponto é a habilidade de algumas funções para processar só uma parte da armação:

Isto foi planejado para operações modulares e atômicas (ou tão útil quanto possível), não realmente velocidade. Ficou inchado e lento. Eu o deixo decidir se esta declaração é totalmente verdade, ou um pouco menos... Os exemplos em III provavelmente são muito mais rápidos se aplicados com os filtros originais.

II) Descrições da Função 

Binarize

Binarize (clip, int "threshold", bool "upper")

O filtro Binarize permite um percentual básico de um quadro. Se upper=true, um píxel cujo valor é estritamente superior ao percentual será fixado em zero, ou senão para 255. Pelo contrário, se upper=false, um píxel cujo valor é estritamente superior ao percentual será fixado em 255, ou senão em zero.

Os padrões são threshold = 20 e upper = true.

CombMask

CombMask (clip, int "thY1", int "thY2")

Este filtro produz uma máscara que exibe áreas penteadas. Os percentuais trabalham para os outros filtros como: depois de calcular o valor pente, se este está abaixo de thY1, o píxel é fixado em 0, acima de thY2, é fixado em 255 e entre eles é fixado ao valor pente dividido por 256.

O valor pente é (upper_pixel - pixel)*(lower_pixel - pixel). Assim, não é normalizado à gama 0.. 255, porque se fosse, o valor estaria perto de 1 ou 2, não mais. Isso significa que você pode usar percentual mais alto que 255, mesmo que eles não sejam úteis.

Os padrões são thY1 = 10 e thY2 = 10 (assim, uma máscara binária).

DEdgeMask

DEdgeMask (clip, int "thY1", int "thY2", int "thC1", int "thC2", string "matrix", float "divisor", bool "setdivisor")

Este filtro cria uma máscara de extremidade do quadro. O algoritmo de achar extremidades usa um núcleo de convolução e o resultado é então percentualizado com thY1 e thY2 (luma) e thC1 e thC2 (croma). O percentual acontece como (r é o resultado da convolução):

Para criar uma máscara binária, você só tem que fixar th1=th2.

A escolha do núcleo de convolução é feita com matrix. A matriz deve ser 3 por 3, cujos coeficientes são inteiros, separados por um só espaço. Conseqüentemente, as strings "-1 -1 -1 -1 8 -1 -1 -1 -1" e "0 -1 0 -1 0 1 0 1 0" darão núcleos respectivamente para o "laplace" e "sobel" do filtro EdgeMask.

Como os coeficientes devem ser inteiros, divisor é usado para refinar o resultado da convolução. Este resultado simplesmente será dividido pelo divisor. Se ele não estiver definido, é padronizado à soma do coeficiente positivo da matriz, permitindo assim uma normalização clássica. Pode ser um flutuante ou um inteiro, sendo que o posterior é mais rápido. setdivisorestá presente só para compatibilidade inversa. Não use.
Os padrões são: thY1 = 0, thY2 = 20, thC1 = 0, thC2 = 20 e matrix = "-1 -1 -1 -1 8 -1 -1 -1 -1".

EdgeMask

EdgeMask (clip, int "thY1", int "thY2", int "thC1", int "thC2", string "detector")

Este filtro cria uma máscara da extremidade do quadro. O algoritmo de achar extremidade usa um núcleo de convolução, e o resultado é então percentualizado com thY1 e thY2 (luma) e thC1 e thC2 (croma). O percentualizado acontece como (r é o resultado da convolução):

Para criar uma máscara binária, você só tem que fixar th1=th2.

A escolha do núcleo de convolução é feita pelo detector:

Finalmente, também há dois outros possíveis valores para detector ("cartoon"  e "line"), que têm comportamentos não são documentados aqui.

Os padrões são: thY1 = 0, thY2 = 20, thC1 = 0, thC2 = 20 e detector = "sobel".

FitY2U / FitY2V / FitY2UV  FitU2Y / FitV2Y / FitU2V / FitV2U

FitPlane (clip, string resizer

FitPlane tem as seguintes formações:
- luma para croma: FitY2U, FitY2V, FitY2UV
- croma para luma: FitU2Y, FitV2Y
- croma para croma: FitU2V, FitV2U

Você pode assim reproduzir uma máscara criada em um plano particular para outro plano.

Inpand / Expand / Deflate / Inflate

Inpand (clip)
Expand (clip)
Deflate (clip)
Inflate (clip)

Este filtro permite aumentar / reduzir uma máscara. Expand substituirá o valor de um píxel pelo valor circunvizinho mais alto. Inpand ao contrário, substituirá pelo mais baixo valor circunvizinho. Inflate computará os meios píxeis circunvizinho e substituirá o valor deste píxel só se este meio for superior ao valor do píxel original. Deflate fará o mesmo só se o meio for inferior ao valor original.

O quadro retornado por Expand / Inflate sempre será mais alto que o quadro original. Pelo contrário, o retornado por Inpand / Deflate sempre será mais baixo.

O aumentado / reduzido produzido por Deflate / Inflate é mais suave que o de Expand / Inpand.

HysteresyMask

HysteresyMask (mask_clip1, mask_clip2)

Este filtro cria uma máscara de duas. Teoricamente, a primeira máscara deve estar dentro da segunda, mas pode trabalhar se não for true (entretanto os resultados serão menos interessantes). O princípio do filtro é aumentar as partes que pertencem a ambas as máscaras, dentro da segunda.

Este algoritmo é interessante porque permite obter uma máscara da extremidade com todas as extremidades interessadas, por exemplo, mas sem o ruído. Você constrói para aguçar máscaras, um com muitas extremidades e ruído, o outro com algumas extremidades e quase nenhum ruído. Então, você usa este filtro e deve obter as extremidades, sem o ruído, porque o ruído não estava lá na segunda máscara.

Invert

Invert (clip, int offX, int offY, int w, int h)

Este filtro troca o valor do píxel pelo de valor 255.

Binarize(upper=false) pode ser visto (mas não é processado) como 

Invert().Binarize(upper=true)

Logic

Logic (mask_clip1, mask_clip2, string "mode")

Este filtro produz uma máscara nova que é o resultado de uma operação binária entre duas máscaras. A operação é escolhida com o parâmetro mode.

Se um operador lógico for usado com uma máscara não binária, os resultados são imprevisíveis.

Padrão: mode = "and".

RGBLUT / YV12LUT / YV12LUTxy

YV12LUT (clip, string "yexpr", string "uexpr", string "vexpr")
RGBLUT (clip, string "yexpr", string "uexpr", string "vexpr", string "AMPFile")
YV12LUTxy (clipx, clipy, string "yexpr", string "uexpr", string "vexpr")

Estes filtros aplicam uma função a cada píxel do quadro. Para permitir uma computação rápida, todo possível valor da função é pré-computado e armazenado num tabela. Isso faz os filtros bastante rápidos. RGBLUT trabalha do mesmo modo como YV12LUT, a não ser por ter um argumento adicional AMPFile. Ele permite carregar um perfil de cor do photoshop. 

Para poder aplicar quase toda possível função, este aqui é determinado por uma string que representa uma expressão em notação de polimento contrário. O princípio desta notação é gravar primeiramente os operandos / parâmetros de um operador / função e então seus operador / função. Conseqüentemente, "3 + 7" se tornam "3 7 +", e "sin(3)" se torna "seno 3". Indo além nas explicações, "3 * 7 + 5" se tornam "3 7 * 5 +", e "(3 + 7) * 5":"3 7 + 5 *." Agora, você entende o recurso principal desta notação: nenhuma necessidade de parênteses.

Computações são líderes em números reais. Números positivos também representam uma verdadeira declaração, enquanto que números negativos representam uma declaração falsa. Na string, o símbolo "x" é o valor do píxel antes do uso da função. Para YV12LUTxy você também tem o símbolo "y" que representa o valor do píxel disposto no segundo clipe. Os símbolos devem ser separados por um único espaço.

Alguns operadores e funções estão implementados:

Alguns exemplos:

* A binarização do quadro com um percentual em 128: "x 128 < 0 255?". É traduzido como: "(x < 128)? 0 : 255".
* Levels(il, gamma, ih, ol, oh) (dê uma olhada no filtro Levels): il de "x - il de ih - / gamma ^ oh ol - *". É traduzido como "(((x - il) / (ih - il)) ^ gamma) * (oh - ol)".

Padrões são: Yexpr = Uexpr = Vexpr = "x" (portanto, o filtro não faz nada).

MaskedMerge

MaskedMerge (base_clip, overlay_clip, mask_clip)

Este filtro aplica o clipe overlay_clip no base_clip, considerando o clipe mask_clip. Mais precisamente, com bc, oc e mc os valores de três píxeis assumidos base_clip, overlay_clip e mask_clip respectivamente, o resultado será:

v = ((256 - mc) * bc + mc * oc + 128) / 256

128 está aqui para reduzir o erro devido ao arredondamento da divisão de inteiro.

Assim, se a máscara for 255, o píxel será do overlay_clip, se a máscara for 0, o píxel será do base_clip, e será misturado entre ambos os clipes.

MotionMask

MotionMask (clip, int "thY1", int "thY2', int "thC1", int "thC2", int "thSD")

Este filtro cria uma máscara do movimento do quadro. Como os outros filtros que criam máscaras, o movimento é computado, é percentualizado por dois percentuais. Este filtro também conferirá mudança de cena e se detectar uma, não produzirá nenhuma máscara.

A detecção de mudança de cena é feita computando a soma de diferenças absolutas do quadro com o prévio. Esta soma é pesada e comparada para thSD. Se for mais que thSD, uma mudança de cena é detectada.

O movimento é computado do mesmo modo como NoMoSmooth, significando que para cada píxel, computaremos a soma absoluta das diferenças entre o píxel e seu vizinho com o do quadro prévio. O valor resultante é dividido por 9 para normalizar o resultado entre 0 e 255.

Este algoritmo só dá uma aproximação do movimento. Trabalhará bem nas extremidades de um objeto, mas não em seu interior.

Padrões são: thY1= 20, thY2 = 20, thC1 = 10, thC2 = 10 e thSD = 10.

YV12Convolution

YV12Convolution (clip, string "horizontal", string "vertical", int "total", bool "automatic", bool "saturate")

Este filtro computa o produto de convolução entre o quadro e o núcleo definido pela multiplicação do horizontal pelo vertical. Estas duas strings representam vetores. Eles têm que ter um número inteiro diferente ou números reais, separado por espaços únicos. total é um fator de normalização que é dividido pelo resultado do produto. Se automatic estiver fixado em 'true', o total é a soma dos coeficientes da matriz. Significa que desse jeito, o brilho global do quadro não é tocado. Saturate permite escolher o comportamento do filtro quando o resultado for um número negativo.

Se total não estiver definido, é fixado à soma dos coeficientes do núcleo de convolução, permitindo assim uma boa normalização para borrar / aguçar os núcleos.

Se um dos coeficientes horizontal ou vertical é um real número, todas as computações serão feitas com flutuantes, assim o filtro será mais lento.

Padrões são: horizontal = "1 1 1", vertical = "1 1 1" e automatic = false, saturate = true.

YV12Subtract

YV12Subtract (clip1, clip2, int tol, bool "widerange")

Este filtro computa a diferença entre os dois clipes. Há vários modos de computar esta diferença, dependendo dos valores de widerange e de tol.

Padrões são: tol = -1 e widerange = false.

III) Alguns usos práticos (não testados extensivamente)

Estes não produzirão os mesmos resultados exatos como nos filtros originais que eles tentam imitar, além de serem mais lentos. Apesar das numerosas funções adicionais, nenhuma idéia mais nova.

Notas: 
- Eu estou com muita preguiça para atualizar a sintaxe, especialmente considerando como mode=2 trabalha e como EdgeMask foi atualizado (agora maiores necessidades de um Binarize por exemplo)
- Alguns filtros eu descrevo como 'para criar' já existe (imagereader, níveis para segurar, ...).

1) MSharpen

# Construa EdgeMask do clip1, Binarize isso e armazene o resultado no clip3
# Aplique qualquer filtro aguçador ao clip1 e salve no clip2
...
return MaskMerge(clip1, clip2, clip3)

As extremidades aguçadas do clip2 maior que o percentual dado a Binarize será aguçado e substituirá seu valor original no clip1. Você também pode escrever um filtro com uma tabela particular (melhor se parecer com um sino), substitua Binarize por ele e tenha um aguçar pesado, dependendo do valor de extremidade: essa é a parte HiQ no SmartSmoothHiQ.

clip2 = clip1.<EdgeEnhancer>(<parameters>)
# Planos U e V não precisam filtragem, Y precisa
# EdgeMask(<...>, "roberts", Y=3, U=-128, V=-128) para mapa cinza
clip3 = clip1.EdgeMask(15, 60, "roberts", Y=3, U=1, V=1)
return MaskedMerge(clip1, clip2, clip3)

2) MSoften

Substitua EdgeEnhancer por um softener espacial (borrado em cascata? spatialsoftenMMX?) e use upper=true para selecionar píxeis planos próximos.

3) Redução de arco-íris (como descrito aqui neste artigo

Aviso, isto não é uma solução milagrosa

clip2 = clip1 soften at maximum (using deen("m2d") or edeen for instance)
# Pegue luma edgemap e aumente as extremidades com inflating
# -> áreas mais largas a serem processadas
clip3 = clip1.EdgeMask(6, "roberts", Y=3, U=1, V=1).Inflate(Y=3, U=1, V=1)
# Agora, use luma edgemask como máscara de croma
clip3 = YtoUV(clip3, clip3).ReduceBy2().Binarize(15, upper=false, Y=1, U=3, V=3)
# Nós temos que processar píxeis croma próximos das extremidades, mas manter intato o plano Y
return MaskedMerge(clip1, clip2, clip3, Y=1, U=3, V=3)

4) Super amostragem de fxtoon

Não testado

. Use tweak para escurecer o quadro ou fazer um plugin que reduz valores de Y - > clip2
. Construa máscara de extremidade, Supersample esta máscara, Binarize isto com um alto percentual (segurando melhor o som), Inflate - > clip3
. Aplique os píxeis mais escuros de clip2 que dependem dos valores de clip3

5) Warpsharp para luma escura

Não testado

. Aplique warpsharp - > clip2 (píxeis de substituição)
. Crie um filtro segurando ou um de contorno baixo luma - > clip3 (máscara)

6) pseudo-desentrelaçador (croma continua problemático)

Não testado

clip2 = clip1.SeparateFields().SelectEven().<Method>Resize(<parameters>)
clip3 = clip1.<CombingDetector>(<parâmetros>)
return MaskedMerge(clip1, clip2, clip3, Y=3, U=3, V=3)

(croma até mais problemático)

7) Sobreposições não retangulares

Na realidade, isto é controlado melhor por camada e máscara...

# Simples corte porque ImageReader precisa de um fps inteiro...
# A maioria das fontes é nativa em YUY2/YV12
clip = AviSsource("test.avi").ConvertToYV12().assumefps(fps)
# Carregue o quadro para ser sobreposto
image = ImageReader("mask.bmp", 0, clip.framecount()-1, 24, use_DevIl=false)
# Modo simples: assumir preto é transparente 
# Qualquer outra cor seria bem mais complicado *
masktemp = imageYV12.Binarize(17, upper=false, Y=3)
# Nós fixamos a máscara luma para ajustar os planos croma
mask = Mask.FitY2UV()
# Agora que nós temos a máscara que nos conta o que queremos manter...
# Substitua por imagem as partes do clipe mascarado por mask!
MaskedMerge(clip, image, mask, Y=3, U=3, V=3)
# * solução: mask = OverlayMask(image, image.BlankClip ($xxxxxx "), 1, 1)

8) Substituir fundos

Este exemplo fica claramente melhor em RGB. Para evitar problemas típicos devido a ruído ou compressão, você usa melhor versões borradas do clipe e quadro.

source = AviSource("overlay.avi").AssumeFPS(24)
# borre a fonte
clip = source.Blur(1.58).Blur(1.58).Blur(1.58)
# carregue o fundo para substituir, capturado da seqüência borrada,
bgnd = ImageReader("bgnd.ebmp", 0, clip.framecount()-1, 24, use_DevIl=false)
# carregue novo fundo
new = ImageReader("new.ebmp", 0, clip.framecount()-1, 24, use_DevIl=false)
# filtro integrado para produzir a máscara = (clip~overlay?)
mask = OverlayMask(clip, overlay.ConvertToYV12(), 10, 10)
MaskedMerge(source, new.ConvertToYV12(), mask, Y=3, U=3, V=3)

9) K-mfToon

Eu preciso incluir mais info (urls/posts original) mas por ora eu penso que o autor do mfToon, mf (mf@onthanet.net) original não reagirá muito violentamente a isto, enquanto ainda não está enviado.
A produção da função no K-mfToon.avs deve ser idêntica à do mftoon.avs original (também incluída), com duas vezes a velocidade.
As exigências são:
- Para mfToon:
. carregar os plugins "MaskTools", "warsharp", "awarsharp" 

IV) TODO - a ser feito

Nada, tudo depende de avaliação.

V) Retratação

Este plugin é liberado sob a licença GPL. Você tem que aceitar as condições em 'Copying.txt' antes de usar o plugin ou seu código fonte.

Você fica avisado também para usar isto em um estado de mente filantrópica, ou seja, "eu não manterei este segredo só para mim".

Último mas não menos, uma muito pequena parte de todos os possíveis usos de cada filtro foi testada (talvez 5% - ainda um par de horas gasto para depurar). Então, avaliação é muito_ bem vinda (o oposto, falta de avaliação - também é verdade...)

VI) Revisões

1.5.1

1.4.16

1.4.15.3

1.4.15.2

1.4.15.1

1.4.15

1.4.14.2

1.4.14.1

1.4.14

1.4.13

1.4.12

1.4.11

1.4.10

1.4.9

1.4.8

1.4.7

1.4.6

1.4.5

1.4.4

1.4.3

1.4.2

1.4.1

1.4.0

1.3.0 (private version)

1.2.0 (private version)

1.1.0 (private version)

1.0.2 (last version - public project dropped):

1.0.1: Initial release

VII) Os Ensaios do Desenvolvedor

Volte ao V) se você não está interessado em desenvolver as ferramentas disponíveis.

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' (but interface.cpp still holds the definition of the calling conventions and exported functions)
- 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 <filter's initials>_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_<filter>).

ChannelMode.cpp defines the Channel operating modes. There could be added the equivalent of a debugprintf.

This quick walkthrough won't probably help most developers, 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. <g>

$Date: 2006/03/26 18:11:53 $ Portuguese translation by RoLon