После обработки клипа обычно нужна только часть результата. Например, у вас есть клип, который назван smooth и является результатом сглаживания (размытия, например фильтром blur() ) клипа, который назовем source.
Большую часть шума удалось убрать, но также исчезли и детали. Таким образом хорошо было бы сохранить часть пикселей из исходного клипа там, где есть большая разница в цвете или яркости. Так работает фильтр MSmooth, от D. Graft. Теперь предположим, что мы заменим все те пикселы из сглаженного варианта клипа, которые хотелось бы оставить, на белые, а остальные - на черные. Это и есть так называемая маска. Набор фильтров MaskTools предназначен для создания, преобразования и использования таких масок в цветовом пространстве YV12
В этом плагине имеются следующие функции :
В дополнение к вышесказанному, все функции кроме группы FitPlane, допускают еще 3 параметра: Y, U и V. В зависимости от значений этих параметров к соответствующим цветовым плоскостям применяются следующие операции :
И наконец, некоторые функции могут обрабатывать только часть кадра:
Первоначально все это задумывалось имея в виду модульность и атомарность операций (или полезность), а вовсе не скорость. В результате получилось все раздутым и медленным. Сами решайте, действительно это так, или не совсем... Примеры из III) со встроенными фильтрами должны работать быстрее.
Binarize
(clip, int "threshold", bool "upper")
The Binarize
производит пороговое преобразование. Если upper=true, то пиксел, значение которого строго больше порога, будет установлен в 0, в противном случае - в 255. Напротив, при upper=false, то пиксел, значение которого строго больше чем параметр threshold, будет установлен в 255, в противном случае - в 0.
Значения по умолчанию такие : threshold = 20, upper = true.
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
(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
(clip, int "thY1", int "thY2", int "thC1", int "thC2", string "type")
Этот фильтр создает маску краев изображения. Алгоритм поиска краев использует свертку, результат которой сравнивается с порогами thY1 и thY2 ( яркость ); thC1 и thC2 ( цветность ). Сравнение по порогам производится следующим образом ( r - результат свертки ) :
Чтобы получилась маска с двумя уровнями, нужно установить th1=th2.
Выбор ядра свертки производится заданием параметра type :
2 | -1 |
-1 | 0 |
0 | -1 | 0 |
-1 | 0 | 1 |
0 | 1 | 0 |
-1/8 | -1/8 | -1/8 |
-1/8 | 1 | -1/8 |
-1/8 | -1/8 | -1/8 |
-1/4 | 0 | -1/4 |
0 | 1 | 0 |
-1/4 | 0 | -1/4 |
Возможны еще два значения параметра type ("cartoon" и "line" ), которые не документированы.
Значения по умолчанию : thY1 = 0, thY2 = 20, thC1 = 0, thC2 = 20 and type = "sobel".
Фильтры FitPlane
(clip, string resizer)
Семейство фильтров FitPlane
включает следующие фильтры :
- яркость в цветность: FitY2U
, FitY2V
, FitY2UV
- цветность в яркость: FitU2Y
, FitV2Y
- цветность в цветность: FitU2V
, FitV2U
При помощи этих фильтров маску, созданную для одной цветовой плоскости можно распространить на другую цветовую плоскость.
Inpand
(clip)Expand
(clip)Deflate
(clip)Inflate
(clip)
Эти фильтры позволяют увеличить или уменьшить маску. Expand
заменяет значение пиксела максимумом из значений всех окружающих пикселов. Inpand
наоборот, выбирает минимальное значение. Inflate
вычисляет среднее значение окружающих пикселов, и заменяет данный пиксел вычисленным средним только если это среднее больше исходного значения пиксела. Deflate
делает то же самое, но заменяет пиксел если среднее значение окружающих пикселов меньше исходного значения пиксела.
Изображения, полученные в результате действия фильтров Expand
и Inflate
будут иметь более высокие значения пикселов, чем исходные. Напротив, в результате действия Inpand
и Deflate
получатся изображения с более низкими значениями пикселов.
Увеличение (уменьшение) маски, произведенное Deflate
(Inflate
) выглядит более мягким, чем произведенное Expand
(Inpand
).
HysteresyMask
(mask_clip1, mask_clip2)
Этот фильтр делает одну маску из двух. Теоретически первая должна лежать внутри второй, хотя все будет работать и если это не так. Однако результаты будут менее интересными. Идея этого фильтра состоит в том, чтобы усилить те части маски, которые принадлежат обеим маскам.
Этот алгоритм интересен тем, что позволяет, например, взять одну маску, содержащую все края всех деталей изображения, плюс, естественно, шум, и вторую, с минимумом деталей и минимумом шума, и получить результат, содержащий наиболее интересные края деталей изображения, но без шума (которого не было во второй маске).
Invert
(clip, int offX, int offX, int w, int h)
Этот фильтр заменяет значение пиксела на 255 - (значение пиксела).
Binarize(upper=false) можно рассматривать как
Invert().Binarize(upper=true)
(хотя оно в действительности обрабатывается совсем не так).
Logic
(mask_clip1, mask_clip2, string "mode")
Этот фильтр создает маску, являющуюся результатом операции с двумя масками. Тип операции задается параметром mode.
Результат логической операции, произведенной над пикселами, значения которых отличаются от 0 и 255 непредсказуем.
Значение по умолчанию : mode = "and".
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
(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
(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
(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
(clip1, clip2, int tol, bool "widerange")
Этот фильтр вычисляет разность между двумя клипами. Существует несколько способов вычисления такой разности, в зависимости от значений параметров widerange и tol.
Значения по умолчанию : tol = -1, widerange = false.
В этих примерах не получатся в точности такие же результаты, как и в исходных фильтрах, работа которых имитируется, кроме того, все это работает довольно медленно. Кроме множества дополнительных функций, никаких новых идей.
Примечания:
- Я слишком ленив чтобы изменять синтаксис каждый раз, особенно в отношении функционирования mode=2, и в отношении изменений в поведении EdgeMask ( например, теперь уже не нужно использовать Binarize )
- Некоторые фильтры, описанные тут, уже существуют (imagereader, пороги для сравнения, ...).
# Строим 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)
Заменить в предыдущем примере EdgeEnhancer на какой-нибудь фильтр для размытия (несколько раз примененный Blur или spatialsoftenMMX ) и поставить upper=true чтобы выделить однородные участки.
Внимание, это тоже не супер-эффективное решение.
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)
Не проверено
. С помощью tweak затемните картинку или сделайте фильтр, который пропорционально уменьшит значения яркости. -> clip2 . Постройте маску краев изображения. Примените сглаживание (Supersampling). Примените Binarize с высоким порогом. Сделайте Inflate -> clip3 . Примените более темные пиксели из clip2 в зависимости от значений clip3
Не проверено
. Примените warpsharp -> clip2 (материал для замены) . Сделайте ограничивающий фильтр по яркости -> clip3 (mask)
Не проверено
clip2 = clip1.SeparateFields().SelectEven().<Method>Resize(<parameters>) clip3 = clip1.<CombingDetector>(<parameters>) return MaskedMerge(clip1, clip2, clip3, Y=3, U=3, V=3)
На самом деле, делается проще через 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)
Этот пример работает лучше в 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)
Я должен включить больше информации, в том числе исходные ссылки и постинги, но сейчас я думаю что автор исходного mfToon, mf ([email protected]) не сильно обидится на это.
Результат функции, содержащейся в K-mfToon.avs должен быть идентичным с результатом исходного mftoon.avs (оба включены в дистрибутив), но она вдвое быстрее.
Для использования функций:
- Из mfToon:
. включите плагины "MaskTools", "warsharp", "awarsharp"
Пока нет, все зависит от реакции общественности.
Этот плагин распространяется в соответствии с лицензией GPL. Перед использованием ознакомьтесь с ее условиями.
Также рекомендуется учитывать интересы окружающих и не думать так : "я знаю как это сделать, но никому не скажу".
И наконец : я протестировал очень небольшую часть из всех возможных применений этого пакета (думаю что 5% - несколько часов на отладку ;-). Это значит, что ваши сообщения об ошибках будут _очень_ ценны (обратное тоже верно - я про отсутствие таких сообщений...)
1.5.8 - 8 Авг 2005
1.5.7
1.5.6
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: Первоначальный релиз
Пропустите все до цифры 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])