Відтворити зображення за допомогою ліній


31

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

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

Усі рядки, навіть ті, що мають довжину 0, повинні мати принаймні один піксель. Лінії можуть бути намальовані одна над одною.

Перед малюванням будь-яких ліній ви можете ініціалізувати фон O до будь-якого суцільного кольору (це може залежати від I ).

Деталі

  • O повинен мати ті ж розміри, що і я .
  • L завжди буде невід’ємним цілим числом. Це може бути більше , ніж площа I .
  • m і M - негативні числа з плаваючою комою з M > = m . Відстань між двома пікселями - це евклідова відстань між їх центрами. Якщо ця відстань менше m або більша за M , то лінія між цими пікселями не допускається.
  • Лінії не повинні бути антиаліазними.
  • Непрозорість та альфа не слід використовувати.
  • Ваша програма не повинна займати більше години для роботи на гідному сучасному комп’ютері на зображеннях з меншим мільйоном пікселів і L менше 10 000.

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

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

Ось кілька тестових зображень (натисніть на оригінали). Ви також можете опублікувати своє.

Мона Ліза Водоспад Солов’ї Зоряна ніч золотоворітський міст

Простіші зображення:

Сходи перозаСмуга Мобіуса Крива Гільберта

Це конкурс на популярність. Виграє найвище подане подання.

Примітки

  • Це може бути корисно, щоб 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 "ліній" у кожному місці пікселів. Така поведінка не потрібна.
  • Можливо, відтворення форми Я важливіше, ніж відтворення кольору. Можливо, ви захочете заглянути у виявлення ребер .

Для уточнення: чи дозволені бібліотеки типу SimpleCV ? І відповіді можуть мати будь-який вибір для I, L, m і M, включаючи m = 0 і L = площа?
раціоналіс

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

Тоді, наприклад, цей конкретний алгоритм бібліотеки вважатиметься недійсною відповіддю?
раціоналіс

@epicwisdom Насправді я дозволю це та інші подібні речі. Схоже, все-таки знадобиться розумне налаштування, щоб зробити зображення поза лініями, які він вам дає.
Захоплення Кальвіна

1
Чи повинні лінії мати товщину 1?
адіцу

Відповіді:


21

C ++ - дещо випадкові лінії, а потім деякі

Спочатку кілька випадкових ліній

Перший крок алгоритму випадковим чином генерує рядки, бере для цільового зображення середнє значення пікселів вздовж цього, а потім обчислює, чи буде підсумований квадратний пробіл відстані всіх пікселів меншим, якби ми намалювали новий рядок (і фарбуйте лише, якщо є). Новий колір рядків для цього вибирається як середнє значення каналу значень 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-файлів усього процесу для менших зображень золотих воріт. Зауважте, що внаслідок gif-формату спостерігається суттєве забарвлення (а оскільки творці справжніх форматів файлів анімації кольорів та виробники браузерів ведуть війну за свої его, немає стандартного формату для справжньої кольорової анімації, інакше я міг би додати .mng або подібне ).

Трошки більше

Як вимагається, ось деякі результати інших зображень (знову, можливо, вам доведеться відкрити їх у новій вкладці, щоб їх не було зменшено в масштабі)

Майбутні думки

Погравши з кодом, можна дати кілька цікавих варіантів.

  • Виберіть колір ліній випадковим чином, а не на середньому. Можливо, вам знадобиться більше двох циклів.
  • Код у пастбіні також містить деяке уявлення про генетичний алгоритм, але зображення, ймовірно, вже настільки добре, що зайняло б занадто багато поколінь, і цей код також занадто повільний, щоб вписатись у правило «однієї години».
  • Зробіть ще один раунд стирання / перефарбовування, а то й двох ...
  • Змініть межу, коли рядки можуть бути стерті (наприклад, "має покращити зображення, щоб не було кращого N")

Кодекс

Це лише дві основні корисні функції, весь код тут не вміщується, і їх можна знайти на веб-сторінці http://ideone.com/Z2P6L

У 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: Я додав деякі. "Прості" зображення нецікаві, оскільки відпочинок майже ідеальний для пікселів.
ПлазмаХГ

17

Java - випадкові лінії

Дуже базове рішення, яке малює випадкові лінії та обчислює для них вихідний малюнок середнього кольору. Колір фону встановлюється в середньому кольорі джерела.

L = 5000, m = 10, M = 50

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

L = 10000, m = 10, M = 50

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

EDIT

Я додав генетичний алгоритм, який обробляє популяцію ліній. У кожному поколінні ми зберігаємо лише 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 Sure. Зараз я працюю над вдосконаленням алгоритму, зберігаючи сукупність ліній, і усуваю, наприклад, гірші на 20%, і відновлюю нові (якийсь генетичний алгоритм)
Арно

Я мав на увазі щось подібне, але немає часу його писати. З нетерпінням чекаю генетичної водорості. результати :)
aditsu

Можливо, ви хочете зняти критерій меншого кута? Чому ти це поставив? Оригінальне зображення добре виглядає, хоча лінії не мають невеликого кута перетину.
justhalf

@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, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 100000, m = 10, M = 50

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


6

Python 3 базується на "дещо випадкових лініях, а потім на деяких", а також виявленні крайових меж.

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

Спочатку зчитується зображення, а потім використовується розпізнавання ребер, щоб знайти кут усіх ребер, щоб переконатися, що лінії не переходять на інший колір. Після того, як встановлена ​​лінія випадкової довжини в межах (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.