Статьи

Визуализация пигментов с помощью D3.js

«В начале 1900-х годов ученые обнаружили, что жидкость или твердое вещество, нагретые до высоких температур, будут излучать широкий спектр цветов света. Однако газ, нагретый до аналогичных температур, будет излучать свет только определенных цветов (длин волн). Причина этого наблюдения не была понята в то время ». [ 1 ]

Я задавался вопросом, может ли этот принцип распространяться на пигменты в художественных произведениях — хотя процесс генерации света отличается, различные цвета со временем выходили из моды. Современные тканевые красители сохраняют яркие, даже яркие цвета, в то время как некоторые пигменты упали в популярности из-за токсичности (свинец, кадмий) или более дешевых заменителей, например, лазурита в качестве основы для ультрамаринового синего в живописи эпохи Возрождения . Если бы вы могли определить присутствие определенных пигментов на фотографии, это могло бы обеспечить грубый механизм фильтрации изображений, чтобы помочь найти изображения с определенного периода времени, региона, художника или среды.

Чтобы проверить эту теорию, я построил небольшую визуализацию, используя D3.js для проверки. Чтобы упростить пример, я покажу только цветовое пространство RGB, чтобы вы могли увидеть, как оно работает. Поскольку это естественная сфера HTML, она упрощает код, но это ужасное цветовое пространство для просмотра реальных произведений искусства.

В тестировании я использовал несколько изображений витража и шибори (японская версия tye-die, которая часто включает приглушенные тона земли). Витражи трудно фотографировать, а большие пятна на большинстве изображений перевернуты или недодержаны. Гистограммы в цветовом пространстве HSV (hue-saturation-value) показывают, что фильтры value и saturation показывают большие пики с обоих концов.

Для Шибори у меня не было таких проблем; изображения имеют дополнительное преимущество в том, что я знаю, какие краски используются . Посмотрев некоторое время на гистограммы, я вспомнил ключевое различие между этими средами: окрашивание ткани — это, в основном, аддитивный процесс, с количеством цвета, определяемым тем, как долго ткань впитывается. Витраж (и любая обожженная керамика) может иметь разные оттенки в зависимости от способа обжига и сочетает в себе цвета стекла с несколькими слоями различных типов красок, пятен и глазурей.

В будущих исследованиях я собираюсь построить гистограммы, которые поддерживают детализацию (аналогично функции хранилища данных ). Я нашел библиотеку с площади, которая предназначена для этого в Javascript.

Я также подозреваю, что использование других цветовых пространств будет полезным. Для современных печатных СМИ CMYK может иметь смысл, или даже угадывать цвета Pantone из изображения (хотя это может потерпеть неудачу из-за смещения в фотографировании части). Цветовые системы, разработанные художниками (например, система Оствальда ), также кажутся многообещающими.

D3.js — это библиотека Javascript для построения документов, управляемых данными, обычно используемая для создания интерактивной инфографики (примеры см. В New York Times и ProPublica). Я стремился к интерактивной анимации различных гистограмм; Потребовалось время, чтобы понять, как заставить анимацию работать.

Библиотека имеет приятный функциональный стиль , который делает для чистого кода, но, как много кода для LISP, не использует синтаксис для вызова важных грамматических точек в коде. Каждый оператор представляет собой последовательность последовательных преобразований объектов data / html / svg, и вам необходимо выяснить, когда начинать новые преобразования. Анимации также происходят в фоновом потоке, поэтому вам нужно подключиться к концу каждого, чтобы вызвать следующий. Если вы скажете D3 преобразовать атрибуты объекта из одного значения в другое, это сделает хорошую работу, за исключением некоторых сбоев рендеринга с округлением.

function update(data, duration, delay, c)
{
  var hist = d3.select("body").selectAll("div");
  var hist = hist.data(data);
 
  function color(d) {
    if (c === 'r')
    return 'rgb(' + Math.round(d.x + d.dx / 2) + ',0,0)';
    if (c === 'g')
    return 'rgb(0,' + Math.round(d.x + d.dx / 2) + ',0)';
    if (c === 'b')
    return 'rgb(0,0,' + Math.round(d.x + d.dx / 2) + ')';
  }
 
  var index = 0;
  hist.transition()
      .delay(delay)
      .duration(duration)
      .style("height", function(d) {
        return Math.floor(200 - y(d.y)) + "px";
      })
      .style("background-color", color)
      .each("end", function(i){
        if (index === 0) {
          // trigger next animation here
        }
        index++;
      });
 
  hist
    .enter()
    .append("div")
    .style("height", function(d) {
      return Math.floor(200 - y(d.y)) + "px";
    })
    .attr("class", "bar")
    .style("background-color", color);
}