Это второй пост о моем проекте по воссозданию серии портретов Эспена Клюге с использованием обработки с помощью Java. Часть 1 можно найти здесь.
До сих пор мы могли случайным образом разбрасывать точки на рисунке, используя функции loadPixels() и random(), предлагаемые обработкой. Следующим шагом было бы соединить эти случайные точки поперек рисунка, чтобы создать эффект пересекающейся линии в реальном художественном произведении.
Теперь мы должны пройти по всем точкам на рисунке, которые мы установили, и соединить это со всеми остальными. Возможно, есть более быстрый способ достичь всех точек, чем время выполнения O(n^2), но время выполнения не очень важно для моих целей здесь. Следовательно, у нас будет один цикл for, который циклически проходит через наш массив точек, и другой цикл, который создает линию между всеми предыдущими точками перед ним в списке массивов (это позволяет избежать двойного подсчета). Это означает, что мы будем делать 1 + 2 + 3 + … +n строк, что хорошо суммируется как n*(n+1)/2. Это означает O(n^2).
Мой код выглядит так:
PImage portrait; ArrayListpoints = new ArrayList(); float threshold = .2; void setup() { size(580, 625); portrait = loadImage("portrait_technique_0014.png"); loadPixels(); portrait.loadPixels(); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { Integer loc = i + j*width; float r = red(portrait.pixels[loc]); float g = green(portrait.pixels[loc]); float b = blue(portrait.pixels[loc]); if (r > 250 & g > 250 & b > 250) { } else { float val = random(0, 100); if (val < threshold) { points.add(new Point(i,j,r,g,b)); } } pixels[loc] = color(250, 250, 250); } } updatePixels(); for (int i = 0; i < points.size(); i++) { for (int z = 0; z < i; z++) { color c = points.get(i).getColor(); stroke(c); strokeWeight(.5); line(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY()); } } }
Поскольку в оригинальном художественном произведении нет непрерывного рисования, нет необходимости использовать функцию рисования. И вот результат:
Это хорошее начало, но точки слишком сильно перекрываются, скрывая фигуру. Я думал, что решением этой проблемы будет установка порога расстояния для линий, соединяющих точки. Обработка имеет хорошую функцию dist(), которой мы можем воспользоваться:
for (int i = 0; i < points.size(); i++) {
for (int z = 0; z < i; z++) {
if (dist(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY()) < 90) {
color c = points.get(i).getColor();
stroke(c);
strokeWeight(.5);
line(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY());
}
}
}
Теперь у нас будет так, что точки, расположенные слишком далеко друг от друга, не будут иметь линии, проведенной между ними.
Это выглядит значительно лучше и больше похоже на иллюстрации Клюга и больше похоже на фактическое эталонное изображение:
Удивительно, но не было необходимости привязывать линии к контурам. У нас есть хорошее представление о контурах фигуры, просто генерируя случайные точки на фигуре. Что упущено, так это интересное использование Клюгом цвета, чтобы подсказать зрителю ориентиры лица (например, ярко-красный для губ или приятный зеленый для глаз).
Прямо сейчас я просто устанавливаю цвет штриха линии на один из цветов краски. Поскольку точки окрашиваются в красный цвет в порядке вставки, мы можем контролировать, является ли цвет линии точкой, контролируемой первым циклом for или вторым. Интересно, что мы получим разные результаты, изменив цвет, который мы берем:
for (int i = 0; i < points.size(); i++) {
for (int z = 0; z < i; z++) {
if (dist(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY()) < 90) {
color a = points.get(z).getColor();
color b = points.get(i).getColor();
stroke(b);
strokeWeight(.85);
line(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY());
}
}
}
Это дает
При этом используется цвет точки во внешнем цикле for, тогда как ниже в качестве цвета используется внутренняя точка цикла for.
for (int i = 0; i < points.size(); i++) {
for (int z = 0; z < i; z++) {
if (dist(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY()) < 90) {
color a = points.get(z).getColor();
color b = points.get(i).getColor();
stroke(a);
strokeWeight(.85);
line(points.get(i).getX(), points.get(i).getY(), points.get(z).getX(), points.get(z).getY());
}
}
}
Вы можете видеть, что в этом есть больше деталей. Я не совсем понимаю, почему это но это очень классный пример того, почему генеративное искусство такое игривое. Небольшие изменения могут привести к совершенно другим результатам.
Я думаю, что основа работы Клюга здесь, поскольку цвета в “Альтернативах”, похоже, связаны с его эталонными изображениями, а не с самим кодом. Моя имитация его работы отличается тем, что диапазон цветов ограничен только коричневыми и телесными тонами, а не насыщенными зелеными и красными.
Например, я немного отредактировал исходную фотографию, которую мы использовали в качестве ссылки. Придание глазам зеленой непрозрачности, а губам более красного цвета:
И эскиз обработки делает это из этого:
Но займись этим. Самое интересное, что можно сделать, это просто начать играть с тем, что может дать вам обработка. Измените значение пороговой точки, чтобы получить больше случайных точек на рисунке, или увеличьте или уменьшите пороговое значение расстояния. Все эти значения являются переменными и дают разные результаты каждый раз, когда вы запускаете код.
Надеюсь, вам понравилось сопровождать меня. Я люблю такие вещи, и наша имитация сложилась очень быстро, как только были заложены основы первой части.
Вот репозиторий github, если вы тоже хотите его клонировать.
Оригинал: “https://dev.to/christiankastner/recreating-digital-art-part-2-4f29”