Tái tạo một hình ảnh bằng cách sử dụng các dòng


31

Viết chương trình lấy hình ảnh RGB màu thật , số dòng tối đa để vẽ L và chiều dài tối thiểu m và tối đa M của mỗi dòng. Đầu ra một hình ảnh O rằng vẻ bề ngoài càng nhiều càng tốt như tôi và được vẽ bằng L hoặc ít đường thẳng, tất cả đều có chiều dài Euclide giữa mM .

Mỗi dòng phải là một màu đơn sắc, có cả hai điểm cuối trong giới hạn của O và được vẽ bằng thuật toán dòng của Bresenham (điều mà hầu hết các thư viện đồ họa sẽ làm cho bạn). Các dòng riêng lẻ chỉ có thể dày 1 pixel.

Tất cả các dòng, ngay cả những dòng có độ dài 0, sẽ chiếm ít nhất một pixel. Các dòng có thể được vẽ trên đầu của nhau.

Trước khi vẽ bất kỳ dòng nào, bạn có thể khởi tạo nền của O thành bất kỳ màu đơn sắc nào (có thể phụ thuộc vào I ).

Chi tiết

  • O nên có cùng kích thước với tôi .
  • L sẽ luôn là một số nguyên không âm. Nó có thể lớn hơn diện tích của tôi .
  • mM là các số dấu phẩy động không âm với M > = m . Khoảng cách giữa hai pixel là khoảng cách Euclide giữa tâm của chúng. Nếu khoảng cách này nhỏ hơn m hoặc lớn hơn M , thì một đường giữa các pixel đó không được phép.
  • Các dòng không nên được khử răng cưa.
  • Opacity và alpha không nên được sử dụng.
  • Chương trình của bạn không nên mất hơn một giờ để chạy trên một máy tính hiện đại đàng hoàng trên các hình ảnh có ít hơn một triệu pixel và L dưới 10.000.

Hình ảnh thử nghiệm

Tất nhiên bạn nên cho chúng tôi xem hình ảnh đầu ra chính xác hoặc thú vị nhất của bạn (điều mà tôi mong đợi sẽ xảy ra khi L nằm trong khoảng từ 5% đến 25% số pixel trong ImM là khoảng một phần mười kích thước đường chéo).

Dưới đây là một số hình ảnh thử nghiệm (bấm vào bản gốc). Bạn cũng có thể đăng bài của riêng bạn.

nàng mô na Li Sa Thác nước Áo ngủ Đêm đầy sao Cầu Cổng Vàng

Hình ảnh đơn giản hơn:

Cầu thang PenroseDải mobius Đường cong Hilbert

Đây là một cuộc thi phổ biến. Bài viết được bình chọn cao nhất sẽ thắng.

Ghi chú

  • Có thể hữu ích khi để L được lấy từ phần trăm tổng số pixel trong I cũng như một giá trị tuyệt đối. ví dụ như >>> imageliner I=img.png L=50% m=10 M=20sẽ là điều tương tự như >>> imageliner I=img.png L=32 m=10 M=20nếu img.pnglà một điểm ảnh 8 8. Một cái gì đó tương tự có thể được thực hiện cho mM . Điều này là không bắt buộc.
  • Kể từ khi dòng không thể đi ra ngoài giới hạn, các đường dài nhất có thể sẽ là độ dài đường chéo của tôi . Có M cao hơn thế này không nên phá vỡ bất cứ điều gì mặc dù.
  • Đương nhiên, nếu m bằng 0 và L lớn hơn hoặc bằng số pixel trong I , O có thể giống hệt I bằng cách có 0 "dòng" ở mỗi vị trí pixel. Hành vi này là không bắt buộc.
  • Có thể cho rằng, tái tạo hình dạng của I quan trọng hơn tái tạo màu sắc. Bạn có thể muốn xem xét phát hiện cạnh .

Để làm rõ: Các thư viện như SimpleCV có được phép không? Và câu trả lời có thể có sự lựa chọn nào cho I, L, m và M, bao gồm m = 0 và L = diện tích?
rationalis

@epicwisdom Có, tất cả các thư viện (ngoại trừ những thứ đã thực hiện nhiệm vụ này) đều được cho phép. Hãy sử dụng các điểm chính, phát hiện cạnh, bất cứ điều gì. Thuật toán của bạn sẽ hoạt động cho mọi lựa chọn hợp lệ của I , L , m , M , bao gồm m = 0 và L = area. (Mặc dù tất nhiên, thuật toán của bạn có thể trông tốt hơn đối với các điều chỉnh cụ thể của các tham số.)
Sở thích của Calvin

Sau đó, ví dụ, thuật toán thư viện cụ thể này sẽ được coi là một câu trả lời không hợp lệ?
rationalis

@epicwisdom Thật ra tôi sẽ cho phép điều đó và những thứ tương tự khác. Có vẻ như nó vẫn cần một số điều chỉnh thông minh để tạo ra một hình ảnh từ các dòng mà nó mang lại cho bạn.
Sở thích của Calvin

1
Các dòng cần phải có độ dày 1?
aditsu

Câu trả lời:


21

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=10M=50. Nó sẽ tạo ra kết quả đẹp bắt đầu từ xung quanh 0.25để 0.26cho 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:

nhập mô tả hình ảnh ở đây

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 bmplớp rawraw_linehà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]);
                }
        }
}

+1, thực sự rất tốt đẹp. Bạn có kết quả cho các hình ảnh thử nghiệm khác?
Nathaniel

1
@Nathaniel: Tôi đã thêm một số. Các hình ảnh "đơn giản" là không mong muốn vì giải trí gần như là pixel hoàn hảo.
PlasmaHH

17

Java - dòng ngẫu nhiên

Một giải pháp rất cơ bản vẽ các đường ngẫu nhiên và tính toán cho chúng màu trung bình của hình ảnh nguồn. Màu nền được đặt thành màu trung bình nguồn.

L = 5000, m = 10, M = 50

nhập mô tả hình ảnh ở đây

L = 10000, m = 10, M = 50

nhập mô tả hình ảnh ở đây

CHỈNH SỬA

Tôi đã thêm một thuật toán di truyền xử lý một quần thể các dòng. Ở mỗi thế hệ, chúng tôi chỉ giữ lại 50% dòng tốt nhất, loại bỏ các dòng khác và tạo ngẫu nhiên những dòng mới. Các tiêu chí để giữ các dòng là:

  • khoảng cách của chúng với màu sắc hình ảnh nguồn là nhỏ
  • số lượng giao điểm với các dòng khác (càng nhỏ càng tốt)
  • chiều dài của chúng (càng dài càng tốt)
  • góc của họ với người hàng xóm gần nhất (càng nhỏ càng tốt)

Với sự thất vọng lớn của tôi, thuật toán dường như không thực sự cải thiện chất lượng hình ảnh :-( chỉ là các đường thẳng ngày càng song song.

Thế hệ thứ nhất (5000 dòng)

nhập mô tả hình ảnh ở đây

Thế hệ thứ mười (5000 dòng)

nhập mô tả hình ảnh ở đây

Chơi với thông số

nhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đâynhập mô tả hình ảnh ở đây

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;
    }
}

Cuối cùng cũng có người trả lời: Tôi rất muốn xem thêm ví dụ.
Sở thích của Calvin

@Calvin Chắc chắn rồi. Ngay bây giờ tôi đang làm việc để cải thiện thuật toán bằng cách duy trì dân số các dòng và loại bỏ ví dụ 20% tệ hơn và tạo lại các thuật toán mới (một số loại thuật toán di truyền)
Arnaud

Tôi đã có một cái gì đó như thế trong tâm trí, nhưng không có thời gian để viết nó. Mong alg di truyền. kết quả :)
aditsu

Có lẽ bạn muốn loại bỏ tiêu chí góc nhỏ hơn? Tại sao bạn đặt nó? Hình ảnh ban đầu trông đẹp mặc dù các đường không có góc giao nhau nhỏ.
vừa qua

@justhalf Xong. Tôi đã thêm tiêu chí góc trong một nỗ lực mô phỏng cọ vẽ.
Arnaud

9

C - đường thẳng

Một cách tiếp cận cơ bản trong C hoạt động trên các tệp ppm. Thuật toán cố gắng đặt các đường thẳng đứng với độ dài đường tối ưu để lấp đầy tất cả các pixel. Màu nền và màu đường được tính là giá trị trung bình của ảnh gốc (trung vị của mỗi kênh màu):

#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

nhập mô tả hình ảnh ở đây


6

Python 3 dựa trên "một số dòng ngẫu nhiên và sau đó một số", cộng với phát hiện cạnh sobel.

về mặt lý thuyết, mã có thể chạy mãi mãi (vì vậy tôi có thể chạy nó qua đêm cho vui), nhưng nó ghi lại tiến trình của nó, vì vậy tất cả các hình ảnh được chụp từ mốc 1-10 phút.

Đầu tiên, nó đọc hình ảnh và sau đó sử dụng tính năng phát hiện cạnh sobel để tìm góc của tất cả các cạnh, để đảm bảo rằng các đường không xâm lấn vào màu khác. Khi một dòng có độ dài ngẫu nhiên trong (lengthmin, lengthmax) được đặt, nó sẽ kiểm tra xem nó có đóng góp cho hình ảnh tổng thể không. Trong khi các dòng nhỏ hơn là tốt hơn, tôi đặt độ dài dòng từ 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')

Mỹ gothic

Escher

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.