Воспроизведите изображение, используя линии


31

Напишите программу , которая принимает в полноцветный RGB изображений I , максимальное количество линий рисовать L , а минимальные м и максимальную M длину каждой строки. Выход изображения вывод , который выглядит как можно больше , как я и обращаются с использованием L или меньше прямыми линиями, все из которых имеет длину между евклидово м и М .

Каждая линия должна быть одного сплошного цвета, иметь обе конечные точки в границах O и быть нарисована с использованием алгоритма линии Брезенхэма (что большинство графических библиотек уже сделает для вас). Отдельные линии могут быть толщиной всего 1 пиксель.

Все строки, даже те, которые имеют длину 0, должны занимать как минимум один пиксель. Линии могут быть нарисованы друг на друге.

Перед рисованием любых линий вы можете инициализировать фон O любым сплошным цветом (который может зависеть от I ).

Детали

  • O должен иметь те же размеры, что и я .
  • L всегда будет неотрицательным целым числом. Это может быть больше , чем площадь I .
  • m и M - неотрицательные числа с плавающей точкой с M > = m . Расстояние между двумя пикселями - это евклидово расстояние между их центрами. Если это расстояние меньше чем m или больше чем M , то линия между этими пикселями не допускается.
  • Линии не должны быть сглажены.
  • Непрозрачность и альфа не должны использоваться.
  • Ваша программа не должна занимать более часа для запуска на приличном современном компьютере с изображениями с размером менее миллиона пикселей и L менее 10000.

Тестовые изображения

Вы, конечно, должны показать нам ваши наиболее точные или интересные выходные изображения (которые, я ожидаю, произойдут, когда L составляет от 5% до 25% от числа пикселей в I , а m и M составляют примерно одну десятую размера диагонали).

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

Мона Лиза Водопад Nighthawks Звездная ночь Мост "Золотые ворота

Более простые изображения:

Лестница ПенроузаПолоса Мёбиуса Кривая Гильберта

Это конкурс популярности. Получивший наибольшее количество голосов выигрывает.

Заметки

  • Может быть полезно, чтобы L было получено из процента от общего числа пикселей в I, а также из абсолютного значения. Например, >>> imageliner I=img.png L=50% m=10 M=20было бы то же самое, как >>> imageliner I=img.png L=32 m=10 M=20если бы img.pngбыло изображение размером 8 на 8 пикселей. Нечто подобное можно сделать для м и м . Это не обязательно.
  • Так как линии не могут выходить за пределы, самые длинные линии возможно будут диагональная длиной I . Иметь М выше, чем это, ничего не должно сломать.
  • Естественно, если m равно 0 и L больше или равно количеству пикселей в I , O может быть идентичным I, если иметь длину «строк» ​​0 в каждом положении пикселя. Такое поведение не требуется.
  • Возможно, воспроизведение формы I является более важным, чем воспроизведение цвета. Вы можете посмотреть на обнаружение края .

Для уточнения: разрешены ли библиотеки, подобные SimpleCV ? И ответы могут иметь любой выбор для I, L, m и M, включая m = 0 и L = площадь?
rationalis

@epicwisdom Да, все библиотеки (кроме вещей, которые уже специально выполняют эту задачу) разрешены. Не стесняйтесь использовать ключевые точки, обнаружение краев, что угодно. Ваш алгоритм должен работать для любых допустимых вариантов I , L , m , M , включая m = 0 и L = площадь. (Хотя, конечно, ваш алгоритм может выглядеть лучше для определенных настроек параметров.)
Увлечения Кэлвина

Тогда, например, этот конкретный библиотечный алгоритм будет считаться неверным ответом?
rationalis

@epicwisdom На самом деле я позволю это и другие подобные вещи. Похоже, что для того, чтобы сделать изображение из линий, которые он вам дает, потребуется немного хитрой настройки.
Увлечения Кэлвина

1
Должны ли линии иметь толщину 1?
aditsu

Ответы:


21

C ++ - несколько случайных строк, а затем некоторые

Сначала несколько случайных строк

Первый шаг алгоритма случайным образом генерирует линии, берет для целевого изображения среднее значение пикселей вдоль этого и затем вычисляет, будет ли сумма квадратов пространственных расстояний rgb всех пикселей меньше, если мы нарисуем новую линию (и только покрась, если есть). Цвет новых линий для этого выбирается как среднее по каналу значений rgb, со случайным добавлением -15 / + 15.

Вещи, которые я заметил и повлиял на реализацию:

  • Начальный цвет - это среднее значение полного изображения. Это сделано для того, чтобы противостоять забавным эффектам, например, делая его белым, и область черная, тогда что-то вроде ярко-зеленой линии видно лучше, так как она ближе к черному, чем уже белая.
  • Взятие чистого среднего цвета для линии не так хорошо, поскольку оказывается невозможным генерировать блики, будучи перезаписанными более поздними линиями. Делать небольшое случайное отклонение немного помогает, но если вы посмотрите на звездную ночь, она потерпит неудачу, если локальный контраст во многих местах высок.

Я экспериментировал с некоторыми числами, выбрал L=0.3*pixel_count(I)и ушел, m=10и M=50. Это даст хорошие результаты, начиная примерно 0.25с 0.26числа строк, но я выбрал 0.3, чтобы иметь больше места для точных деталей.

Для полноразмерного изображения золотых ворот это привело к рисованию 235929 строк (для чего здесь потребовались колоссальные 13 секунд). Обратите внимание, что все изображения здесь отображаются в уменьшенном размере, и вам необходимо открыть их в новой вкладке / загрузить их, чтобы просмотреть полное разрешение.

Стереть недостойных

Следующий шаг довольно дорогой (для 235 тыс. Строк это заняло около часа, но это должно быть в пределах времени «час для 10 тыс. Строк на 1 мегапиксель»), но это также немного удивительно. Я прохожу все ранее нарисованные линии и убираю те, которые не делают изображение лучше. Это оставляет меня в этом прогоне только 97347 строк, которые производят следующее изображение:

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

и начать все сначала

Теперь у меня есть много линий, которые я могу нарисовать снова, чтобы в общей сложности снова было 235929. Не так много, чтобы сказать, так вот изображение:

введите описание изображения здесь

краткий анализ

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

анимации

И так как мы все любим анимацию, вот несколько анимационных картинок всего процесса для меньшего изображения золотых ворот. Обратите внимание, что существует существенное сглаживание из-за формата gif (и поскольку создатели форматов файлов анимации истинного цвета и производители браузеров воюют из-за своего эго, стандартного формата для анимации истинного цвета не существует, в противном случае я мог бы добавить .mng или аналогичный ).

Еще немного

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

Будущие мысли

Игра с кодом может дать некоторые интересные вариации.

  • Выбирайте цвет линий случайным образом, а не на основе среднего. Вам может понадобиться более двух циклов.
  • Код в pastebin также содержит некоторое представление о генетическом алгоритме, но изображение, вероятно, уже настолько хорошее, что потребуется слишком много поколений, и этот код также слишком медленный, чтобы соответствовать правилу «один час».
  • Сделайте еще один раунд стереть / перекрасить, или даже два ...
  • Измените предел того, где строки могут быть стерты (например, «должен сделать изображение как минимум N лучше»)

Код

Это всего лишь две основные полезные функции, весь код здесь не помещается и может быть найден на http://ideone.com/Z2P6Ls.

Эти bmpклассы rawи raw_lineфункции делают пиксели доступа и линии соответственно в объект , который может быть записан в формате BMP формат (Это был просто какой - то хак лежал вокруг , и я думал , что делает это несколько независимым от любой библиотеки).

Формат входного файла - PPM

std::pair<bmp,std::vector<line>>  paint_useful( const bmp& orig, bmp& clone, std::vector<line>& retlines, bmp& layer, const std::string& outprefix, size_t x, size_t y )
{
        const size_t pixels = (x*y);
        const size_t lines = 0.3*pixels;
//      const size_t lines = 10000;

//      const size_t start_accurate_color = lines/4;

        std::random_device rnd;

        std::uniform_int_distribution<size_t> distx(0,x-1);
        std::uniform_int_distribution<size_t> disty(0,y-1);
        std::uniform_int_distribution<size_t> col(-15,15);
        std::uniform_int_distribution<size_t> acol(0,255);

        const ssize_t m = 1*1;
        const ssize_t M = 50*50;

        retlines.reserve( lines );

        for (size_t i = retlines.size(); i < lines; ++i)
        {
                size_t x0;
                size_t x1;

                size_t y0;
                size_t y1;

                size_t dist = 0;
                do
                {
                        x0 = distx(rnd);
                        x1 = distx(rnd);

                        y0 = disty(rnd);
                        y1 = disty(rnd);

                        dist = distance(x0,x1,y0,y1);
                }
                while( dist > M || dist < m );

                std::vector<std::pair<int32_t,int32_t>> points = clone.raw_line_pixels(x0,y0,x1,y1);

                ssize_t r = 0;
                ssize_t g = 0;
                ssize_t b = 0;

                for (size_t i = 0; i < points.size(); ++i)
                {
                        r += orig.raw(points[i].first,points[i].second).r;
                        g += orig.raw(points[i].first,points[i].second).g;
                        b += orig.raw(points[i].first,points[i].second).b;
                }

                r += col(rnd);
                g += col(rnd);
                b += col(rnd);

                r /= points.size();
                g /= points.size();
                b /= points.size();

                r %= 255;
                g %= 255;
                b %= 255;

                r = std::max(ssize_t(0),r);
                g = std::max(ssize_t(0),g);
                b = std::max(ssize_t(0),b);

//              r = acol(rnd);
//              g = acol(rnd);
//              b = acol(rnd);

//              if( i > start_accurate_color )
                {
                        ssize_t dp = 0; // accumulated distance of new color to original
                        ssize_t dn = 0; // accumulated distance of current reproduced to original
                        for (size_t i = 0; i < points.size(); ++i)
                        {
                                dp += rgb_distance(
                                                                                orig.raw(points[i].first,points[i].second).r,r,
                                                                                orig.raw(points[i].first,points[i].second).g,g,
                                                                                orig.raw(points[i].first,points[i].second).b,b
                                                                        );

                                dn += rgb_distance(
                                                                                clone.raw(points[i].first,points[i].second).r,orig.raw(points[i].first,points[i].second).r,
                                                                                clone.raw(points[i].first,points[i].second).g,orig.raw(points[i].first,points[i].second).g,
                                                                                clone.raw(points[i].first,points[i].second).b,orig.raw(points[i].first,points[i].second).b
                                                                        );

                        }

                        if( dp > dn ) // the distance to original is bigger, use the new one
                        {
                                --i;
                                continue;
                        }
                        // also abandon if already too bad
//                      if( dp > 100000 )
//                      {
//                              --i;
//                              continue;
//                      }
                }

                layer.raw_line_add(x0,y0,x1,y1,{1u,1u,1u});
                clone.raw_line(x0,y0,x1,y1,{(uint32_t)r,(uint32_t)g,(uint32_t)b});
                retlines.push_back({ (int)x0,(int)y0,(int)x1,(int)y1,(int)r,(int)g,(int)b});

                static time_t last = 0;
                time_t now = time(0);
                if( i % (lines/100) == 0 )
                {
                        std::ostringstream fn;
                        fn << outprefix + "perc_" << std::setw(3) << std::setfill('0') << (i/(lines/100)) << ".bmp"; 
                        clone.write(fn.str());
                        bmp lc(layer);
                        lc.max_contrast_all();
                        lc.write(outprefix + "layer_" + fn.str());
                }

                if( (now-last) > 10 )
                {
                        last = now;
                        static int st = 0;
                        std::ostringstream fn;
                        fn << outprefix + "inter_" << std::setw(8) << std::setfill('0') << i << ".bmp";
                        clone.write(fn.str());

                        ++st;
                }
        }
        clone.write(outprefix + "clone.bmp");
        return { clone, retlines };
}


void erase_bad( std::vector<line>& lines, const bmp& orig )
{
        ssize_t current_score = evaluate(lines,orig);

        std::vector<line> newlines(lines);

        uint32_t deactivated = 0;
        std::cout << "current_score = " << current_score << "\n";
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                newlines[i].active = false;
                ssize_t score = evaluate(newlines,orig);
                if( score > current_score )
                {
                        newlines[i].active = true;
                }
                else
                {
                        current_score = score;
                        ++deactivated;
                }
                if( i % 1000 == 0 )
                {
                        std::ostringstream fn;
                        fn << "erase_" << std::setw(6) << std::setfill('0') << i << ".bmp";
                        bmp tmp(orig);
                        paint(newlines,tmp);
                        tmp.write(fn.str());
                        paint_layers(newlines,tmp);
                        tmp.max_contrast_all();
                        tmp.write("layers_" + fn.str());
                        std::cout << "\r i = " << i << std::flush;
                }
        }
        std::cout << "\n";
        std::cout << "current_score = " << current_score << "\n";
        std::cout << "deactivated = " << deactivated << "\n";


        bmp tmp(orig);

        paint(newlines,tmp);
        tmp.write("newlines.bmp");
        lines.clear();
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                if( newlines[i].is_active() )
                {
                        lines.push_back(newlines[i]);
                }
        }
}

+1, очень мило на самом деле. У вас есть результаты для других тестовых изображений?
Натаниэль

1
@Nathaniel: я добавил немного. «Простые» изображения неинтересны, потому что воспроизведение почти идеальное.
PlasmaHH

17

Java - случайные строки

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

L = 5000, м = 10, М = 50

введите описание изображения здесь

L = 10000, m = 10, M = 50

введите описание изображения здесь

РЕДАКТИРОВАТЬ

Я добавил генетический алгоритм, который обрабатывает множество строк. В каждом поколении мы сохраняем только 50% лучших линий, отбрасываем остальные и генерируем случайно новые. Критерии для сохранения линий:

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

К моему большому разочарованию, алгоритм на самом деле не улучшает качество изображения :-( только линии становятся более параллельными.

Первое поколение (5000 линий)

введите описание изображения здесь

Десятое поколение (5000 линий)

введите описание изображения здесь

Игра с параметрами

введите описание изображения здесьвведите описание изображения здесьвведите описание изображения здесь

package line;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import snake.Image;

public class Lines {

    private final static int NB_LINES = 5000;
    private final static int MIN_LENGTH = 10;
    private final static int MAX_LENGTH = 50;

    public static void main(String[] args) throws IOException {     
        BufferedImage src = ImageIO.read(Image.class.getClassLoader().getResourceAsStream("joconde.png"));
        BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);


        int [] bgColor = {0, 0, 0};
        int avgRed = 0, avgGreen = 0, avgBlue = 0, count = 0;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                int colsrc = src.getRGB(x, y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
        }

        bgColor[0] = avgBlue/count; bgColor[1] = avgGreen/count; bgColor[2] = avgRed/count;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                dest.getRaster().setPixel(x, y, bgColor);
            }
        }
        List<List<Point>> lines = new ArrayList<List<Point>>();
        Random rand = new Random();
        for (int i = 0; i < NB_LINES; i++) {
            int length = rand.nextInt(MAX_LENGTH - MIN_LENGTH) + MIN_LENGTH;
            double ang = rand.nextDouble() * Math.PI;
            int lx = (int)(Math.cos(ang) * length); // can be negative or positive
            int ly = (int)(Math.sin(ang) * length); // positive only
            int sx = rand.nextInt(dest.getWidth() -1 - Math.abs(lx));
            int sy = rand.nextInt(dest.getHeight() - 1- Math.abs(ly));
            List<Point> line;
            if (lx > 0) {
                line = line(sx, sy, sx+lx, sy+ly);
            } else {
                line = line(sx+Math.abs(lx), sy, sx, sy+ly);
            }
            lines.add(line);    
        }

        // render the picture
        int [] color = {0, 0, 0};
        for (List<Point> line : lines) {

            avgRed = 0; avgGreen = 0; avgBlue = 0;
            count = 0;
            for (Point p : line) {
                int colsrc = src.getRGB(p.x, p.y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
            avgRed /= count; avgGreen /= count; avgBlue /= count;
            color[0] = avgBlue; color[1] = avgGreen; color[2] = avgRed;
            for (Point p : line) {
                dest.getRaster().setPixel(p.x, p.y, color);
            }

        }
        ImageIO.write(dest, "png", new File("a0.png"));

    }

    private static List<Point> line(int x0, int y0, int x1, int y1) {
        List<Point> points = new ArrayList<Point>();
        int deltax = x1 - x0;
        int deltay = y1 - y0;
        int tmp;
        double error = 0;       
        double deltaerr = 0;
        if (Math.abs(deltax) >= Math.abs(deltay)) {
            if (x0 > x1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltay) / deltax); 
            int y = y0;
            for (int x = x0; x <= x1; x++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (y0 < y1) y++; else y--;
                    error -= 1.0;
                }
            }
        } else {
            if (y0 > y1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltax) / deltay);   // Assume deltay != 0 (line is not horizontal),
            int x = x0;
            for (int y = y0; y <= y1; y++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (x0 < x1) x++; else x--;
                    error -= 1.0;
                }
            }
        }
        return points;
    }
}

Наконец кто-то ответил: D Я хотел бы увидеть больше примеров.
Увлечения Кэлвина

@ Calvin Конечно. Прямо сейчас я работаю над улучшением алгоритма, сохраняя совокупность строк и устраняя, например, на 20% хуже, и заново генерируя новые (своего рода генетический алгоритм)
Арно

Я имел в виду нечто подобное, но не было времени, чтобы написать это. С нетерпением жду генетического alg. результаты :)
aditsu

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

@justhalf Готово. Я добавил критерий угла в попытке смоделировать кисть художника.
Арно

9

C - прямые

Основной подход в C, который работает с файлами ppm. Алгоритм пытается разместить вертикальные линии с оптимальной длиной линии, чтобы заполнить все пиксели. Цвет фона и цвета линий рассчитываются как среднее значение исходного изображения (медиана каждого цветового канала):

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIGN(x) ((x > 0) ? 1 : (x < 0) ? -1 : 0)
#define MIN(x, y) ((x > y) ? y : x)
#define MAX(x, y) ((x > y) ? x : y)

typedef struct {
    size_t width;
    size_t height;

    unsigned char *r;
    unsigned char *g;
    unsigned char *b;
} image;

typedef struct {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} color;

void init_image(image *data, size_t width, size_t height) {
    data->width = width;
    data->height = height;
    data->r = malloc(sizeof(data->r) * data->width * data->height);
    data->g = malloc(sizeof(data->g) * data->width * data->height);
    data->b = malloc(sizeof(data->b) * data->width * data->height);
}

#define BUFFER_LEN 1024
int load_image(const char *filename, image* data) {
    FILE *f = fopen(filename, "r");
    char buffer[BUFFER_LEN];          /* read buffer */
    size_t max_value;
    size_t i;
    fgets(buffer, BUFFER_LEN, f);
    if (strncmp(buffer, "P3", 2) != 0) {
        printf("File begins with %s instead of P3\n", buffer);
        return 0;
    }

    fscanf(f, "%u", &data->width);
    fscanf(f, "%u", &data->height);
    fscanf(f, "%u", &max_value);
    assert(max_value==255);

    init_image(data, data->width, data->height);

    for (i = 0; i < data->width * data->height; i++) {
        fscanf(f, "%hhu", &(data->r[i]));
        fscanf(f, "%hhu", &(data->g[i]));
        fscanf(f, "%hhu", &(data->b[i]));
    }
    fclose(f);

    printf("Read %zux%zu pixels from %s.\n", data->width, data->height, filename);
}

int write_image(const char *filename, image *data) {
    FILE *f = fopen(filename, "w");
    size_t i;
    fprintf(f, "P3\n%zu %zu\n255\n", data->width, data->height);
    for (i = 0; i < data->width * data->height; i++) {
        fprintf(f, "%hhu %hhu %hhu ", data->r[i], data->g[i], data->b[i]);
    }
    fclose(f);
}

unsigned char average(unsigned char *data, size_t data_len) {
    size_t i;
    size_t j;
    size_t hist[256];

    for (i = 0; i < 256; i++) hist[i] = 0;
    for (i = 0; i < data_len; i++) hist[data[i]]++;
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist[i];
        if (j >= data_len / 2) return i;
    }
    return 255;
}

void set_pixel(image *data, size_t x, size_t y, unsigned char r, unsigned char g, unsigned char b) {
    data->r[x + data->width * y] = r;
    data->g[x + data->width * y] = g;
    data->b[x + data->width * y] = b;
}

color get_pixel(image *data, size_t x, size_t y) {
    color ret;
    ret.r = data->r[x + data->width * y];
    ret.g = data->g[x + data->width * y];
    ret.b = data->b[x + data->width * y];
    return ret;
}

void fill(image *data, unsigned char r, unsigned char g, unsigned char b) {
    size_t i;
    for (i = 0; i < data->width * data->height; i++) {
        data->r[i] = r;
        data->g[i] = g;
        data->b[i] = b;
    }
}

void line(image *data, size_t x1, size_t y1, size_t x2, size_t y2, unsigned char r, unsigned char g, unsigned char b) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    set_pixel(data, x, y, r, g, b);

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        set_pixel(data, x, y, r, g, b);
    }
}

color average_line(image *data, size_t x1, size_t y1, size_t x2, size_t y2) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;
    color ret;
    color px;
    size_t i;
    size_t j;
    size_t hist_r[256];
    size_t hist_g[256];
    size_t hist_b[256];
    size_t data_len = 0;

    for (i = 0; i < 256; i++) {
        hist_r[i] = 0;
        hist_g[i] = 0;
        hist_b[i] = 0;
    }

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    px = get_pixel(data, x, y);
    hist_r[px.r]++;
    hist_g[px.g]++;
    hist_b[px.b]++;
    data_len++;

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        px = get_pixel(data, x, y);
        hist_r[px.r]++;
        hist_g[px.g]++;
        hist_b[px.b]++;
        data_len++;
    }

    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_r[i];
        if (j >= data_len / 2) {
            ret.r = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_g[i];
        if (j >= data_len / 2) {
            ret.g = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_b[i];
        if (j >= data_len / 2) {
            ret.b = i;
            break;
        }
    }
    return ret;
}


void lines(image *source, image *dest, size_t L, float m, float M) {
    size_t i, j;
    float dx;
    float mx, my;
    float mm = MAX(MIN(source->width * source->height / L, M), m);
    unsigned char av_r = average(source->r, source->width * source->height);
    unsigned char av_g = average(source->g, source->width * source->height);
    unsigned char av_b = average(source->b, source->width * source->height);
    fill(dest, av_r, av_g, av_b);
    dx = (float)source->width / L;
    mx = 0;
    my = mm / 2;
    for (i = 0; i < L; i++) {
        color avg;
        mx += dx;
        my += (source->height - mm) / 8;
        if (my + mm / 2 > source->height) {
            my = mm / 2 + ((size_t)(my + mm / 2) % (size_t)(source->height - mm));
        }
        avg = average_line(source, mx, my - mm / 2, mx, my + mm / 2);
        line(dest, mx, my - mm / 2, mx, my + mm / 2, avg.r, avg.g, avg.b);
    }
}

int main(int argc, char *argv[]) {
    image source;
    image dest;
    size_t L;
    float m;
    float M;

    load_image(argv[1], &source);
    L = atol(argv[2]);
    m = atof(argv[3]);
    M = atof(argv[4]);

    init_image(&dest, source.width, source.height);
    lines(&source, &dest, L, m, M);


    write_image(argv[5], &dest);
}

L = 5000, м = 10, М = 50

L = 5000, м = 10, М = 50

L = 5000, м = 10, М = 50

L = 5000, м = 10, М = 50

L = 100000, m = 10, M = 50

введите описание изображения здесь


6

Python 3 основан на «несколько случайных линиях, а затем и на некоторых», плюс распознавание краев.

код теоретически может работать вечно (так что я могу запустить его всю ночь для забавы), но он записывает его ход, поэтому все изображения снимаются с отметки 1-10 мин.

Сначала он читает изображение, а затем использует распознавание краев sobel, чтобы найти угол всех ребер, чтобы убедиться, что линии не пересекаются с другим цветом. Как только линия произвольной длины в пределах (lengthmin, lengthmax) установлена, она проверяет, вносит ли она вклад в общее изображение. В то время как меньшие линии лучше, я установил длину линии от 10-50.

from random import randint, uniform
import json
from PIL import Image, ImageDraw, ImageFilter
import math
k=(-1,0,1,-2,0,2,-1,0,1)
k1=(-1,-2,-1,0,0,0,1,2,1)
population=[]
lengthmin=10
lengthmax=50
number_lines=10**8
im=Image.open('0.png')
[x1,y1]=im.size
dx=0
class drawer():
    def __init__(self,genome,score,filename):
        self.genome=genome
        self.score=score
        self.filename=filename
    def initpoint(self,g1):
        g2=self.genome
        im=Image.open('0.png')
        im1=im.filter(ImageFilter.Kernel((3,3),k,1,128))
        im2=im.filter(ImageFilter.Kernel((3,3),k1,1,128))
        im1=im1.filter(ImageFilter.GaussianBlur(radius=4))
        im2=im2.filter(ImageFilter.GaussianBlur(radius=4))
        for x in range(0,number_lines):
            if(x%10**4==0):
                print(x*100/number_lines)
                self.save()
                g1.save('1.png')
            (x,y)=(randint(0,x1-1),randint(0,y1-1))
            w=im1.getpixel((x,y))[0]-128
            z=im2.getpixel((x,y))[0]-128
            w=int(w)
            z=int(z)
            W=(w**2+z**2)**0.5
            if(W!=0):
                w=(w/W)*randint(lengthmin,lengthmax)
                z=(z/W)*randint(lengthmin,lengthmax)
                (w,z)=(z,w)
                (a,b)=(x+w,y+z)
                a=int(a)
                b=int(b)
                x=int(x)
                y=int(y)
                if(a>=x1):
                    a=x1-1
                if(b>=y1):
                    b=y1-1
                if(a<0):
                    a=0
                if(b<0):
                    b=0
                if(x>=x1):
                    x=x1-1
                if(y>=y1):
                    y=y1-1
                if(x<0):
                    x=0
                if(y<0):
                    y=0
                C=[0,0,0]
                D=0
                E=0
                F=0
                G=0
                W=((x-a)**2+(y-b)**2)**0.5
                if(W!=0):
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        C[0]+=im.getpixel((c,d))[0]
                        C[1]+=im.getpixel((c,d))[1]
                        C[2]+=im.getpixel((c,d))[2]
                    C[0]/=W
                    C[1]/=W
                    C[2]/=W
                    C[0]=int(C[0])
                    C[1]=int(C[1])
                    C[2]=int(C[2])
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        E=0
                        D=0
                        D+=(g1.getpixel((c,d))[0]-im.getpixel((c,d))[0])**2
                        D+=(g1.getpixel((c,d))[1]-im.getpixel((c,d))[1])**2
                        D+=(g1.getpixel((c,d))[2]-im.getpixel((c,d))[2])**2
                        F+=D**0.5
                        E+=(im.getpixel((c,d))[0]-C[0])**2
                        E+=(im.getpixel((c,d))[1]-C[1])**2
                        E+=(im.getpixel((c,d))[2]-C[2])**2
                        G+=E**0.5
                    #print((G/W,F/W))
                    if(G<F):
                        for Z in range(0,int(W)):
                            w=(Z/W)
                            (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                            c=int(c)
                            d=int(d)
                            g1.putpixel((c,d),(int(C[0]),int(C[1]),int(C[2])))
                        g2.append((x,y,a,b,int(C[0]%256),int(C[1]%256),int(C[2]%256)))
        return(g1)
    def import_file(self):
        with open(self.filename, 'r') as infile:
            self.genome=json.loads(infile.read())
        print(len(self.genome))
    def save(self):
        with open(self.filename, 'w') as outfile:
            data = json.dumps(self.genome)
            outfile.write(data)
population.append(drawer([],0,'0.txt'))
G=0
g1=Image.new('RGB',(x1,y1),'black')
g1=population[0].initpoint(g1)
g1.save('1.png')

американская готика

Эшер

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.