Reproduksi gambar menggunakan garis

31

Tulis program yang menggunakan gambar RGB warna asli I , jumlah garis maksimum untuk menggambar L , dan minimum m dan panjang M maksimum setiap baris. Output gambar O yang terlihat sebanyak mungkin seperti saya dan diambil menggunakan L atau garis lurus sedikit, yang semuanya memiliki panjang Euclidean antara m dan M .

Setiap baris harus satu warna solid, memiliki kedua titik akhir dalam batas O , dan digambar menggunakan algoritma garis Bresenham (yang sebagian besar perpustakaan grafis akan lakukan untuk Anda). Garis individual hanya dapat setebal 1-pixel.

Semua baris, bahkan yang panjangnya 0, harus mengambil setidaknya satu piksel. Garis dapat ditarik di atas satu sama lain.

Sebelum menggambar garis apa pun, Anda dapat menginisialisasi latar belakang O ke warna solid apa pun (yang mungkin bergantung pada I ).

Detail

  • O harus memiliki dimensi yang sama dengan saya .
  • L akan selalu menjadi bilangan bulat tidak negatif. Ini mungkin lebih besar daripada daerah saya .
  • m dan M adalah angka floating point yang tidak negatif dengan M > = m . Jarak antara dua piksel adalah jarak Euclidean di antara pusat-pusatnya. Jika jarak ini kurang dari m atau lebih besar dari M , maka garis di antara piksel tersebut tidak diperbolehkan.
  • Garis-garis tidak boleh antialiased.
  • Opacity dan alpha tidak boleh digunakan.
  • Program Anda seharusnya tidak perlu lebih dari satu jam untuk berjalan di komputer modern yang layak pada gambar dengan kurang dari satu juta piksel dan L kurang dari 10.000.

Gambar Uji

Anda tentu saja harus menunjukkan kepada kami gambar-gambar keluaran Anda yang paling akurat atau menarik (yang saya perkirakan akan terjadi ketika L adalah antara 5% dan 25% dari jumlah piksel dalam I , dan m dan M sekitar sepersepuluh dari ukuran diagonal).

Berikut adalah beberapa gambar uji (klik untuk dokumen asli). Anda juga dapat memposting sendiri.

Mona lisa Air terjun Nighthawks Malam berbintang jembatan Golden Gate

Gambar yang lebih sederhana:

Tangga PenroseMobius strip Kurva Hilbert

Ini adalah kontes popularitas. Kiriman terpilih tertinggi menang.

Catatan

  • Mungkin bermanfaat untuk membiarkan L diturunkan dari persentase total piksel dalam I serta nilai absolut. misalnya >>> imageliner I=img.png L=50% m=10 M=20akan menjadi hal yang sama seperti >>> imageliner I=img.png L=32 m=10 M=20jika img.pnggambar berukuran 8 x 8 piksel. Hal serupa dapat dilakukan untuk m dan M . Ini tidak wajib.
  • Karena garis tidak bisa keluar dari batas, garis terpanjang yang mungkin akan menjadi panjang diagonal dari saya . Memiliki M lebih tinggi dari ini seharusnya tidak merusak apa pun.
  • Secara alami, jika m adalah 0 dan L lebih besar dari atau sama dengan jumlah piksel dalam I , O dapat identik dengan I dengan memiliki panjang 0 "garis" di setiap lokasi piksel. Perilaku ini tidak diperlukan.
  • Boleh dibilang, mereproduksi bentuk I lebih penting daripada mereproduksi warna. Anda mungkin ingin melihat ke deteksi tepi .
Hobi Calvin
sumber
Untuk memperjelas: Apakah perpustakaan seperti SimpleCV diizinkan? Dan jawaban mungkin punya pilihan untuk I, L, m, dan M, termasuk m = 0 dan L = area?
rationalis
@epicwisdom Ya, semua perpustakaan (kecuali hal-hal yang sudah secara khusus melakukan tugas ini) diperbolehkan Jangan ragu untuk menggunakan titik kunci, deteksi tepi, apa pun. Algoritme Anda harus bekerja untuk semua pilihan I , L , m , M , termasuk m = 0 dan L = area yang valid . (Meskipun tentu saja, algoritme Anda mungkin terlihat lebih baik untuk penyetelan parameter tertentu.)
Calvin Hobbies
Lalu, misalnya, algoritma perpustakaan khusus ini akan dianggap sebagai jawaban yang tidak valid?
rationalis
@wicwisdom Sebenarnya saya akan membiarkan itu dan hal-hal serupa lainnya. Sepertinya masih perlu beberapa penyesuaian cerdas untuk membuat gambar dari garis yang memberi Anda.
Calvin Hobi
1
Apakah garis harus memiliki ketebalan 1?
aditsu

Jawaban:

21

C ++ - garis agak acak dan kemudian beberapa

Pertama beberapa garis acak

Langkah pertama dari algoritma secara acak menghasilkan garis, mengambil gambar target rata-rata dari piksel di sepanjang ini, dan kemudian menghitung jika kuadrat persegi dari jarak ruang rgb dari semua piksel akan lebih rendah jika kita akan melukis garis baru (dan cat saja, jika ada). Warna garis baru untuk ini dipilih sebagai rata-rata saluran bijaksana dari nilai rgb, dengan tambahan acak -15 / + 15.

Hal-hal yang saya perhatikan dan memengaruhi implementasinya:

  • Warna awal adalah rata-rata dari gambar yang lengkap. Ini untuk melawan efek lucu seperti ketika membuatnya putih, dan area itu hitam, maka sudah terlihat sesuatu sebagai garis hijau terang terlihat lebih baik, karena lebih dekat ke hitam daripada yang sudah putih.
  • Mengambil warna rata-rata murni untuk garis tidak begitu baik karena ternyata tidak dapat menghasilkan highlight dengan ditimpa oleh garis-garis selanjutnya. Melakukan sedikit penyimpangan acak sedikit membantu, tetapi jika Anda melihat malam berbintang, gagal jika kontras lokal tinggi di banyak tempat.

Saya bereksperimen dengan beberapa angka, dan memilih L=0.3*pixel_count(I)dan pergi m=10dan M=50. Ini akan menghasilkan hasil yang bagus mulai sekitar 0.25untuk 0.26untuk jumlah baris, tapi aku memilih 0,3 untuk memiliki lebih banyak ruang untuk rincian akurat.

Untuk gambar gerbang emas berukuran penuh, ini menghasilkan 235929 garis untuk melukis (yang butuh 13 detik kekalahan di sini). Perhatikan bahwa semua gambar di sini ditampilkan dalam ukuran yang diperkecil dan Anda harus membukanya di tab baru / unduh untuk melihat resolusi penuh.

Hapus yang tidak layak

Langkah selanjutnya agak mahal (untuk jalur 235k butuh waktu sekitar satu jam, tetapi itu harus baik dalam "satu jam untuk jalur 10k pada persyaratan waktu 1 megapiksel"), tetapi juga agak mengejutkan. Saya melewati semua garis yang sebelumnya dicat, dan menghapus yang tidak membuat gambar lebih baik. Ini membuat saya dalam menjalankan ini dengan hanya 97347 baris yang menghasilkan gambar berikut:

Anda mungkin perlu mengunduh dan membandingkannya di penampil gambar yang sesuai untuk melihat sebagian besar perbedaan.

dan mulai lagi dari awal

Sekarang saya memiliki banyak garis yang bisa saya lukis lagi hingga total 235929 lagi. Tidak banyak bicara, jadi di sini adalah gambar:

masukkan deskripsi gambar di sini

analisis singkat

Seluruh prosedur tampaknya berfungsi seperti filter buram yang peka terhadap kontras lokal dan ukuran objek. Tetapi juga menarik untuk melihat di mana garis-garisnya dicat, sehingga program merekam ini juga (Untuk setiap baris, warna piksel akan dibuat satu langkah lebih putih, pada akhirnya kontras dimaksimalkan). Berikut adalah yang sesuai dengan tiga warna di atas.

animasi

Dan karena kita semua menyukai animasi, berikut adalah beberapa animasi gif dari seluruh proses untuk gambar golden gate yang lebih kecil. Perhatikan bahwa ada dithering yang signifikan karena format gif (dan karena pencipta format file animasi true color dan pabrikan browser sedang berperang melawan ego mereka, tidak ada format standar untuk animasi warna yang sebenarnya, kalau tidak saya bisa menambahkan .mng atau serupa ).

Lebih lagi

Seperti yang diminta, berikut adalah beberapa hasil dari gambar lain (sekali lagi Anda mungkin perlu membukanya di tab baru untuk tidak menurunkannya)

Pikiran masa depan

Bermain-main dengan kode dapat memberikan beberapa variasi yang menarik.

  • Memilih warna garis secara acak, bukan berdasarkan rata-rata. Anda mungkin membutuhkan lebih dari dua siklus.
  • Kode dalam pastebin juga mengandung beberapa gagasan tentang algoritma genetika, tetapi gambarnya mungkin sudah sangat bagus sehingga akan membutuhkan terlalu banyak generasi, dan kode ini juga terlalu lambat untuk masuk ke dalam aturan "satu jam".
  • Lakukan putaran menghapus / mengecat ulang, atau bahkan dua ...
  • Ubah batas di mana garis dapat dihapus (mis. "Harus membuat gambar setidaknya N lebih baik")

Kode

Ini hanyalah dua fungsi utama yang bermanfaat, seluruh kode tidak cocok di sini dan dapat ditemukan di http://ideone.com/Z2P6Ls

bmpKelas - kelas rawdan raw_linefungsinya masing-masing mengakses piksel dan garis dalam sebuah objek yang dapat ditulis ke format bmp (Itu hanya beberapa retasan yang tergeletak di sekitar dan saya pikir itu membuat ini agak independen dari perpustakaan mana pun).

Format file input adalah 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]);
                }
        }
}
PlasmaHH
sumber
+1, memang sangat bagus. Apakah Anda memiliki hasil untuk gambar uji lainnya?
Nathaniel
1
@Nathaniel: Saya telah menambahkan beberapa. Gambar "sederhana" tidak menarik karena rekreasinya hampir sempurna.
PlasmaHH
17

Java - garis acak

Solusi yang sangat mendasar yang menarik garis acak dan menghitung bagi mereka warna gambar sumber rata-rata. Warna latar diatur ke warna rata-rata sumber.

L = 5000, m = 10, M = 50

masukkan deskripsi gambar di sini

L = 10000, m = 10, M = 50

masukkan deskripsi gambar di sini

EDIT

Saya telah menambahkan algoritma genetika yang menangani populasi garis. Pada setiap generasi, kami hanya menyimpan 50% baris terbaik, menjatuhkan yang lain dan menghasilkan yang baru secara acak. Kriteria untuk menjaga garis adalah:

  • jarak mereka ke warna gambar sumber kecil
  • jumlah persimpangan dengan garis lain (semakin kecil semakin baik)
  • panjangnya (semakin lama semakin baik)
  • sudut mereka dengan tetangga terdekat (semakin kecil semakin baik)

Sangat mengecewakan saya, algoritme tampaknya tidak benar-benar meningkatkan kualitas gambar :-( hanya garis yang semakin paralel.

Generasi pertama (5000 baris)

masukkan deskripsi gambar di sini

Generasi kesepuluh (5000 baris)

masukkan deskripsi gambar di sini

Bermain dengan parameter

masukkan deskripsi gambar di sinimasukkan deskripsi gambar di sinimasukkan deskripsi gambar di sini

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;
    }
}
Arnaud
sumber
Akhirnya seseorang menjawab: D Saya ingin melihat lebih banyak contoh.
Hobi Calvin
@Calvin Sure. Saat ini saya sedang bekerja untuk meningkatkan algoritma dengan menjaga populasi garis, dan menghilangkan misalnya 20% lebih buruk, dan menghasilkan kembali yang baru (semacam algoritma genetika)
Arnaud
Saya memiliki sesuatu seperti itu dalam pikiran, tetapi tidak ada waktu untuk menulisnya. Menantikan alg genetik. hasil :)
aditsu
Mungkin Anda ingin menghapus kriteria sudut yang lebih kecil? Mengapa Anda mengatakannya? Gambar asli terlihat bagus meskipun garis tidak memiliki sudut persimpangan kecil.
justhalf
@justhalf Selesai. Saya telah menambahkan kriteria sudut dalam upaya untuk mensimulasikan sikat pelukis.
Arnaud
9

C - garis lurus

Pendekatan dasar dalam C yang beroperasi pada file ppm. Algoritme mencoba menempatkan garis vertikal dengan panjang garis optimal untuk mengisi semua piksel. Warna latar belakang dan warna garis dihitung sebagai nilai rata-rata dari gambar asli (median setiap saluran warna):

#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

masukkan deskripsi gambar di sini

urzeit
sumber
6

Python 3 berbasis dari "garis agak acak dan kemudian beberapa", ditambah deteksi tepi sobel.

kode secara teoritis dapat berjalan selamanya (jadi saya bisa menjalankannya semalaman untuk bersenang-senang), tetapi mencatat kemajuannya, sehingga semua gambar diambil dari tanda 1-10 menit.

Pertama kali membaca gambar, dan kemudian menggunakan deteksi tepi sobel untuk menemukan sudut semua tepi, untuk memastikan bahwa garis tidak masuk pada warna lain. Setelah satu baris dari panjang acak dalam (lengthmin, lengthmax) diatur, itu kemudian menguji untuk melihat apakah itu berkontribusi pada keseluruhan gambar. Sementara garis yang lebih kecil lebih baik, saya mengatur panjang garis dari 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')

American Gothic

Escher

Magenta
sumber