Riprodurre un'immagine usando le linee


31

Scrivere un programma che prende in un true-immagine a colori RGB I , il numero massimo di righe per disegnare L , ed il minimo m e massimo M lunghezza di ciascuna linea. Uscita un'immagine O che aspetto il più possibile simile I e disegnata utilizzando L o meno linee rette, che hanno tutti lunghezza euclidea tra m ed M .

Ogni linea deve essere di un colore solido, avere entrambi gli endpoint nei limiti di O ed essere disegnata usando l'algoritmo di linea di Bresenham (che la maggior parte delle librerie grafiche farà già per te). Le singole linee possono avere solo 1 pixel di spessore.

Tutte le linee, anche quelle di lunghezza 0, dovrebbero occupare almeno un pixel. Le linee possono essere tracciate una sopra l'altra.

Prima di disegnare qualsiasi linea puoi inizializzare lo sfondo di O su qualsiasi colore solido (che può dipendere da I ).

Dettagli

  • O dovrebbe avere le stesse dimensioni di me .
  • L sarà sempre un numero intero non negativo. Esso può essere maggiore rispetto alla zona di I .
  • m e M sono numeri in virgola mobile non negativi con M > = m . La distanza tra due pixel è la distanza euclidea tra i loro centri. Se questa distanza è inferiore a m o maggiore di M , non è consentita una linea tra tali pixel.
  • Le linee non devono essere antialiaste.
  • Opacità e alfa non devono essere utilizzate.
  • Il tuo programma non dovrebbe richiedere più di un'ora per essere eseguito su un computer moderno decente con immagini con meno di un milione di pixel e L meno di 10.000.

Immagini di prova

Ovviamente dovresti mostrarci le tue immagini di output più accurate o interessanti (cosa che mi aspetto accadrà quando L è tra il 5% e il 25% del numero di pixel in I e m e M sono circa un decimo della dimensione diagonale).

Ecco alcune immagini di prova (fare clic per gli originali). Puoi anche pubblicare il tuo.

Monna Lisa Cascata Nighthawks Notte stellata Golden Gate Bridge

Immagini più semplici:

Scale PenroseStriscia di Mobius Curva di Hilbert

Questo è un concorso di popolarità. Vince il contributo più votato.

Gli appunti

  • Può essere utile lasciare che L sia derivato da una percentuale di pixel totali in I e da un valore assoluto. ad esempio >>> imageliner I=img.png L=50% m=10 M=20sarebbe la stessa cosa come >>> imageliner I=img.png L=32 m=10 M=20se img.pngfosse un'immagine di 8 per 8 pixel. Qualcosa di simile potrebbe essere fatto per m e M . Questo non è richiesto
  • Dal momento che le linee non possono andare fuori dai limiti, le linee più lunghe possibili saranno la lunghezza diagonale di I . Avere M superiore a questo non dovrebbe rompere nulla però.
  • Naturalmente, se m è 0 e L è maggiore o uguale al numero di pixel in I , O potrebbe essere identico a I avendo lunghezza 0 "linee" in ogni posizione di pixel. Questo comportamento non è richiesto.
  • Probabilmente, riprodurre la forma di I è più importante che riprodurre il colore. Potresti voler esaminare il rilevamento dei bordi .

Per chiarire: sono consentite librerie come SimpleCV ? E le risposte possono avere qualche scelta per I, L, m e M, compresi m = 0 e L = area?
rationalis,

@epicwisdom Sì, sono consentite tutte le librerie (tranne le cose che già svolgono questa specifica attività). Sentiti libero di usare punti chiave, rilevamento dei bordi, qualunque cosa. Il tuo algoritmo dovrebbe funzionare per qualsiasi scelta valida di I , L , m , M , includa m = 0 e L = area. (Anche se, naturalmente, il tuo algoritmo potrebbe apparire migliore per particolari accordature dei parametri.)
Calvin's Hobbies

Quindi, ad esempio, questo particolare algoritmo di libreria sarebbe considerato una risposta non valida?
rationalis,

@epicwisdom In realtà permetterò questo e altre cose simili. Sembra che ci vorrebbe ancora qualche ritocco intelligente per creare un'immagine dalle linee che ti dà.
Hobby di Calvin il

1
Le linee devono avere spessore 1?
aditsu,

Risposte:


21

C ++ - linee un po 'casuali e poi alcune

Prima alcune righe casuali

Il primo passo dell'algoritmo genera casualmente linee, assume per l'immagine di destinazione una media dei pixel lungo questa e quindi calcola se il quadrato sommato delle distanze spaziali rgb di tutti i pixel sarebbe inferiore se disegnassimo la nuova linea (e dipingilo solo, se lo è). Il nuovo colore delle linee per questo viene scelto come media saggia del canale dei valori rgb, con un'aggiunta casuale di -15 / + 15.

Cose che ho notato e influenzato l'implementazione:

  • Il colore iniziale è la media dell'immagine completa. Questo per contrastare effetti divertenti come quando lo rende bianco, e l'area è nera, quindi già qualcosa di meglio si vede una linea verde brillante, poiché è più vicina al nero di quella già bianca.
  • Prendere il colore medio puro per la linea non è così buono in quanto risulta incapace di generare riflessi venendo sovrascritto da linee successive. Fare una piccola deviazione casuale aiuta un po ', ma se guardi la notte stellata, fallisce se il contrasto locale è elevato in molti punti.

Stavo sperimentando alcuni numeri, e ho scelto, L=0.3*pixel_count(I)lasciato m=10e M=50. Esso produrrà buoni risultati a partire da circa 0.25per 0.26per il numero di linee, ma ho scelto 0.3 di avere più spazio per i dettagli precisi.

Per l'immagine della porta d'oro a grandezza naturale, ciò ha comportato la 233529 linee da dipingere (per le quali ci sono voluti 13 secondi qui). Nota che tutte le immagini qui sono visualizzate in dimensioni ridotte e devi aprirle in una nuova scheda / scaricarle per visualizzare la risoluzione completa.

Cancella l'indegno

Il passo successivo è piuttosto costoso (per le 235k linee ci sono voluti circa un'ora, ma dovrebbe essere ben entro il tempo "un'ora per 10k linee su 1 megapixel"), ma è anche un po 'sorprendente. Passo attraverso tutte le linee precedentemente dipinte e rimuovo quelle che non migliorano l'immagine. Questo mi lascia in questa corsa con solo 97347 righe che producono la seguente immagine:

Probabilmente dovrai scaricarli e confrontarli in un visualizzatore di immagini appropriato per individuare la maggior parte delle differenze.

e ricominciare da capo

Ora ho molte linee che posso dipingere di nuovo per avere di nuovo un totale di 235929. Non c'è molto da dire, quindi ecco l'immagine:

inserisci qui la descrizione dell'immagine

breve analisi

L'intera procedura sembra funzionare come un filtro di sfocatura sensibile al contrasto locale e alle dimensioni degli oggetti. Ma è anche interessante vedere dove sono disegnate le linee, quindi anche il programma le registra (per ogni linea, il colore dei pixel sarà reso un passo più bianco, alla fine il contrasto è massimizzato). Ecco quelli corrispondenti ai tre colorati sopra.

animazioni

E poiché tutti amiamo le animazioni, ecco alcune gif animate dell'intero processo per l'immagine del cancello dorato più piccolo. Si noti che esiste un significativo dithering a causa del formato gif (e poiché i creatori di formati di file di animazione a colori reali e i produttori di browser sono in guerra sui loro ego, non esiste un formato standard per le animazioni a colori reali, altrimenti avrei potuto aggiungere un .mng o simile ).

Alcuni di più

Come richiesto, ecco alcuni risultati delle altre immagini (potrebbe essere necessario aprirle di nuovo in una nuova scheda per non ridimensionarle)

Pensieri futuri

Giocare con il codice può dare alcune variazioni interessanti.

  • Scegli il colore delle linee in modo casuale anziché basarsi sulla media. Potrebbero essere necessari più di due cicli.
  • Il codice nel pastebin contiene anche qualche idea di un algoritmo genetico, ma l'immagine è probabilmente già così buona che richiederebbe troppe generazioni e questo codice è anche troppo lento per adattarsi alla regola di "un'ora".
  • Fai un altro giro di cancellazione / riverniciatura, o anche due ...
  • Modifica il limite di cancellazione delle linee (ad es. "Deve migliorare l'immagine per non N")

Il codice

Queste sono solo le due principali funzioni utili, l'intero codice non si adatta qui e può essere trovato su http://ideone.com/Z2P6Ls

Le bmpclassi rawe le raw_linefunzioni accedono rispettivamente ai pixel e alle linee in un oggetto che può essere scritto nel formato bmp (era solo un po 'di hack in giro e ho pensato che lo rende in qualche modo indipendente da qualsiasi libreria).

Il formato del file di input è 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, davvero molto bello. Hai risultati per le altre immagini di prova?
Nathaniel,

1
@ Nathaniel: ne ho aggiunti alcuni. Le immagini "semplici" non sono interessanti perché la ricreazione è quasi perfetta per i pixel.
PlasmaHH,

17

Java - linee casuali

Una soluzione molto semplice che disegna linee casuali e calcola per loro il colore medio dell'immagine sorgente. Il colore di sfondo è impostato sul colore medio sorgente.

L = 5000, m = 10, M = 50

inserisci qui la descrizione dell'immagine

L = 10000, m = 10, M = 50

inserisci qui la descrizione dell'immagine

MODIFICARE

Ho aggiunto un algoritmo genetico che gestisce una popolazione di linee. Ad ogni generazione, manteniamo solo il 50% di linee migliori, eliminiamo le altre e ne generiamo a caso nuove. I criteri per mantenere le linee sono:

  • la loro distanza dai colori dell'immagine sorgente è piccola
  • il numero di intersezioni con altre linee (minore è il migliore)
  • la loro lunghezza (più lunga è, meglio è)
  • il loro angolo con il vicino più vicino (più piccolo è il migliore)

Con mia grande delusione, l'algoritmo non sembra davvero migliorare la qualità dell'immagine :-( solo le linee stanno diventando più parallele.

Prima generazione (5000 linee)

inserisci qui la descrizione dell'immagine

Decima generazione (5000 linee)

inserisci qui la descrizione dell'immagine

Giocare con i parametri

inserisci qui la descrizione dell'immagineinserisci qui la descrizione dell'immagineinserisci qui la descrizione dell'immagine

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;
    }
}

Finalmente qualcuno ha risposto: D Mi piacerebbe vedere altri esempi.
Calvin's Hobbies,

@Calvin Certo. In questo momento sto lavorando per migliorare l'algoritmo mantenendo una popolazione di linee, eliminando ad esempio il 20% in meno e rigenerando quelli nuovi (una sorta di algoritmo genetico)
Arnaud,

Avevo in mente qualcosa del genere, ma non ho tempo di scriverlo. In attesa dell'alg genetico. risultati :)
aditsu,

Forse vuoi rimuovere il criterio dell'angolo più piccolo? Perché l'hai messo? L'immagine originale sembra buona anche se le linee non hanno un piccolo angolo di intersezione.
solo il

@justhalf Fatto. Ho aggiunto il criterio dell'angolo nel tentativo di simulare il pennello del pittore.
Arnaud,

9

C - linee rette

Un approccio di base in C che opera su file ppm. L'algoritmo tenta di posizionare le linee verticali con una lunghezza ottimale per riempire tutti i pixel. Il colore di sfondo e i colori delle linee vengono calcolati come valore medio dell'immagine originale (la mediana di ciascun canale di colore):

#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

inserisci qui la descrizione dell'immagine


6

Python 3 si basava su "linee alquanto casuali e poi alcune", oltre al rilevamento dei bordi sobel.

il codice può teoricamente funzionare per sempre (quindi posso eseguirlo durante la notte per divertimento), ma registra i suoi progressi, quindi tutte le immagini sono prese dal segno 1-10 min.

Prima legge l'immagine, quindi utilizza il rilevamento dei bordi sobel per trovare l'angolazione di tutti i bordi, per assicurarsi che le linee non passino su un altro colore. Una volta impostata una linea della lunghezza casuale all'interno (lengthmin, lengthmax), verifica quindi se contribuisce all'immagine complessiva. Mentre le linee più piccole sono migliori, ho impostato la lunghezza della linea da 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')

gotico americano

Escher

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.