Черно-белый (не полутоновый) фильтр в svg, в идеале - snap.svg

Редактировать 1: вопрос переписан, чтобы его было проще понять.

Это действительно поразило меня.

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

вот карта, представляющая значения RGB изображения 3x3 пикселя:

|(255,255,255)|(220,220,220)|(100,100,100)|  
|(254,254,254)|(12 ,12 ,12 )|(38 ,38 ,38 )|  
|(201,201,201)|(105,105,105)|(60 ,60 ,60 )|

После применения моего фильтра я хочу получить изображение, в котором все значения больше 200 преобразуются в (255,255,255), а все значения, равные или меньшие 200, преобразуются в (0,0,0), например:

|(255,255,255)|(255,255,255)|(0  ,0  ,0  )|
|(255,255,255)|(0  ,0  ,0  )|(0  ,0  ,0  )|
|(255,255,255)|(0  ,0  ,0  )|(0  ,0  ,0  )|

Есть идеи, с чего мне начать? Это вообще возможно в SVG? Я знаю, что могу создавать матричные умножения в фильтрах в svg, но не знаю, как подойти к этой проблеме.

Спасибо!

Изменить 1: Связанный вопрос, который я разместил в математическом обмене: https://math.stackexchange.com/questions/2087482/creating-binary-matrix-for-threshold-as-a-result-of-matrix-multiplcation

Редактировать 2: извлечено из кода Snap.svg: эта матрица преобразует цветное изображение в оттенки серого:

<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>

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

Дополнительная информация. Согласно MSDN, умножение происходит следующим образом:

Resulting vector   my coveted matrix     original vector
    | R' |     | a00 a01 a02 a03 a04 |   | R |
    | G' |     | a10 a11 a12 a13 a14 |   | G |
    | B' |  =  | a20 a21 a22 a23 a24 | * | B |
    | A' |     | a30 a31 a32 a33 a34 |   | A |
    | 1  |     |  0   0   0   0   1  |   | 1 |

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


person Michael Seltenreich    schedule 07.01.2017    source источник
comment
Вы можете посмотреть фильтры svg. Трудно понять, что вам нужно. Есть ли исходное изображение или svg, которые будут стираться, что ли?   -  person Ian    schedule 07.01.2017
comment
Нет конкретного svg или изображения. В основном мне нужен (для создания) фильтр, который берет изображение и вместо него возвращает монохроматическое изображение (не в оттенках серого, а строго черно-белое). Сделайте так, чтобы все значения, превышающие x, отображались как белые, а все значения, меньшие или равные x, отображались как черные.   -  person Michael Seltenreich    schedule 07.01.2017
comment
Если вы знаете о встроенном svg-фильтре или чем-то подобном, это было бы здорово. Если нет, мне придется решить эту математическую задачу. math.stackexchange.com/questions/2087482/   -  person Michael Seltenreich    schedule 07.01.2017
comment
Я понимаю, что вы хотите преобразовать изображение RGB в черно-белое. Как вы позже сказали, вероятно, вам нужно приложение, которое применялось бы к каждому пикселю, return (0,0,0) или (255,255,255) (A означает непрозрачность). Мой вопрос: если вы получите пиксель (203,97,150), что будет на выходе?   -  person Exodd    schedule 07.01.2017
comment
У меня нет пикселя (203,97,150). В моем вводе все три поля равны, то есть всегда имеют формат (x, x, x)   -  person Michael Seltenreich    schedule 07.01.2017


Ответы (3)


Вы можете установить порог с помощью примитива фильтра <feComponentTransfer type="discrete">.

Вот пример.

<svg width="300" height="550" style="background-color: linen">
  <defs>
    <linearGradient id="gradient">
      <stop offset="0" stop-color="white"/>
      <stop offset="1" stop-color="black"/>
    </linearGradient>
    
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR type="discrete" tableValues="0 1"/>
        <feFuncG type="discrete" tableValues="0 1"/>
        <feFuncB type="discrete" tableValues="0 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <rect x="50" y="50" width="200" height="200" fill="url(#gradient)"/>
  <rect x="50" y="300" width="200" height="200" fill="url(#gradient)" filter="url(#threshold)"/>
</svg>

Принцип работы этого примитива заключается в том, что вы создаете таблицу полос для каждого цветового компонента. Однако количество полос связано со входом, а не с выходом. Подумайте об этом как о том, что входные данные разделены на несколько полос равного размера. Затем вы назначаете выходное значение каждой полосе. Итак, если вы хотите, чтобы разделение произошло на 50% (R, G или B = 0,5 (128)), вы создаете две полосы, «0 1». Значениям в первом диапазоне (0 -> 0,5) присваивается значение 0, а значениям во втором диапазоне (0,5 -> 1) присваивается значение 1.

Так, например, если вы хотите установить порог на уровне 20% (0,2 или 51), вам нужно будет создать пять полос. Значения выходной таблицы будут «0 1 1 1 1».

<svg width="300" height="550" style="background-color: linen">
  <defs>
    <linearGradient id="gradient">
      <stop offset="0" stop-color="white"/>
      <stop offset="1" stop-color="black"/>
    </linearGradient>
    
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR type="discrete" tableValues="0 1 1 1 1"/>
        <feFuncG type="discrete" tableValues="0 1 1 1 1"/>
        <feFuncB type="discrete" tableValues="0 1 1 1 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <rect x="50" y="50" width="200" height="200" fill="url(#gradient)"/>
  <rect x="50" y="300" width="200" height="200" fill="url(#gradient)" filter="url(#threshold)"/>
</svg>

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

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

var selector = document.getElementById("selector");
var funcR = document.getElementById("funcR");
var funcG = document.getElementById("funcG");
var funcB = document.getElementById("funcB");

function updateFilter() {
  // Input value
  var threshold = selector.value;
  // Create the table
  var table = "0 ".repeat(threshold) + "1 ".repeat(256-threshold);
  // Update the filter components
  funcR.setAttribute("tableValues", table);
  funcG.setAttribute("tableValues", table);
  funcB.setAttribute("tableValues", table);
}

selector.addEventListener('input', updateFilter);

// Call the filter updater once at the start to initialise the filter table
updateFilter();
<svg width="300" height="300" style="background-color: linen">
  <defs>
    <filter id="threshold" color-interpolation-filters="sRGB">
      <feComponentTransfer>
        <feFuncR id="funcR" type="discrete" tableValues="0 1 1 1"/>
        <feFuncG id="funcG" type="discrete" tableValues="0 1 1 1"/>
        <feFuncB id="funcB" type="discrete" tableValues="0 1 1 1"/>
      </feComponentTransfer>
    </filter>
  </defs>

  <image xlink:href="https://placekitten.com/g/300/300"
         x="50" y="50"width="200" height="200"
         filter="url(#threshold)"/>
</svg>

<form>
  <input id="selector" type="range" min="0" max="256"/>
</form>

person Paul LeBeau    schedule 08.01.2017
comment
IE / Edge поддерживает только до 64 значений в массиве feComponentTransfer - просто хедз-ап - person Michael Mullany; 08.01.2017
comment
Похоже, что это не так. Я немного изменил сценарий, чтобы он работал в IE, и, похоже, он отлично работает в IE11. Эдж не пробовал. jsfiddle.net/ga0xh0oj - person Paul LeBeau; 08.01.2017
comment
Раньше было так. Хорошо, если это исправят. - person Michael Mullany; 09.01.2017

Это то, для чего используется примитив feComponentTransfer для SVG с типом = "дискретный". Чтобы узнать, как использовать элемент, обратитесь к документации по веб-платформе.

person Michael Mullany    schedule 08.01.2017
comment
Ссылка вроде не работает. - person ldirer; 03.08.2018

Преобразование RGB в оттенки серого, о котором вы говорите, представляет собой линейный процесс. Это можно сделать с помощью умножения матриц. Каждое выходное значение представляет собой взвешенную сумму значений R, G и B. Если вы выберете одинаковые веса для каждого выходного канала, результирующее изображение RGB станет серым.

Бинаризация - это нелинейная операция. По этой причине невозможно изменить эту матрицу преобразования так, чтобы она отображала черно-белый цвет, а не оттенки серого.

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

person Piglet    schedule 07.01.2017