C ++ - một số dòng ngẫu nhiên và sau đó một số
Đầu tiên một số dòng ngẫu nhiên
Bước đầu tiên của thuật toán tạo ngẫu nhiên các dòng, lấy hình ảnh trung bình trung bình của các pixel dọc theo đó, sau đó tính toán nếu tổng bình phương khoảng cách không gian rgb của tất cả các pixel sẽ thấp hơn nếu chúng ta vẽ đường mới (và Chỉ sơn nó, nếu có). Màu dòng mới cho màu này được chọn làm trung bình thông minh kênh của các giá trị rgb, với phép cộng ngẫu nhiên -15 / + 15.
Những điều mà tôi nhận thấy và ảnh hưởng đến việc thực hiện:
- Màu sắc ban đầu là trung bình của hình ảnh hoàn chỉnh. Điều này là để chống lại các hiệu ứng ngộ nghĩnh như khi làm cho nó có màu trắng và khu vực này có màu đen, sau đó đã có một đường màu xanh lá cây tươi sáng hơn, vì nó gần màu đen hơn màu trắng.
- Lấy màu trung bình thuần cho đường kẻ là không tốt vì hóa ra không thể tạo ra các điểm nổi bật bằng cách bị ghi đè bởi các dòng sau. Làm một chút sai lệch ngẫu nhiên sẽ giúp một chút, nhưng nếu bạn nhìn vào đêm đầy sao, sẽ thất bại nếu độ tương phản cục bộ cao ở nhiều nơi.
Tôi đã thử nghiệm với một số con số, và chọn L=0.3*pixel_count(I)
và rời đi m=10
và M=50
. Nó sẽ tạo ra kết quả đẹp bắt đầu từ xung quanh 0.25
để 0.26
cho số dòng, nhưng tôi đã chọn 0.3 có nhiều phòng để biết chi tiết chính xác.
Đối với hình ảnh cổng vàng có kích thước đầy đủ, điều này dẫn đến 235929 dòng để vẽ (mất 13 giây ở đây). Lưu ý rằng tất cả các hình ảnh ở đây được hiển thị ở kích thước giảm và bạn cần mở chúng trong một tab mới / tải xuống để xem độ phân giải đầy đủ.
Xóa bỏ những điều không xứng đáng
Bước tiếp theo khá tốn kém (đối với các dòng 235k mất khoảng một giờ, nhưng điều đó sẽ phù hợp với yêu cầu thời gian "một giờ cho 10k trên 1 megapixel"), nhưng cũng hơi ngạc nhiên. Tôi đi qua tất cả các dòng được vẽ trước đó và loại bỏ những dòng không làm cho hình ảnh tốt hơn. Điều này khiến tôi trong lần chạy này chỉ với 97347 dòng tạo ra hình ảnh sau:
Bạn có thể cần phải tải xuống và so sánh chúng trong một trình xem hình ảnh phù hợp để phát hiện ra hầu hết sự khác biệt.
và bắt đầu lại
Bây giờ tôi có rất nhiều dòng mà tôi có thể vẽ lại để có tổng cộng 235929 một lần nữa. Không có gì nhiều để nói, vì vậy đây là hình ảnh:
phân tích ngắn
Toàn bộ quy trình dường như hoạt động giống như một bộ lọc làm mờ nhạy cảm với độ tương phản và kích thước đối tượng cục bộ. Nhưng cũng rất thú vị khi xem các đường được vẽ ở đâu, vì vậy chương trình cũng ghi lại các đường này (Đối với mỗi dòng, màu pixel sẽ được thực hiện một bước trắng hơn, cuối cùng độ tương phản được tối đa hóa). Dưới đây là những cái tương ứng với ba màu ở trên.
hình ảnh động
Và vì tất cả chúng ta đều yêu thích hoạt hình, đây là một số gif hoạt hình của toàn bộ quá trình cho hình ảnh cổng vàng nhỏ hơn. Lưu ý rằng có độ hoà sắc đáng kể do định dạng gif (và vì người tạo định dạng tệp hoạt hình màu thực và nhà sản xuất trình duyệt đang chiến tranh với bản ngã của họ, không có định dạng chuẩn cho hoạt ảnh màu thực, nếu không tôi có thể đã thêm .mng hoặc tương tự ).
Một số chi tiết
Theo yêu cầu, đây là một số kết quả của các hình ảnh khác (một lần nữa bạn có thể cần phải mở chúng trong một tab mới để không bị giảm giá trị)
Những suy nghĩ trong tương lai
Chơi xung quanh với mã có thể cung cấp một số biến thể hấp dẫn.
- Chọn màu sắc của các dòng một cách ngẫu nhiên thay vì dựa trên mức trung bình. Bạn có thể cần nhiều hơn hai chu kỳ.
- Mã trong pastebin cũng chứa một số ý tưởng về thuật toán di truyền, nhưng hình ảnh có thể đã tốt đến mức phải mất quá nhiều thế hệ và mã này cũng quá chậm để phù hợp với quy tắc "một giờ".
- Thực hiện một vòng xóa / sơn lại, hoặc thậm chí hai ...
- Thay đổi giới hạn nơi các dòng có thể bị xóa (ví dụ: "phải làm cho hình ảnh ở mức thấp nhất tốt hơn")
Mật mã
Đây chỉ là hai chức năng hữu ích chính, toàn bộ mã không phù hợp ở đây và có thể được tìm thấy tại http://ideone.com/Z2P6Ls
Các bmp
lớp raw
và raw_line
hàm thực hiện truy cập các pixel và dòng tương ứng trong một đối tượng có thể được ghi thành định dạng bmp (Đó chỉ là một số hack nằm xung quanh và tôi nghĩ rằng điều này làm cho nó hơi độc lập với bất kỳ thư viện nào).
Định dạng tệp đầu vào là 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]);
}
}
}