MaskTools

Общая информация

Авторы: kurosu и Manao
Версия: 1.5.8
Где взять:
Категория: Разное
Требования: Цветовое пространство YV12

Содержание

I) О MaskTools

1) Общее описание

После обработки клипа обычно нужна только часть результата. Например, у вас есть клип, который назван smooth и является результатом сглаживания (размытия, например фильтром blur() ) клипа, который назовем source.
Большую часть шума удалось убрать, но также исчезли и детали. Таким образом хорошо было бы сохранить часть пикселей из исходного клипа там, где есть большая разница в цвете или яркости. Так работает фильтр MSmooth, от D. Graft. Теперь предположим, что мы заменим все те пикселы из сглаженного варианта клипа, которые хотелось бы оставить, на белые, а остальные - на черные. Это и есть так называемая маска. Набор фильтров MaskTools предназначен для создания, преобразования и использования таких масок в цветовом пространстве YV12

2) Описание

В этом плагине имеются следующие функции :

В дополнение к вышесказанному, все функции кроме группы FitPlane, допускают еще 3 параметра: Y, U и V. В зависимости от значений этих параметров к соответствующим цветовым плоскостям применяются следующие операции :

И наконец, некоторые функции могут обрабатывать только часть кадра:

Первоначально все это задумывалось имея в виду модульность и атомарность операций (или полезность), а вовсе не скорость. В результате получилось все раздутым и медленным. Сами решайте, действительно это так, или не совсем... Примеры из III) со встроенными фильтрами должны работать быстрее.

II) Описания функций.

Binarize

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

The Binarizeпроизводит пороговое преобразование. Если upper=true, то пиксел, значение которого строго больше порога, будет установлен в 0, в противном случае - в 255. Напротив, при upper=false, то пиксел, значение которого строго больше чем параметр threshold, будет установлен в 255, в противном случае - в 0.

Значения по умолчанию такие : threshold = 20, upper = true.

CombMask

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

Этот фильтр строит маску, содержащую те места исходного клипа, где наблюдается "гребенка". Значения порогов работают так же, как и в других фильтрах : после вычисления оценки "гребенчатости", если это значение меньше thY1, то пикселу присваивается 0, если больше thY2, пикселу присваивается 255, если посередине, то значение оценки делится на 256 и присваивается пикселу.

Оценка гребенки это (upper_pixel - pixel)*(lower_pixel - pixel). Как можно видеть, результат не приводится к диапазону 0..255, (не нормализуется), поскольку если бы это делалось, обычное значение было бы 1 или 2, не более. Таким образом можно устанавливать пороги выше чем 255, хотя это вряд ли полезно.

Значения по умолчанию : thY1 = 10 и thY2 = 10 ( таким образом по умолчанию получается маска с двумя возможными значениями пикселей ).

DEdgeMask / DEdgeMask2

DEdgeMask (clip, int "thY1", int "thY2", int "thC1", int "thC2", string "matrix", float "divisor", bool "setdivisor", bool "vmode")
DEdgeMask2 (clip source, clip low_thres, clip high_thres, string"matrix", float "divisor", bool "setdivisor", bool "vmode")

Этот фильтр делает маску краев деталей изображения (или просто краев) - областей с максимальными перепадами контраста. Алгоритм использует свертку, результат которой сравнивается с порогами thY1 и thY2 ( яркость ); thC1 и thC2 ( цветность ). Сравнение по порогам производится следующим образом ( r - результат свертки ) :

Чтобы получилась маска с двумя уровнями, нужно установить th1=th2.

Выбор ядра свертки осуществляется при помощи параметра matrix. Ее значение должно быть матрицей 3 на 3 с целочисленными коэффициентами, разделенными одиночными пробелами. Так, строки "-1 -1 -1 -1 8 -1 -1 -1 -1" и "0 -1 0 -1 0 1 0 1 0" дадут операторы Лапласа и Собеля соответственно (см. "laplace" и "sobel" в описании к EdgeMask.

Так как коэффициенты должны быть целыми, параметр divisor служит для уточнения результата свертки. Этот результат будет поделен на значение параметра divisor. Если divisor не определен, то по умолчанию он берется равным сумме положительных коэффициентов матрицы, то есть производит нормализацию результата. Этот параметр может быть как целым числом, так и числом с плавающей точкой, хотя с целым числом фильтр будет работать быстрее.
Параметр setdivisor нужен только для совместимости с предыдущими версиями. Не надо его использовать.
Наконец, vmode позволяет сделать маску с центральным значением 128, а не 0. Значения по умолчанию : thY1 = 0, thY2 = 20, thC1 = 0, thC2 = 20, matrix = "-1 -1 -1 -1 8 -1 -1 -1 -1" и vmode=false.

DEdgemask2 работает в целом так же как и DEdgeMask, за исключением того, что вместо двух числовых значений порогов он требует задания двух клипов. Каждый из этих клипов содержит локальные пороги для каждого пикселя. Например, нужно сделать адаптивные пороги в зависимости от контраста каждого кадра. Локальные минимумы и максимумы можно сделать посредством inpand() and expand(). Разность делается с помощью YV12LUTxy или YV12Subtract. И вот пожалуйста, получаем клип с пороговыми значениями, зависящими от локального контраста.

EdgeMask

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

Этот фильтр создает маску краев изображения. Алгоритм поиска краев использует свертку, результат которой сравнивается с порогами thY1 и thY2 ( яркость ); thC1 и thC2 ( цветность ). Сравнение по порогам производится следующим образом ( r - результат свертки ) :

Чтобы получилась маска с двумя уровнями, нужно установить th1=th2.

Выбор ядра свертки производится заданием параметра type :

Возможны еще два значения параметра type ("cartoon" и "line" ), которые не документированы.

Значения по умолчанию : thY1 = 0, thY2 = 20, thC1 = 0, thC2 = 20 and type = "sobel".

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

Фильтры FitPlane(clip, string resizer

Семейство фильтров FitPlaneвключает следующие фильтры :
- яркость в цветность: FitY2U, FitY2V, FitY2UV
- цветность в яркость: FitU2Y, FitV2Y
- цветность в цветность: FitU2V, FitV2U

При помощи этих фильтров маску, созданную для одной цветовой плоскости можно распространить на другую цветовую плоскость.

Inpand / Expand / Deflate / Inflate

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

Эти фильтры позволяют увеличить или уменьшить маску. Expandзаменяет значение пиксела максимумом из значений всех окружающих пикселов. Inpandнаоборот, выбирает минимальное значение. Inflateвычисляет среднее значение окружающих пикселов, и заменяет данный пиксел вычисленным средним только если это среднее больше исходного значения пиксела. Deflateделает то же самое, но заменяет пиксел если среднее значение окружающих пикселов меньше исходного значения пиксела.

Изображения, полученные в результате действия фильтров Expandи Inflateбудут иметь более высокие значения пикселов, чем исходные. Напротив, в результате действия Inpandи Deflateполучатся изображения с более низкими значениями пикселов.

Увеличение (уменьшение) маски, произведенное Deflate(Inflate) выглядит более мягким, чем произведенное Expand(Inpand).

HysteresyMask

HysteresyMask (mask_clip1, mask_clip2)

Этот фильтр делает одну маску из двух. Теоретически первая должна лежать внутри второй, хотя все будет работать и если это не так. Однако результаты будут менее интересными. Идея этого фильтра состоит в том, чтобы усилить те части маски, которые принадлежат обеим маскам.

Этот алгоритм интересен тем, что позволяет, например, взять одну маску, содержащую все края всех деталей изображения, плюс, естественно, шум, и вторую, с минимумом деталей и минимумом шума, и получить результат, содержащий наиболее интересные края деталей изображения, но без шума (которого не было во второй маске).

Invert

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

Этот фильтр заменяет значение пиксела на 255 - (значение пиксела).

Binarize(upper=false) можно рассматривать как

Invert().Binarize(upper=true)

(хотя оно в действительности обрабатывается совсем не так).

Logic

Logic (mask_clip1, mask_clip2, string "mode")

Этот фильтр создает маску, являющуюся результатом операции с двумя масками. Тип операции задается параметром mode.

Результат логической операции, произведенной над пикселами, значения которых отличаются от 0 и 255 непредсказуем.

Значение по умолчанию : mode = "and".

RGBLUT / YV12LUT / YV12LUTxy / YUY2LUT

YV12LUT (clip, string "yexpr", string "uexpr", string "vexpr")
YUY2LUT (clip, string "yexpr", string"uexpr", string "vexpr")
RGBLUT (clip, string "Rexpr", string "Gexpr", string "Bexpr", string "AMPFile")
YV12LUTxy (clipx, clipy, string "yexpr", string "uexpr", string "vexpr")

Эти фильтры вычисляют заданную функцию для каждого пиксела изображения. Для того чтобы повысить скорость фильтра, все возможные результаты функции вычисляются заранее и хранятся в таблице. RGBLUTработает так же как и YV12LUT, за исключением дополнительного параметра AMPFile. Он позволяет подгрузить цветовой профиль фотошопа (color profile в англоязычной версии).

Функция задается в в виде строки с выражением в так называемой обратной польской записи. Идея состоит в том чтобы писать сначала аргументы а потом оператор, действующий на эти аргументы. Так, вместо "3 + 7" надо писать "3 7 +", а "sin(3)" становится "3 sin". Далее, "3 * 7 + 5" переходит в "3 7 * 5 +", а "(3 + 7) * 5" в "3 7 + 5 *". Это похоже на советские программируемые калькуляторы Б3-34 и подобные (прим. перев.) Главное достоинство такой записи - полное отсутствие скобок.

Вычисления производятся над вещественными числами. Положительные значения в логических выражениях означают "истина", отрицательные - "ложь". Символ "x" означает исходное значение пиксела. В функции YV12LUTxyиспользуется также обозначение "y" для соответствующего пиксела из второго клипа. Элементы выражения (числа, символы и знаки операций) должны быть разделены одиночными пробелами.

Вот некоторые функции и операторы :

Примеры :

* Постеризация картинки по порогу в 128 : "x 128 < 0 255 ?". Это переводится на язык С как : "(x < 128) ? 0 : 255".
* Levels(il, gamma, ih, ol, oh) ( см. фильтр Levels ) : "x il - ih il - / 1 gamma / ^ oh ol - *". На С это будет "(((x - il) / (ih - il)) ^ (1 / gamma)) * (oh - ol)".

Значение по умолчанию : Yexpr = Uexpr = Vexpr = "x" ( то есть попросту ничего не делать ).

MaskedMerge

MaskedMerge (base_clip, overlay_clip, mask_clip)

Этот фильтр накладывает клип, заданный как overlay_clip на клип base_clip по маске mask_clip. А именно, пусть bc, oc и mc представляют значения трех соответствующих пикселов в base_clip, overlay_clip и mask_clip, тогда результат будет такой :

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

128 присутствует здесь чтобы уменьшить ошибку, возникающую из-за целочисленного деления.

Таким образом, там где маска равна 255, результат будет взят из overlay_clip, где маска равна 0, результат будет взят из base_clip, а при промежуточных значениях маски будет одним из переходных от overlay_clip к base_clip значений.

MotionMask

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

Этот фильтр создает маску движения картинки. Как и в других фильтрах, после создания маска подвергается пороговому преобразованию. Этот фильтр также проверяет наличие смены сцены и не выдает маску если такая смена имеет место.

Определение смены сцены осуществляется посредством вычисления суммы отличий данного кадра и предыдущего. Эта сумма нормируется (averaged ?) и сравнивается с порогом thSD. Если она оказывается больше этого порога, то фиксируется факт смены сцены.

Движение анализируется подобно NoMoSmooth, то есть для каждого пиксела вычисляется сумма отличий данного пиксела и всех окружающих от соответствующих пикселов предыдущего кадра. Полученная сумма делится на 9 чтобы результат лежал между 0 и 255.

Этот алгоритм дает некую оценку движения. Он хорошо работает на краях изображения, и хуже - на однородных участках.

Значения по умолчанию : thY1= 20, thY2 = 20, thC1 = 10, thC2 = 10 и thSD = 10.

YV12Convolution

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

Эти фильтры вычисляют свертку картинки с ядром, образованным произведением строки horizontal на столбец vertical. Обе эти параметра должны содержать нечетное количество целых или вещественных чисел, разделенных одиночными пробелами. Параметр total - фактор нормализации, на который делится произведение вышеописанных векторов. Если параметр automatic равен 'true', то результат делится на сумму коэффициентов матрицы. Таким образом, общая яркость картинки не изменяется. Saturate позволяет определить поведение фильтра в случае если результат свертки меньше нуля.

Если параметр total не задан, он полагается равным сумме коэффициентов ядра свертки, таким образом достигается хорошая нормализация для сверток типа размытие / повышение резкости.

Если хотя бы один коэффициент горизонтального или вертикального векторов представлен вещественным числом, то все вычисления будут производиться с вещественными числами, в результате фильтр будет работать медленнее.

Значения по умолчанию : horizontal = "1 1 1", vertical = "1 1 1", automatic = false, saturate = true.

YV12Subtract

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

Этот фильтр вычисляет разность между двумя клипами. Существует несколько способов вычисления такой разности, в зависимости от значений параметров widerange и tol.

Значения по умолчанию : tol = -1, widerange = false.

III) Примеры практического использования (не протестированные достаточно основательно)

В этих примерах не получатся в точности такие же результаты, как и в исходных фильтрах, работа которых имитируется, кроме того, все это работает довольно медленно. Кроме множества дополнительных функций, никаких новых идей.

Примечания:
- Я слишком ленив чтобы изменять синтаксис каждый раз, особенно в отношении функционирования mode=2, и в отношении изменений в поведении EdgeMask ( например, теперь уже не нужно использовать Binarize )
- Некоторые фильтры, описанные тут, уже существуют (imagereader, пороги для сравнения, ...).

1) MSharpen

# Строим EdgeMask от clip1, делаем Binarize и называем результат clip3
# Посредством любого фильтра увеличения резкости (шарп) получаем из clip1 clip2
...
return MaskMerge(clip1, clip2, clip3)

Края деталей изображения, содержащегося в clip2, у которых яркость выше порога, заданного для Binarize заменят исходные значения в clip1. Еще можно сделать фильтр с какой-нибудь таблично заданной функцией (лучше всего подходит колоколообразная кривая), использовать его вместо Binarize, и получить шарпинг взвешенный по яркости границ. Так работает SmartSmoothHiQ.

clip2 = clip1.<EdgeEnhancer>(<parameters>)
#Каналы цветности U и V фильтровать не нужно, а Y нужно
#EdgeMask(<...>, "roberts", Y=3, U=-128, V=-128) работает с яркостями
clip3 = clip1.EdgeMask(15, 60, "roberts", Y=3, U=1, V=1)
return MaskedMerge(clip1, clip2, clip3)

2) MSoften

Заменить в предыдущем примере EdgeEnhancer на какой-нибудь фильтр для размытия (несколько раз примененный Blur или spatialsoftenMMX ) и поставить upper=true чтобы выделить однородные участки.

3) Устранение "радуги" (как описано здесь )

Внимание, это тоже не супер-эффективное решение.

clip2 = clip1 до упора размытый (например deen("m2d") или edeen)
#Берем маску краев по яркости и растягиваем края
# чтобы получились более широкие области в этих местах
clip3 = clip1.EdgeMask(6, "roberts", Y=3, U=1, V=1).Inflate(Y=3, U=1, V=1)
#Теперь возьмем эту маску и применим ее к каналам цветности
clip3 = YtoUV(clip3, clip3).ReduceBy2().Binarize(15, upper=false, Y=1, U=3, V=3)
#Нам надо обработать цветность вокруг краев, но не трогать при этом яркость
return MaskedMerge(clip1, clip2, clip3, Y=1, U=3, V=3)

4) Передискретизация в fxtoon

Не проверено

. С помощью tweak затемните картинку или сделайте фильтр, который пропорционально
уменьшит значения яркости.
-> clip2
. Постройте маску краев изображения. Примените сглаживание (Supersampling).
Примените Binarize с высоким порогом. Сделайте Inflate -> clip3
. Примените более темные пиксели из clip2 в зависимости от значений clip3

5) Warpsharp для темных клипов

Не проверено

. Примените warpsharp -> clip2 (материал для замены)
. Сделайте ограничивающий фильтр по яркости -> clip3 (mask)

6) псевдо - деинтерлейс (проблемы с цветностью все равно останутся)

Не проверено

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

7) Непрямоугольные наложения

На самом деле, делается проще через layer и mask...

#Обманем ImageReader, которому нужно целое число кадров в секунду
#В основном исходники все в YUY2/YV12
clip = AviSsource("test.avi").ConvertToYV12().assumefps(fps)
#Загрузим картинку для наложения
image = ImageReader("mask.bmp", 0, clip.framecount()-1, 24, use_DevIl=false)
#Предположим что черный должен быть прозрачным
#С другими цветами все будет сложнее (*)
masktemp = imageYV12.Binarize(17, upper=false, Y=3)
#Подгоним маску, сделанную по яркости, к каналам цветности
mask = Mask.FitY2UV()
#Теперь у нас есть маска, в которой записано, что нужно оставить...
#Заменим все что нужно по маске
MaskedMerge(clip, image, mask, Y=3, U=3, V=3)
#* можно сделать так: mask = OverlayMask(image, image.BlankClip("$xxxxxx"), 1, 1)

8) Замена фона

Этот пример работает лучше в RGB. Чтобы избежать типичных проблем с шумом или артефактами сжатия, лучше применить размытие к клипу и к фону.

source = AviSource("overlay.avi").AssumeFPS(24)
#Размытие исходного изображения
clip = source.Blur(1.58).Blur(1.58).Blur(1.58)
#Загрузим фон
bgnd = ImageReader("bgnd.ebmp", 0, clip.framecount()-1, 24,
use_DevIl=false)
#Загрузим новый фон
new = ImageReader("new.ebmp", 0, clip.framecount()-1, 24,
use_DevIl=false)
#фильтр, делающий mask = (clip~overlay?)
mask = OverlayMask(clip, overlay.ConvertToYV12(), 10, 10)
MaskedMerge(source, new.ConvertToYV12(), mask, Y=3, U=3, V=3)

9) K-mfToon

Я должен включить больше информации, в том числе исходные ссылки и постинги, но сейчас я думаю что автор исходного mfToon, mf ([email protected]) не сильно обидится на это.
Результат функции, содержащейся в K-mfToon.avs должен быть идентичным с результатом исходного mftoon.avs (оба включены в дистрибутив), но она вдвое быстрее.
Для использования функций:
- Из mfToon:
. включите плагины "MaskTools", "warsharp", "awarsharp"

IV) Планы на будущее

Пока нет, все зависит от реакции общественности.

V) Предупреждение

Этот плагин распространяется в соответствии с лицензией GPL. Перед использованием ознакомьтесь с ее условиями.
Также рекомендуется учитывать интересы окружающих и не думать так : "я знаю как это сделать, но никому не скажу".

И наконец : я протестировал очень небольшую часть из всех возможных применений этого пакета (думаю что 5% - несколько часов на отладку ;-). Это значит, что ваши сообщения об ошибках будут _очень_ ценны (обратное тоже верно - я про отсутствие таких сообщений...)

VI) Версии

1.5.8 - 8 Авг 2005

1.5.7

1.5.6

v1.5.5 - 6 Ноя 2004

v1.5.4 - 14 Окт 2004

1.5.2 - 1.5.3

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 (версия для внутреннего употребления)

1.2.0 (версия для внутреннего употребления)

1.1.0 (версия для внутреннего употребления)

1.0.2 (последняя версия - публичные релизы приостановлены):

1.0.1: Первоначальный релиз

VII) Обзор для разработчиков

Пропустите все до цифры V) если вам не интересно разбираться в исходном коде.

Исходники представляют собой простой проект VC++ 6. Каждый фильтр лежит в своем собственном каталоге, где находится h-файл, нужный для интерфейса, исходники интерфейсного класса, исходники функций-обработчиков и их h-файлы. Например EdgeMask:
- EdgeMask.h используется интерфейсом чтобы определить, как фильтр "выглядит", (но в interface.cpp находятся определения соглашений о вызовах и экспортируемых функций)
- EM_func.h описывает обработчики (у них у всех должны быть одинаковые параметры):
. Line_MMX и Line_C
. Roberts_MMX и Roberts_C
. Sobel_MMX и Sobel_C
- EM_func.cpp, как и все <что-то>_func.cpp, содержит реализацию обработчиков, и иногда их MMX версии.
- EdgeMask.cpp содержит реализацию класса; в конструкторе выбирается соответствующая функция-обработчик (MMX? C? Roberts? Line? Sobel?) и передается как указатель в GetFrame

Interface.cpp соержит экспортируемые функции и все внешние функции (AVSValue ... Create_<filter>).

ChannelMode.cpp определяет модель Channel. Туда может быть добавлен некий эквивалент функции debugprintf.

Такой краткий обзор вряд ли поможет всем разработчикам, так же как примеры V) не помогут пользователям, но это все что есть. Конечно, со временем, все улучшится, в зависимости от общего успеха идеи. У которой есть один главный недостаток - время исполнения, что скорее всего приведет к тому, что использование этого пакета будет не особенно интенсивным, если оно вообще будет.

$English Date: 2005/08/28 12:19:19 $
Русский перевод 04.10.2005 Alexander Nickolsky ([email protected])