C ++ - biraz rasgele çizgiler ve sonra bazı
İlk önce bazı rasgele çizgiler
Algoritmanın ilk adımı rasgele çizgiler oluşturur, hedef görüntü için bu boyunca piksellerin ortalamasını alır ve sonra yeni çizgiyi boyarsak tüm piksellerin rgb alan mesafelerinin toplanan karesinin daha düşük olup olmadığını hesaplar (ve sadece öyleyse boyayın). Bunun için yeni çizgi rengi, -15 / + 15 rasgele ilavesiyle, rgb değerlerinin kanal bazında ortalaması olarak seçilir.
Uygulamayı farkettiğim ve etkilediğim şeyler:
- İlk renk, tam görüntünün ortalamasıdır. Bu, beyaz olunca ve alanın siyah olması gibi komik etkilere karşı koymak, o zaman zaten parlak bir yeşil çizgi gibi bir şey, zaten beyaz olandan daha siyah olduğu için daha iyi görülüyor.
- Satır için ortalama saf rengin alınması, daha sonraki satırların üzerine yazılarak vurgu oluşturamadığı için iyi değildir. Biraz rastgele sapma yapmak biraz yardımcı olur, ancak yıldızlı geceye bakarsanız, yerel kontrastın birçok yerde yüksek olması durumunda başarısız olur.
Bazı sayıları deniyordum, ve L=0.3*pixel_count(I)
seçip bıraktım m=10
ve M=50
. Etrafında başlayan güzel sonuçlar üretecek0.25
için 0.26
hatların sayısı için, ama doğru detaylar için daha fazla oda ise 0.3 seçtik.
Tam boyutlu altın kapı görüntüsü için bu, 235929 çizginin boyanmasına neden oldu (bunun için yaklaşık 13 saniye sürdü). Buradaki tüm görüntülerin küçültülmüş boyutta görüntülendiğini ve tam çözünürlüğü görüntülemek için bunları yeni bir sekmede açmanız / indirmeniz gerektiğini unutmayın.
Değersiz silmek
Bir sonraki adım oldukça pahalıdır (235k hatlar için yaklaşık bir saat sürdü, ancak bu "1 megapiksellikteki 10k hatlar için bir saat" süresi içinde olmalı), ama biraz da şaşırtıcı. Daha önce boyanmış tüm çizgilerden geçiyorum ve görüntüyü daha iyi hale getirmeyenleri kaldırıyorum. Bu beni, bu görüntüde aşağıdaki resmi üreten sadece 97347 satır bırakıyor:
Muhtemelen çoğu farkı tespit etmek için bunları uygun bir resim görüntüleyicide indirmeniz ve karşılaştırmanız gerekir.
ve tekrar başla
Şimdi tekrar toplamda 235929 olacak şekilde boyayabileceğim çok fazla çizgim var. Söylenecek fazla bir şey yok, işte resim:
kısa analiz
Tüm prosedür yerel kontrast ve nesne boyutlarına duyarlı olan bulanık bir filtre gibi işliyor gibi görünüyor. Ancak satırların nerede boyandığını görmek de ilginç, bu nedenle program bunları da kaydeder (Her satır için, piksel rengi bir adım daha beyaz yapılır, sonuçta kontrast en üst düzeye çıkarılır). Yukarıdaki üç renkli karşılık gelenler.
animasyonlar
Ve hepimiz animasyonları sevdiğimiz için, işte küçük altın kapı görüntüsü için tüm sürecin animasyonlu gifleri. Gif formatından dolayı önemli bir titreme olduğunu unutmayın (ve gerçek renkli animasyon dosyası formatlarının ve tarayıcı üreticilerinin egolarına göre bir savaşta olduğundan, gerçek renk animasyonları için standart bir format yoktur, aksi halde .mng veya benzerini ekleyebilirdim ).
Biraz daha
İstenildiği gibi, diğer görüntülerin bazı sonuçları aşağıdadır (küçültülmemeleri için bunları yeni bir sekmede açmanız gerekebilir)
Gelecek düşünceler
Kodla uğraşmak bazı ilginç değişiklikler verebilir.
- Çizgilerin rengini ortalamaya dayanmak yerine rastgele seçin. İkiden fazla devire ihtiyacınız olabilir.
- Pastebindeki kod aynı zamanda genetik bir algoritma hakkında bazı fikirler içerir, ancak görüntü muhtemelen çok fazla nesiller alabilecek kadar iyidir ve bu kod "bir saat" kuralına uymayacak kadar yavaş da olabilir.
- Başka bir silme / boyama, hatta iki kez yapın ...
- Çizgilerin silinebileceği yerin sınırını değiştirin (örn. "Resmi en iyi N konumuna getirmeli")
Kod
Bunlar sadece iki ana kullanışlı fonksiyondur, kodun tamamı buraya uymaz ve bulunabilir. http://ideone.com/Z2P6L adresinde
bmp
Sınıflar raw
ve raw_line
fonksiyon biçimi bmp yazılabilir bir nesne sırasıyla erişim piksel ve çizgiler yapmak (Sadece biraz kesmek ortalıkta oldu ve ben herhangi bir kütüphaneden bu biraz bağımsız kılan düşünce).
Giriş dosyası formatı PPM'dir
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]);
}
}
}