सी ++ - कुछ यादृच्छिक लाइनें और फिर कुछ
पहले कुछ बेतरतीब लाइनें
एल्गोरिथ्म का पहला चरण बेतरतीब ढंग से लाइनें उत्पन्न करता है, लक्ष्य छवि को पिक्सल के एक औसत के साथ लेता है, और फिर गणना करता है कि क्या सभी पिक्सेल की आरजीबी अंतरिक्ष दूरी के वर्ग को घटाया जाएगा यदि हम नई रेखा को चित्रित करेंगे (और केवल इसे पेंट करें, यदि यह है)। इसके लिए नई लाइनों का रंग आरजीबी मूल्यों के चैनल वार औसत के रूप में चुना जाता है, एक -15 / + 15 यादृच्छिक जोड़ के साथ।
चीजें जो मैंने देखीं और कार्यान्वयन को प्रभावित किया:
- प्रारंभिक रंग पूर्ण छवि का औसत है। यह सफेद बनाने के दौरान मज़ेदार प्रभावों का मुकाबला करने के लिए है, और यह क्षेत्र काला है, फिर पहले से ही उज्ज्वल हरी रेखा के रूप में कुछ बेहतर दिखाई देता है, क्योंकि यह पहले से ही सफेद की तुलना में काले रंग के करीब है।
- लाइन के लिए शुद्ध औसत रंग लेना इतना अच्छा नहीं है क्योंकि यह बाद की पंक्तियों द्वारा अधिलेखित होकर हाइलाइट उत्पन्न करने में असमर्थ हो जाता है। थोड़ा यादृच्छिक विचलन करने से थोड़ी मदद मिलती है, लेकिन अगर आप तारों वाली रात को देखते हैं, तो कई स्थानों पर स्थानीय कंट्रास्ट अधिक होने पर यह विफल हो जाता है।
मैं कुछ नंबरों के साथ प्रयोग कर रहा था, और चुना L=0.3*pixel_count(I)
और छोड़ दिया m=10
और M=50
। यह लाइनों की संख्या के लिए चारों ओर 0.25
से शुरू होने वाले अच्छे परिणामों का उत्पादन करेगा 0.26
, लेकिन मैंने सटीक विवरण के लिए अधिक स्थान के लिए 0.3 का चयन किया।
पूर्ण आकार की गोल्डन गेट छवि के लिए, इसने 235929 पंक्तियों को चित्रित किया (जिसके लिए यहां 13 सेकंड का समय लगा)। ध्यान दें कि यहां सभी चित्र कम आकार में प्रदर्शित किए गए हैं और आपको उन्हें पूर्ण समाधान देखने के लिए एक नए टैब में खोलने / डाउनलोड करने की आवश्यकता है।
अयोग्य को मिटाओ
अगला कदम बल्कि महंगा है (235k लाइनों के लिए इसे लगभग एक घंटे का समय लगता है, लेकिन "1 मेगापिक्सेल पर 10k लाइनों के लिए एक घंटे के भीतर" अच्छी तरह से होना चाहिए), लेकिन यह भी थोड़ा आश्चर्य की बात है। मैं पहले से चित्रित सभी लाइनों के माध्यम से जाता हूं, और उन लोगों को हटा देता हूं जो छवि को बेहतर नहीं बनाते हैं। यह मुझे इस रन में केवल 97347 लाइनों के साथ छोड़ता है जो निम्नलिखित छवि का निर्माण करते हैं:
संभवतः आपको सबसे अधिक अंतरों को देखने के लिए एक उपयुक्त छवि दर्शक में डाउनलोड करने और उनकी तुलना करने की आवश्यकता है।
और फिर से शुरू करें
अब मेरे पास बहुत सारी लाइनें हैं जिन्हें मैं फिर से 235929 के लिए फिर से पेंट कर सकता हूं। कहने के लिए बहुत कुछ नहीं है, इसलिए यहां छवि है:
अल्प विश्लेषण
पूरी प्रक्रिया एक धुंधला फ़िल्टर की तरह काम करती है जो स्थानीय कंट्रास्ट और ऑब्जेक्ट आकारों के प्रति संवेदनशील है। लेकिन यह देखना भी दिलचस्प है कि लाइनें कहाँ चित्रित की जाती हैं, इसलिए कार्यक्रम इन्हें भी रिकॉर्ड करता है (प्रत्येक पंक्ति के लिए, पिक्सेल रंग को एक कदम व्हिटर बनाया जाएगा, अंत में इसके विपरीत अधिकतम होता है)। यहाँ ऊपर के तीन रंगों के अनुरूप हैं।
एनिमेशन
और चूंकि हम सभी एनिमेशन पसंद करते हैं, इसलिए यहां छोटी गोल्डन गेट छवि के लिए पूरी प्रक्रिया के कुछ एनिमेटेड जिफ हैं। ध्यान दें कि जीआईएफ प्रारूप (और वास्तविक रंग एनीमेशन फ़ाइल प्रारूप के रचनाकारों के कारण महत्वपूर्ण डीटरिंग है) और ब्राउज़र निर्माता अपने एगोस पर युद्ध में हैं, असली रंग एनिमेशन के लिए कोई मानक प्रारूप नहीं है, अन्यथा मैं एक .mng या समान जोड़ सकता था। )।
कुछ और
जैसा कि अनुरोध किया गया है, यहां अन्य छवियों के कुछ परिणाम हैं (फिर से आपको उन्हें नीचे नहीं करने के लिए एक नए टैब में खोलने की आवश्यकता हो सकती है)
भविष्य के विचार
कोड के साथ चारों ओर खेलना कुछ अंतरंग बदलाव दे सकता है।
- औसत के आधार पर यादृच्छिक के बजाय लाइनों के रंग को चुनो। आपको दो से अधिक चक्रों की आवश्यकता हो सकती है।
- पास्टबिन के कोड में एक आनुवंशिक एल्गोरिथ्म का भी कुछ विचार होता है, लेकिन छवि शायद पहले से ही इतनी अच्छी है कि इसमें कई पीढ़ियों का समय लगेगा, और यह कोड "एक घंटे" नियम में फिट होने के लिए बहुत धीमा है।
- मिटा / दमन, या यहां तक कि दो का एक और दौर ...
- जहाँ रेखाएँ मिटाई जा सकती हैं, उनकी सीमा बदलें (जैसे "lest N पर छवि को बेहतर बनाना चाहिए")
कोड
ये सिर्फ दो मुख्य उपयोगी कार्य हैं, पूरा कोड यहां फिट नहीं है और http://ideone.com/Z2P6Ls पर पाया जा सकता है
bmp
वर्गों raw
और raw_line
समारोह एक वस्तु है कि प्रारूप बीएमपी के लिए लिखा जा सकता है में क्रमशः पहुँच पिक्सल और लाइनों करते हैं (यह सिर्फ कुछ हैक चारों ओर झूठ बोल रही थी और मैंने सोचा कि यह कुछ हद तक किसी भी पुस्तकालय से स्वतंत्र करता है)।
इनपुट फ़ाइल प्रारूप 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]);
}
}
}