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=10
dan M=50
. Ini akan menghasilkan hasil yang bagus mulai sekitar 0.25
untuk 0.26
untuk 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:
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
bmp
Kelas - kelas raw
dan raw_line
fungsinya 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]);
}
}
}