Hình ảnh với tất cả các màu sắc


433

Tương tự như hình ảnh trên allrgb.com , tạo hình ảnh trong đó mỗi pixel là một màu duy nhất (không sử dụng màu nào hai lần và không thiếu màu).

Đưa ra một chương trình tạo ra một hình ảnh như vậy, cùng với ảnh chụp màn hình hoặc tệp của đầu ra (tải lên dưới dạng PNG).

  • Tạo hình ảnh hoàn toàn bằng thuật toán.
  • Hình ảnh phải là 256 × 128 (hoặc lưới có thể được chụp màn hình và lưu ở 256 × 128)
  • Sử dụng tất cả các màu 15 bit *
  • Không cho phép đầu vào bên ngoài (cũng không có truy vấn web, URL hoặc cơ sở dữ liệu)
  • Không cho phép hình ảnh nhúng (mã nguồn là hình ảnh tốt, ví dụ: Piet )
  • Phối màu được cho phép
  • Đây không phải là một cuộc thi mã ngắn, mặc dù nó có thể giúp bạn giành được phiếu bầu.
  • Nếu bạn thực sự thách thức, hãy thực hiện 512 × 512, 2048 × 1024 hoặc 4096 × 4096 (tăng 3 bit).

Ghi điểm là bằng phiếu bầu. Bình chọn cho những hình ảnh đẹp nhất được thực hiện bởi mã thanh lịch nhất và / hoặc thuật toán thú vị.

Thuật toán hai bước, trong đó trước tiên bạn tạo ra một hình ảnh đẹp và sau đó khớp tất cả các pixel với một trong các màu có sẵn, tất nhiên được cho phép, nhưng sẽ không mang lại cho bạn điểm thanh lịch.

* Màu 15 bit là 32768 màu có thể được tạo bằng cách trộn 32 màu đỏ, 32 màu xanh lá cây và 32 màu xanh lam, tất cả trong các bước tương đương và phạm vi bằng nhau. Ví dụ: trong hình ảnh 24 bit (8 bit trên mỗi kênh), phạm vi trên mỗi kênh là 0..255 (hoặc 0..224), vì vậy hãy chia nó thành 32 sắc thái cách đều nhau.

Để rõ ràng, mảng pixel hình ảnh phải là một hoán vị, bởi vì tất cả các hình ảnh có thể có cùng màu, chỉ ở các vị trí pixel khác nhau. Tôi sẽ đưa ra một hoán vị tầm thường ở đây, không đẹp chút nào:

Java 7

import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;

public class FifteenBitColors {
    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(256, 128, BufferedImage.TYPE_INT_RGB);

        // Generate algorithmically.
        for (int i = 0; i < 32768; i++) {
            int x = i & 255;
            int y = i / 256;
            int r = i << 3 & 0xF8;
            int g = i >> 2 & 0xF8;
            int b = i >> 7 & 0xF8;
            img.setRGB(x, y, (r << 8 | g) << 8 | b);
        }

        // Save.
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB15.png"))) {
            ImageIO.write(img, "png", out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

Người chiến thắng

Vì 7 ngày đã kết thúc, tôi tuyên bố là người chiến thắng

Tuy nhiên, không có nghĩa là, nghĩ rằng điều này đã kết thúc. Tôi, và tất cả các độc giả, luôn chào đón những thiết kế tuyệt vời hơn. Đừng ngừng tạo.

Người chiến thắng: fejesjoco với 231 phiếu bầu


8
Khi bạn nói "Phối màu được cho phép", bạn có ý gì? Đây có phải là một ngoại lệ cho quy tắc "mỗi pixel là một màu duy nhất" không? Nếu không, bạn cho phép cái gì bị cấm?
Peter Taylor

1
Nó có nghĩa là bạn có thể đặt màu sắc trong một mô hình, vì vậy khi nhìn bằng mắt, chúng hòa trộn thành một màu khác. Ví dụ: xem hình ảnh "rõ ràng tất cả RGB" trên trang allRGB và nhiều trang khác ở đó.
Mark Jeronimus

8
Tôi thực sự tìm thấy ví dụ hoán vị tầm thường của bạn là khá dễ chịu cho mắt.
Jason C

2
@ Zom-B Man, tôi thích bài này. Cảm ơn!
Jason C

7
Kết quả / câu trả lời đẹp!
EthanB

Câu trả lời:


534

C #

Tôi đặt một pixel ngẫu nhiên ở giữa, và sau đó bắt đầu đặt các pixel ngẫu nhiên vào một vùng lân cận giống với chúng nhất. Hai chế độ được hỗ trợ: với lựa chọn tối thiểu, mỗi lần chỉ có một pixel lân cận được xem xét; với lựa chọn trung bình, tất cả (1..8) được tính trung bình. Lựa chọn tối thiểu là hơi ồn ào, lựa chọn trung bình tất nhiên là mờ hơn, nhưng cả hai trông giống như bức tranh thực sự. Sau một số chỉnh sửa, đây là phiên bản hiện tại, được tối ưu hóa một chút (thậm chí nó còn sử dụng xử lý song song!):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;

class Program
{
    // algorithm settings, feel free to mess with it
    const bool AVERAGE = false;
    const int NUMCOLORS = 32;
    const int WIDTH = 256;
    const int HEIGHT = 128;
    const int STARTX = 128;
    const int STARTY = 64;

    // represent a coordinate
    struct XY
    {
        public int x, y;
        public XY(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public override int GetHashCode()
        {
            return x ^ y;
        }
        public override bool Equals(object obj)
        {
            var that = (XY)obj;
            return this.x == that.x && this.y == that.y;
        }
    }

    // gets the difference between two colors
    static int coldiff(Color c1, Color c2)
    {
        var r = c1.R - c2.R;
        var g = c1.G - c2.G;
        var b = c1.B - c2.B;
        return r * r + g * g + b * b;
    }

    // gets the neighbors (3..8) of the given coordinate
    static List<XY> getneighbors(XY xy)
    {
        var ret = new List<XY>(8);
        for (var dy = -1; dy <= 1; dy++)
        {
            if (xy.y + dy == -1 || xy.y + dy == HEIGHT)
                continue;
            for (var dx = -1; dx <= 1; dx++)
            {
                if (xy.x + dx == -1 || xy.x + dx == WIDTH)
                    continue;
                ret.Add(new XY(xy.x + dx, xy.y + dy));
            }
        }
        return ret;
    }

    // calculates how well a color fits at the given coordinates
    static int calcdiff(Color[,] pixels, XY xy, Color c)
    {
        // get the diffs for each neighbor separately
        var diffs = new List<int>(8);
        foreach (var nxy in getneighbors(xy))
        {
            var nc = pixels[nxy.y, nxy.x];
            if (!nc.IsEmpty)
                diffs.Add(coldiff(nc, c));
        }

        // average or minimum selection
        if (AVERAGE)
            return (int)diffs.Average();
        else
            return diffs.Min();
    }

    static void Main(string[] args)
    {
        // create every color once and randomize the order
        var colors = new List<Color>();
        for (var r = 0; r < NUMCOLORS; r++)
            for (var g = 0; g < NUMCOLORS; g++)
                for (var b = 0; b < NUMCOLORS; b++)
                    colors.Add(Color.FromArgb(r * 255 / (NUMCOLORS - 1), g * 255 / (NUMCOLORS - 1), b * 255 / (NUMCOLORS - 1)));
        var rnd = new Random();
        colors.Sort(new Comparison<Color>((c1, c2) => rnd.Next(3) - 1));

        // temporary place where we work (faster than all that many GetPixel calls)
        var pixels = new Color[HEIGHT, WIDTH];
        Trace.Assert(pixels.Length == colors.Count);

        // constantly changing list of available coordinates (empty pixels which have non-empty neighbors)
        var available = new HashSet<XY>();

        // calculate the checkpoints in advance
        var checkpoints = Enumerable.Range(1, 10).ToDictionary(i => i * colors.Count / 10 - 1, i => i - 1);

        // loop through all colors that we want to place
        for (var i = 0; i < colors.Count; i++)
        {
            if (i % 256 == 0)
                Console.WriteLine("{0:P}, queue size {1}", (double)i / WIDTH / HEIGHT, available.Count);

            XY bestxy;
            if (available.Count == 0)
            {
                // use the starting point
                bestxy = new XY(STARTX, STARTY);
            }
            else
            {
                // find the best place from the list of available coordinates
                // uses parallel processing, this is the most expensive step
                bestxy = available.AsParallel().OrderBy(xy => calcdiff(pixels, xy, colors[i])).First();
            }

            // put the pixel where it belongs
            Trace.Assert(pixels[bestxy.y, bestxy.x].IsEmpty);
            pixels[bestxy.y, bestxy.x] = colors[i];

            // adjust the available list
            available.Remove(bestxy);
            foreach (var nxy in getneighbors(bestxy))
                if (pixels[nxy.y, nxy.x].IsEmpty)
                    available.Add(nxy);

            // save a checkpoint
            int chkidx;
            if (checkpoints.TryGetValue(i, out chkidx))
            {
                var img = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format24bppRgb);
                for (var y = 0; y < HEIGHT; y++)
                {
                    for (var x = 0; x < WIDTH; x++)
                    {
                        img.SetPixel(x, y, pixels[y, x]);
                    }
                }
                img.Save("result" + chkidx + ".png");
            }
        }

        Trace.Assert(available.Count == 0);
    }
}

256x128 pixel, bắt đầu ở giữa, lựa chọn tối thiểu:

256x128 pixel, bắt đầu ở góc trên cùng bên trái, lựa chọn tối thiểu:

256x128 pixel, bắt đầu ở giữa, lựa chọn trung bình:

Dưới đây là hai hình động 10 khung hình cho thấy cách lựa chọn tối thiểu và trung bình hoạt động (kudos sang định dạng gif để có thể hiển thị nó chỉ với 256 màu):

Chế độ lựa chọn tối thiểu phát triển với một mặt sóng nhỏ, giống như một đốm màu, lấp đầy tất cả các pixel khi nó đi. Tuy nhiên, ở chế độ trung bình, khi hai nhánh màu khác nhau bắt đầu mọc cạnh nhau, sẽ có một khoảng cách nhỏ màu đen vì không có gì đủ gần với hai màu khác nhau. Do những khoảng trống đó, mặt sóng sẽ có độ lớn lớn hơn, do đó thuật toán sẽ chậm hơn rất nhiều. Nhưng nó đẹp bởi vì nó trông giống như một san hô đang phát triển. Nếu tôi bỏ chế độ trung bình, nó có thể được thực hiện nhanh hơn một chút vì mỗi màu mới được so sánh với mỗi pixel hiện tại khoảng 2-3 lần. Tôi thấy không có cách nào khác để tối ưu hóa nó, tôi nghĩ nó đủ tốt như vậy.

Và điểm thu hút lớn, đây là kết xuất 512x512 pixel, bắt đầu giữa, lựa chọn tối thiểu:

Tôi không thể ngừng chơi với điều này! Trong đoạn mã trên, các màu được sắp xếp ngẫu nhiên. Nếu chúng ta không sắp xếp tất cả hoặc sắp xếp theo màu sắc ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue())), chúng ta sẽ nhận được những thứ này, tương ứng (cả lựa chọn bắt đầu giữa và lựa chọn tối thiểu):

Một kết hợp khác, trong đó dạng san hô được giữ cho đến hết: màu sắc được sắp xếp với lựa chọn trung bình, với hình động 30 khung hình:

CẬP NHẬT: NÓ S READN SÀNG !!!

Bạn muốn hi-res, tôi muốn hi-res, bạn thiếu kiên nhẫn, tôi hầu như không ngủ. Bây giờ tôi rất vui mừng thông báo rằng cuối cùng nó đã sẵn sàng, chất lượng sản xuất. Và tôi đang phát hành nó với một tiếng nổ lớn, một video YouTube 1080p tuyệt vời! Bấm vào đây để xem video , hãy làm cho nó lan truyền để quảng bá phong cách đam mê. Tôi cũng đang đăng nội dung trên blog của mình tại http://joco.name/ , sẽ có một bài viết kỹ thuật về tất cả các chi tiết thú vị, tối ưu hóa, cách tôi tạo video, v.v ... Và cuối cùng, tôi đang chia sẻ nguồn mã theo GPL. Nó trở nên rất lớn vì vậy một máy chủ lưu trữ thích hợp là nơi tốt nhất cho việc này, tôi sẽ không chỉnh sửa phần trên của câu trả lời của mình nữa. Hãy chắc chắn để biên dịch trong chế độ phát hành! Chương trình có quy mô tốt cho nhiều lõi CPU. Kết xuất 4Kx4K cần khoảng 2-3 GB RAM.

Bây giờ tôi có thể hiển thị hình ảnh lớn trong 5-10 giờ. Tôi đã có một số kết xuất 4Kx4K, tôi sẽ đăng chúng sau. Chương trình đã tiến bộ rất nhiều, đã có vô số tối ưu hóa. Tôi cũng làm cho nó thân thiện với người dùng để bất kỳ ai cũng có thể dễ dàng sử dụng nó, nó có một dòng lệnh đẹp. Chương trình cũng là ngẫu nhiên xác định, có nghĩa là, bạn có thể sử dụng một hạt giống ngẫu nhiên và nó sẽ tạo ra cùng một hình ảnh mỗi lần.

Dưới đây là một số ám ảnh lớn.

512 yêu thích của tôi:


(nguồn: joco.name )

Những năm 2048 xuất hiện trong video của tôi :


(nguồn: joco.name )


(nguồn: joco.name )


(nguồn: joco.name )


(nguồn: joco.name )

4096 kết xuất đầu tiên (TODO: chúng đang được tải lên và trang web của tôi không thể xử lý lưu lượng truy cập lớn, vì vậy chúng tạm thời được di dời):


(nguồn: joco.name )


(nguồn: joco.name )


(nguồn: joco.name )


(nguồn: joco.name )


25
Bây giờ điều này là mát mẻ!
Jaa-c

5
Rất đẹp :-D Bây giờ làm cho một số lớn hơn!
squossish ossifrage

20
Bạn là một nghệ sĩ thực thụ! :)
AL

10
Bao nhiêu cho một bản in?
primo

16
Tôi đang làm việc trên các bản render lớn và một video 1080p. Gonna mất hàng giờ hoặc nhiều ngày. Tôi hy vọng ai đó sẽ có thể tạo ra một bản in từ một kết xuất lớn. Hoặc thậm chí là một chiếc áo phông: mã ở một bên, hình ảnh ở bên kia. Bất cứ ai có thể sắp xếp điều đó?
fejesjoco

248

Chế biến

Cập nhật! Hình ảnh 4096x4096!

Tôi đã hợp nhất bài đăng thứ hai của mình vào bài này bằng cách kết hợp hai chương trình lại với nhau.

Một bộ sưu tập đầy đủ các hình ảnh được chọn có thể được tìm thấy ở đây, trên Dropbox . (Lưu ý: DropBox không thể tạo bản xem trước cho hình ảnh 4096x4096; chỉ cần nhấp vào chúng sau đó nhấp vào "Tải xuống").

Nếu bạn chỉ nhìn vào một cái nhìn vào cái này (có thể điều chỉnh được)! Ở đây, nó được thu nhỏ lại (và nhiều hơn nữa bên dưới), 2048x1024 ban đầu:

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

Chương trình này hoạt động bằng cách đi bộ các đường dẫn từ các điểm được chọn ngẫu nhiên trong khối màu, sau đó vẽ chúng thành các đường dẫn được chọn ngẫu nhiên trong ảnh. Có rất nhiều khả năng. Các tùy chọn cấu hình là:

  • Chiều dài tối đa của đường dẫn khối màu.
  • Bước tối đa để vượt qua khối màu (giá trị lớn hơn gây ra phương sai lớn hơn nhưng giảm thiểu số lượng đường dẫn nhỏ về cuối khi mọi thứ trở nên chặt chẽ).
  • Ốp lát hình ảnh.
  • Hiện tại có hai chế độ đường dẫn hình ảnh:
    • Chế độ 1 (chế độ của bài gốc này): Tìm một khối pixel không sử dụng trong ảnh và hiển thị cho khối đó. Các khối có thể được đặt ngẫu nhiên, hoặc được sắp xếp từ trái sang phải.
    • Chế độ 2 (chế độ của bài đăng thứ hai mà tôi đã hợp nhất vào bài này): Chọn một điểm bắt đầu ngẫu nhiên trong ảnh và đi dọc theo một con đường qua các pixel không sử dụng; có thể đi bộ xung quanh các pixel được sử dụng. Tùy chọn cho chế độ này:
      • Đặt hướng để đi bộ (trực giao, chéo hoặc cả hai).
      • Có hay không thay đổi hướng (hiện tại theo chiều kim đồng hồ nhưng mã vẫn linh hoạt) sau mỗi bước hoặc chỉ thay đổi hướng khi gặp pixel bị chiếm dụng ..
      • Tùy chọn xáo trộn thứ tự thay đổi hướng (thay vì theo chiều kim đồng hồ).

Nó hoạt động cho tất cả các kích thước lên đến 4096x4096.

Bản phác thảo xử lý hoàn chỉnh có thể được tìm thấy ở đây: Tracer.zip

Tôi đã dán tất cả các tệp trong cùng một khối mã bên dưới chỉ để tiết kiệm dung lượng (thậm chí tất cả trong một tệp, nó vẫn là một bản phác thảo hợp lệ). Nếu bạn muốn sử dụng một trong các cài đặt trước, hãy thay đổi chỉ mục trong gPresetbài tập. Nếu bạn chạy nó trong Xử lý, bạn có thể nhấn rtrong khi nó đang chạy để tạo một hình ảnh mới.

  • Cập nhật 1: Mã được tối ưu hóa để theo dõi màu / pixel không sử dụng đầu tiên và không tìm kiếm trên các pixel đã sử dụng; giảm thời gian tạo 2048x1024 từ 10-30 phút xuống còn khoảng 15 giây và 4096x4096 từ 1-3 giờ xuống còn khoảng 1 phút. Thả hộp nguồn và nguồn dưới đây cập nhật.
  • Cập nhật 2: Đã sửa lỗi ngăn không cho hình ảnh 4096x4096 được tạo.
final int BITS = 5; // Set to 5, 6, 7, or 8!

// Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts)
final Preset[] PRESETS = new Preset[] {
  // 0
  new Preset("flowers",      BITS, 8*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS),
  new Preset("diamonds",     BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
  new Preset("diamondtile",  BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
  new Preset("shards",       BITS, 2*32*32, 2, ImageRect.MODE2, ImageRect.ALL_CW | ImageRect.CHANGE_DIRS | ImageRect.SHUFFLE_DIRS),
  new Preset("bigdiamonds",  BITS,  100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS),
  // 5
  new Preset("bigtile",      BITS,  100000, 6, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.CHANGE_DIRS | ImageRect.WRAP),
  new Preset("boxes",        BITS,   32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW),
  new Preset("giftwrap",     BITS,   32*32, 2, ImageRect.MODE2, ImageRect.ORTHO_CW | ImageRect.WRAP),
  new Preset("diagover",     BITS,   32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW),
  new Preset("boxfade",      BITS,   32*32, 2, ImageRect.MODE2, ImageRect.DIAG_CW | ImageRect.CHANGE_DIRS),
  // 10
  new Preset("randlimit",    BITS,     512, 2, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
  new Preset("ordlimit",     BITS,      64, 2, ImageRect.MODE1, 0),
  new Preset("randtile",     BITS,    2048, 3, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS | ImageRect.WRAP),
  new Preset("randnolimit",  BITS, 1000000, 1, ImageRect.MODE1, ImageRect.RANDOM_BLOCKS),
  new Preset("ordnolimit",   BITS, 1000000, 1, ImageRect.MODE1, 0)
};


PGraphics gFrameBuffer;
Preset gPreset = PRESETS[2];

void generate () {
  ColorCube cube = gPreset.createCube();
  ImageRect image = gPreset.createImage();
  gFrameBuffer = createGraphics(gPreset.getWidth(), gPreset.getHeight(), JAVA2D);
  gFrameBuffer.noSmooth();
  gFrameBuffer.beginDraw();
  while (!cube.isExhausted())
    image.drawPath(cube.nextPath(), gFrameBuffer);
  gFrameBuffer.endDraw();
  if (gPreset.getName() != null)
    gFrameBuffer.save(gPreset.getName() + "_" + gPreset.getCubeSize() + ".png");
  //image.verifyExhausted();
  //cube.verifyExhausted();
}

void setup () {
  size(gPreset.getDisplayWidth(), gPreset.getDisplayHeight());
  noSmooth();
  generate();
}

void keyPressed () {
  if (key == 'r' || key == 'R')
    generate();
}

boolean autogen = false;
int autop = 0;
int autob = 5;

void draw () {
  if (autogen) {
    gPreset = new Preset(PRESETS[autop], autob);
    generate();
    if ((++ autop) >= PRESETS.length) {
      autop = 0;
      if ((++ autob) > 8)
        autogen = false;
    }
  }
  if (gPreset.isWrapped()) {
    int hw = width/2;
    int hh = height/2;
    image(gFrameBuffer, 0, 0, hw, hh);
    image(gFrameBuffer, hw, 0, hw, hh);
    image(gFrameBuffer, 0, hh, hw, hh);
    image(gFrameBuffer, hw, hh, hw, hh);
  } else {
    image(gFrameBuffer, 0, 0, width, height);
  }
}

static class ColorStep {
  final int r, g, b;
  ColorStep (int rr, int gg, int bb) { r=rr; g=gg; b=bb; }
}

class ColorCube {

  final boolean[] used;
  final int size; 
  final int maxPathLength;
  final ArrayList<ColorStep> allowedSteps = new ArrayList<ColorStep>();

  int remaining;
  int pathr = -1, pathg, pathb;
  int firstUnused = 0;

  ColorCube (int size, int maxPathLength, int maxStep) {
    this.used = new boolean[size*size*size];
    this.remaining = size * size * size;
    this.size = size;
    this.maxPathLength = maxPathLength;
    for (int r = -maxStep; r <= maxStep; ++ r)
      for (int g = -maxStep; g <= maxStep; ++ g)
        for (int b = -maxStep; b <= maxStep; ++ b)
          if (r != 0 && g != 0 && b != 0)
            allowedSteps.add(new ColorStep(r, g, b));
  }

  boolean isExhausted () {
    println(remaining);
    return remaining <= 0;
  }

  boolean isUsed (int r, int g, int b) {
    if (r < 0 || r >= size || g < 0 || g >= size || b < 0 || b >= size)
      return true;
    else
      return used[(r*size+g)*size+b];
  }

  void setUsed (int r, int g, int b) {
    used[(r*size+g)*size+b] = true;
  }

  int nextColor () {

    if (pathr == -1) { // Need to start a new path.

      // Limit to 50 attempts at random picks; things get tight near end.
      for (int n = 0; n < 50 && pathr == -1; ++ n) {
        int r = (int)random(size);
        int g = (int)random(size);
        int b = (int)random(size);
        if (!isUsed(r, g, b)) {
          pathr = r;
          pathg = g;
          pathb = b;
        }
      }
      // If we didn't find one randomly, just search for one.
      if (pathr == -1) {
        final int sizesq = size*size;
        final int sizemask = size - 1;
        for (int rgb = firstUnused; rgb < size*size*size; ++ rgb) {
          pathr = (rgb/sizesq)&sizemask;//(rgb >> 10) & 31;
          pathg = (rgb/size)&sizemask;//(rgb >> 5) & 31;
          pathb = rgb&sizemask;//rgb & 31;
          if (!used[rgb]) {
            firstUnused = rgb;
            break;
          }
        }
      }

      assert(pathr != -1);

    } else { // Continue moving on existing path.

      // Find valid next path steps.
      ArrayList<ColorStep> possibleSteps = new ArrayList<ColorStep>();
      for (ColorStep step:allowedSteps)
        if (!isUsed(pathr+step.r, pathg+step.g, pathb+step.b))
          possibleSteps.add(step);

      // If there are none end this path.
      if (possibleSteps.isEmpty()) {
        pathr = -1;
        return -1;
      }

      // Otherwise pick a random step and move there.
      ColorStep s = possibleSteps.get((int)random(possibleSteps.size()));
      pathr += s.r;
      pathg += s.g;
      pathb += s.b;

    }

    setUsed(pathr, pathg, pathb);  
    return 0x00FFFFFF & color(pathr * (256/size), pathg * (256/size), pathb * (256/size));

  } 

  ArrayList<Integer> nextPath () {

    ArrayList<Integer> path = new ArrayList<Integer>(); 
    int rgb;

    while ((rgb = nextColor()) != -1) {
      path.add(0xFF000000 | rgb);
      if (path.size() >= maxPathLength) {
        pathr = -1;
        break;
      }
    }

    remaining -= path.size();

    //assert(!path.isEmpty());
    if (path.isEmpty()) {
      println("ERROR: empty path.");
      verifyExhausted();
    }
    return path;

  }

  void verifyExhausted () {
    final int sizesq = size*size;
    final int sizemask = size - 1;
    for (int rgb = 0; rgb < size*size*size; ++ rgb) {
      if (!used[rgb]) {
        int r = (rgb/sizesq)&sizemask;
        int g = (rgb/size)&sizemask;
        int b = rgb&sizemask;
        println("UNUSED COLOR: " + r + " " + g + " " + b);
      }
    }
    if (remaining != 0)
      println("REMAINING COLOR COUNT IS OFF: " + remaining);
  }

}


static class ImageStep {
  final int x;
  final int y;
  ImageStep (int xx, int yy) { x=xx; y=yy; }
}

static int nmod (int a, int b) {
  return (a % b + b) % b;
}

class ImageRect {

  // for mode 1:
  //   one of ORTHO_CW, DIAG_CW, ALL_CW
  //   or'd with flags CHANGE_DIRS
  static final int ORTHO_CW = 0;
  static final int DIAG_CW = 1;
  static final int ALL_CW = 2;
  static final int DIR_MASK = 0x03;
  static final int CHANGE_DIRS = (1<<5);
  static final int SHUFFLE_DIRS = (1<<6);

  // for mode 2:
  static final int RANDOM_BLOCKS = (1<<0);

  // for both modes:
  static final int WRAP = (1<<16);

  static final int MODE1 = 0;
  static final int MODE2 = 1;

  final boolean[] used;
  final int width;
  final int height;
  final boolean changeDir;
  final int drawMode;
  final boolean randomBlocks;
  final boolean wrap;
  final ArrayList<ImageStep> allowedSteps = new ArrayList<ImageStep>();

  // X/Y are tracked instead of index to preserve original unoptimized mode 1 behavior
  // which does column-major searches instead of row-major.
  int firstUnusedX = 0;
  int firstUnusedY = 0;

  ImageRect (int width, int height, int drawMode, int drawOpts) {
    boolean myRandomBlocks = false, myChangeDir = false;
    this.used = new boolean[width*height];
    this.width = width;
    this.height = height;
    this.drawMode = drawMode;
    this.wrap = (drawOpts & WRAP) != 0;
    if (drawMode == MODE1) {
      myRandomBlocks = (drawOpts & RANDOM_BLOCKS) != 0;
    } else if (drawMode == MODE2) {
      myChangeDir = (drawOpts & CHANGE_DIRS) != 0;
      switch (drawOpts & DIR_MASK) {
      case ORTHO_CW:
        allowedSteps.add(new ImageStep(1, 0));
        allowedSteps.add(new ImageStep(0, -1));
        allowedSteps.add(new ImageStep(-1, 0));
        allowedSteps.add(new ImageStep(0, 1));
        break;
      case DIAG_CW:
        allowedSteps.add(new ImageStep(1, -1));
        allowedSteps.add(new ImageStep(-1, -1));
        allowedSteps.add(new ImageStep(-1, 1));
        allowedSteps.add(new ImageStep(1, 1));
        break;
      case ALL_CW:
        allowedSteps.add(new ImageStep(1, 0));
        allowedSteps.add(new ImageStep(1, -1));
        allowedSteps.add(new ImageStep(0, -1));
        allowedSteps.add(new ImageStep(-1, -1));
        allowedSteps.add(new ImageStep(-1, 0));
        allowedSteps.add(new ImageStep(-1, 1));
        allowedSteps.add(new ImageStep(0, 1));
        allowedSteps.add(new ImageStep(1, 1));
        break;
      }
      if ((drawOpts & SHUFFLE_DIRS) != 0)
        java.util.Collections.shuffle(allowedSteps);
    }
    this.randomBlocks = myRandomBlocks;
    this.changeDir = myChangeDir;
  }

  boolean isUsed (int x, int y) {
    if (wrap) {
      x = nmod(x, width);
      y = nmod(y, height);
    }
    if (x < 0 || x >= width || y < 0 || y >= height)
      return true;
    else
      return used[y*width+x];
  }

  boolean isUsed (int x, int y, ImageStep d) {
    return isUsed(x + d.x, y + d.y);
  }

  void setUsed (int x, int y) {
    if (wrap) {
      x = nmod(x, width);
      y = nmod(y, height);
    }
    used[y*width+x] = true;
  }

  boolean isBlockFree (int x, int y, int w, int h) {
    for (int yy = y; yy < y + h; ++ yy)
      for (int xx = x; xx < x + w; ++ xx)
        if (isUsed(xx, yy))
          return false;
    return true;
  }

  void drawPath (ArrayList<Integer> path, PGraphics buffer) {
    if (drawMode == MODE1)
      drawPath1(path, buffer);
    else if (drawMode == MODE2)
      drawPath2(path, buffer);
  }

  void drawPath1 (ArrayList<Integer> path, PGraphics buffer) {

    int w = (int)(sqrt(path.size()) + 0.5);
    if (w < 1) w = 1; else if (w > width) w = width;
    int h = (path.size() + w - 1) / w; 
    int x = -1, y = -1;

    int woff = wrap ? 0 : (1 - w);
    int hoff = wrap ? 0 : (1 - h);

    // Try up to 50 times to find a random location for block.
    if (randomBlocks) {
      for (int n = 0; n < 50 && x == -1; ++ n) {
        int xx = (int)random(width + woff);
        int yy = (int)random(height + hoff);
        if (isBlockFree(xx, yy, w, h)) {
          x = xx;
          y = yy;
        }
      }
    }

    // If random choice failed just search for one.
    int starty = firstUnusedY;
    for (int xx = firstUnusedX; xx < width + woff && x == -1; ++ xx) {
      for (int yy = starty; yy < height + hoff && x == -1; ++ yy) {
        if (isBlockFree(xx, yy, w, h)) {
          firstUnusedX = x = xx;
          firstUnusedY = y = yy;
        }  
      }
      starty = 0;
    }

    if (x != -1) {
      for (int xx = x, pathn = 0; xx < x + w && pathn < path.size(); ++ xx)
        for (int yy = y; yy < y + h && pathn < path.size(); ++ yy, ++ pathn) {
          buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
          setUsed(xx, yy);
        }
    } else {
      for (int yy = 0, pathn = 0; yy < height && pathn < path.size(); ++ yy)
        for (int xx = 0; xx < width && pathn < path.size(); ++ xx)
          if (!isUsed(xx, yy)) {
            buffer.set(nmod(xx, width), nmod(yy, height), path.get(pathn));
            setUsed(xx, yy);
            ++ pathn;
          }
    }

  }

  void drawPath2 (ArrayList<Integer> path, PGraphics buffer) {

    int pathn = 0;

    while (pathn < path.size()) {

      int x = -1, y = -1;

      // pick a random location in the image (try up to 100 times before falling back on search)

      for (int n = 0; n < 100 && x == -1; ++ n) {
        int xx = (int)random(width);
        int yy = (int)random(height);
        if (!isUsed(xx, yy)) {
          x = xx;
          y = yy;
        }
      }  

      // original:
      //for (int yy = 0; yy < height && x == -1; ++ yy)
      //  for (int xx = 0; xx < width && x == -1; ++ xx)
      //    if (!isUsed(xx, yy)) {
      //      x = xx;
      //      y = yy;
      //    }
      // optimized:
      if (x == -1) {
        for (int n = firstUnusedY * width + firstUnusedX; n < used.length; ++ n) {
          if (!used[n]) {
            firstUnusedX = x = (n % width);
            firstUnusedY = y = (n / width);
            break;
          }     
        }
      }

      // start drawing

      int dir = 0;

      while (pathn < path.size()) {

        buffer.set(nmod(x, width), nmod(y, height), path.get(pathn ++));
        setUsed(x, y);

        int diro;
        for (diro = 0; diro < allowedSteps.size(); ++ diro) {
          int diri = (dir + diro) % allowedSteps.size();
          ImageStep step = allowedSteps.get(diri);
          if (!isUsed(x, y, step)) {
            dir = diri;
            x += step.x;
            y += step.y;
            break;
          }
        }

        if (diro == allowedSteps.size())
          break;

        if (changeDir) 
          ++ dir;

      }    

    }

  }

  void verifyExhausted () {
    for (int n = 0; n < used.length; ++ n)
      if (!used[n])
        println("UNUSED IMAGE PIXEL: " + (n%width) + " " + (n/width));
  }

}


class Preset {

  final String name;
  final int cubeSize;
  final int maxCubePath;
  final int maxCubeStep;
  final int imageWidth;
  final int imageHeight;
  final int imageMode;
  final int imageOpts;
  final int displayScale;

  Preset (Preset p, int colorBits) {
    this(p.name, colorBits, p.maxCubePath, p.maxCubeStep, p.imageMode, p.imageOpts);
  }

  Preset (String name, int colorBits, int maxCubePath, int maxCubeStep, int imageMode, int imageOpts) {
    final int csize[] = new int[]{ 32, 64, 128, 256 };
    final int iwidth[] = new int[]{ 256, 512, 2048, 4096 };
    final int iheight[] = new int[]{ 128, 512, 1024, 4096 };
    final int dscale[] = new int[]{ 2, 1, 1, 1 };
    this.name = name; 
    this.cubeSize = csize[colorBits - 5];
    this.maxCubePath = maxCubePath;
    this.maxCubeStep = maxCubeStep;
    this.imageWidth = iwidth[colorBits - 5];
    this.imageHeight = iheight[colorBits - 5];
    this.imageMode = imageMode;
    this.imageOpts = imageOpts;
    this.displayScale = dscale[colorBits - 5];
  }

  ColorCube createCube () {
    return new ColorCube(cubeSize, maxCubePath, maxCubeStep);
  }

  ImageRect createImage () {
    return new ImageRect(imageWidth, imageHeight, imageMode, imageOpts);
  }

  int getWidth () {
    return imageWidth;
  }

  int getHeight () {
    return imageHeight;
  }

  int getDisplayWidth () {
    return imageWidth * displayScale * (isWrapped() ? 2 : 1);
  }

  int getDisplayHeight () {
    return imageHeight * displayScale * (isWrapped() ? 2 : 1);
  }

  String getName () {
    return name;
  }

  int getCubeSize () {
    return cubeSize;
  }

  boolean isWrapped () {
    return (imageOpts & ImageRect.WRAP) != 0;
  }

}

Dưới đây là bộ ảnh 256x128 đầy đủ mà tôi thích:

Chế độ 1:

Yêu thích của tôi từ bộ ban đầu (max_path_length = 512, path_step = 2, ngẫu nhiên, được hiển thị 2x, liên kết 256x128 ):

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

Những người khác (hai bên trái được đặt hàng, bên phải hai ngẫu nhiên, giới hạn hai chiều dài trên cùng, hai bên dưới không giới hạn):

mệnh lệnh randlimit ordnolimit randnolimit

Điều này có thể được lát gạch:

randtile

Chế độ 2:

kim cương những bông hoa đấu hộp tã bigdiamonds hộp2 mảnh vỡ

Những cái này có thể được lát gạch:

bigtile kim cương gói quà

Lựa chọn 512x512:

Kim cương có thể điều chỉnh, yêu thích của tôi từ chế độ 2; bạn có thể thấy trong phần này cách các đường dẫn đi xung quanh các đối tượng hiện có:

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

Bước đường dẫn lớn hơn và độ dài đường dẫn tối đa, có thể điều chỉnh:

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

Chế độ ngẫu nhiên 1, có thể điều chỉnh:

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

Nhiều lựa chọn hơn:

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

Tất cả các kết xuất 512x512 có thể được tìm thấy trong thư mục dropbox (* _64.png).

2048x1024 và 4096x4096:

Chúng quá lớn để nhúng và tất cả các máy chủ hình ảnh tôi tìm thấy thả chúng xuống 1600x1200. Tôi hiện đang hiển thị một bộ hình ảnh 4096x4096 để sớm có nhiều hơn nữa. Thay vì bao gồm tất cả các liên kết ở đây, chỉ cần kiểm tra chúng trong thư mục dropbox (* _128.png và * _256.png, lưu ý: các liên kết 4096x4096 quá lớn đối với trình xem trước dropbox, chỉ cần nhấp vào "tải xuống"). Dưới đây là một số mục yêu thích của tôi:

2048x1024 viên kim cương có thể điều chỉnh lớn (cùng loại mà tôi đã liên kết ở đầu bài này)

2048x1024 kim cương (tôi thích cái này!), Thu nhỏ lại:

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

4096x4096 viên kim cương có thể điều chỉnh lớn (Cuối cùng! Nhấp vào 'tải xuống' trong liên kết Dropbox; nó quá lớn so với trình xem trước của chúng), thu nhỏ lại:

4096x4096 kim cương có thể điều chỉnh lớn

4096x4096 chế độ ngẫu nhiên 1 : nhập mô tả hình ảnh ở đây

4096x4096 một cái khác tuyệt vời

Cập nhật: Bộ ảnh cài đặt sẵn 2048x1024 đã hoàn tất và trong hộp thả xuống. Bộ 4096x4096 nên được thực hiện trong vòng một giờ.

Có rất nhiều bài hay, tôi đang rất khó chọn bài nào để đăng, vì vậy hãy kiểm tra liên kết thư mục!


6
Nó làm tôi nhớ đến những cái nhìn cận cảnh về một số khoáng sản.
Morwenn

3
Không phải là một phần của cuộc thi, nhưng tôi nghĩ điều này thật tuyệt ; Tôi đã áp dụng độ mờ gaussian lớn và độ tương phản tự động tăng cường cho một trong những bức ảnh chế độ ngẫu nhiên 1 trong photoshop và nó tạo ra một loại hình nền-y đẹp cho máy tính để bàn.
Jason C

2
Whoa, đây là những hình ảnh mát mẻ!
Sevenseacat

2
Nhắc nhở tôi về kết cấu của Gustav Klimt.
Kim

2
Bạn có biết bạn có thể liên kết hình ảnh trong Dropbox không? Chỉ cần sao chép URL tải về, tháo dl=1token_hash=<something>một phần và làm cho một liên kết đến hình ảnh của bạn như thế này: [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). Một mẹo khác: bạn có thể nén hình ảnh của mình (Tôi nhận được kết quả tốt với TruePNG ( Tải xuống )). Tôi đã có thể lưu 28,1% kích thước tệp trên hình ảnh này .
dùng2428118

219

Python w / PIL

Điều này dựa trên Fractal Newton , đặc biệt cho z → z 5 - 1 . Do có năm gốc, và do đó năm điểm hội tụ, không gian màu có sẵn được chia thành năm vùng, dựa trên Huế. Các điểm riêng lẻ được sắp xếp trước theo số lần lặp cần thiết để đạt đến điểm hội tụ của chúng và sau đó theo khoảng cách đến điểm đó, với các giá trị trước đó được gán màu sáng hơn.

Cập nhật: 4096x4096 kết xuất lớn, được lưu trữ trên allrgb.com .

Bản gốc (33,7 MB)

Cận cảnh chính trung tâm (kích thước thực tế):

Một điểm thuận lợi khác nhau sử dụng các giá trị này:

xstart = 0
ystart = 0

xd = 1 / dim[0]
yd = 1 / dim[1]

Bản gốc (32,2 MB)

Và một cái khác sử dụng:

xstart = 0.5
ystart = 0.5

xd = 0.001 / dim[0]
yd = 0.001 / dim[1]

Bản gốc (27,2 MB)


Hoạt hình

Theo yêu cầu, tôi đã biên soạn một hình ảnh động phóng to.

Tiêu điểm: ( 0,50051 , -0,50051 )
Hệ số thu phóng: 2 1/5

Tiêu điểm là một giá trị hơi kỳ lạ, vì tôi không muốn phóng to một chấm đen. Hệ số thu phóng được chọn sao cho nó tăng gấp đôi cứ sau 5 khung hình.

Một lời trêu ghẹo 32x32:

Có thể xem phiên bản 256x256 tại đây:
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

Có thể có những điểm phóng to về mặt toán học "vào chính chúng", cho phép tạo ra một hình ảnh động vô hạn. Nếu tôi có thể xác định bất kỳ, tôi sẽ thêm chúng ở đây.


Nguồn

from __future__ import division
from PIL import Image, ImageDraw
from cmath import phase
from sys import maxint

dim  = (4096, 4096)
bits = 8

def RGBtoHSV(R, G, B):
  R /= 255
  G /= 255
  B /= 255

  cmin = min(R, G, B)
  cmax = max(R, G, B)
  dmax = cmax - cmin

  V = cmax

  if dmax == 0:
    H = 0
    S = 0

  else:
    S = dmax/cmax

    dR = ((cmax - R)/6 + dmax/2)/dmax
    dG = ((cmax - G)/6 + dmax/2)/dmax
    dB = ((cmax - B)/6 + dmax/2)/dmax

    if   R == cmax: H = (dB - dG)%1
    elif G == cmax: H = (1/3 + dR - dB)%1
    elif B == cmax: H = (2/3 + dG - dR)%1

  return (H, S, V)

cmax = (1<<bits)-1
cfac = 255/cmax

img  = Image.new('RGB', dim)
draw = ImageDraw.Draw(img)

xstart = -2
ystart = -2

xd = 4 / dim[0]
yd = 4 / dim[1]

tol = 1e-12

a = [[], [], [], [], []]

for x in range(dim[0]):
  print x, "\r",
  for y in range(dim[1]):
    z = d = complex(xstart + x*xd, ystart + y*yd)
    c = 0
    l = 1
    while abs(l-z) > tol and abs(z) > tol:
      l = z
      z -= (z**5-1)/(5*z**4)
      c += 1
    if z == 0: c = maxint
    p = int(phase(z))

    a[p] += (c,abs(d-z), x, y),

for i in range(5):
  a[i].sort(reverse = False)

pnum = [len(a[i]) for i in range(5)]
ptot = dim[0]*dim[1]

bounds = []
lbound = 0
for i in range(4):
  nbound = lbound + pnum[i]/ptot
  bounds += nbound,
  lbound = nbound

t = [[], [], [], [], []]
for i in range(ptot-1, -1, -1):
  r = (i>>bits*2)*cfac
  g = (cmax&i>>bits)*cfac
  b = (cmax&i)*cfac
  (h, s, v) = RGBtoHSV(r, g, b)
  h = (h+0.1)%1
  if   h < bounds[0] and len(t[0]) < pnum[0]: p=0
  elif h < bounds[1] and len(t[1]) < pnum[1]: p=1
  elif h < bounds[2] and len(t[2]) < pnum[2]: p=2
  elif h < bounds[3] and len(t[3]) < pnum[3]: p=3
  else: p=4
  t[p] += (int(r), int(g), int(b)),

for i in range(5):
  t[i].sort(key = lambda c: c[0]*2126 + c[1]*7152 + c[2]*722, reverse = True)

r = [0, 0, 0, 0, 0]
for p in range(5):
  for c,d,x,y in a[p]:
    draw.point((x,y), t[p][r[p]])
    r[p] += 1

img.save("out.png")

6
Cuối cùng là một fractal :) Yêu những người đó. Ngoài ra, màu xanh lá cây ở 144 độ là màu yêu thích của tôi (trái ngược với màu xanh lá cây thuần khiết ở 120 độ chỉ là nhàm chán).
Mark Jeronimus

2
Tôi không biết, tôi thực sự thích phiên bản AllRGB hơn; sự cần thiết phải sử dụng không gian độ chói đầy đủ nhấn mạnh độc đáo độ dốc.
Ilmari Karonen

2
+1 Cuối cùng là một số fractals tốt! Cái cuối cùng là sở thích cá nhân của tôi. Bạn nên làm cho một video phóng to! (@Quincunx: Tôi cũng thấy bạn; nó đã có phiếu bầu của tôi từ ngày 1!)
Jason C

1
@JasonC Tôi đã thêm một hình ảnh động;)
primo

2
@primo Tôi biết tôi đến muộn, nhưng tôi chỉ muốn nói những hình ảnh này thật ngoạn mục.
Ashwin Gupta

130

Tôi có ý tưởng này từ thuật toán của người dùng fejesjoco và muốn chơi một chút, vì vậy tôi bắt đầu viết thuật toán của riêng mình từ đầu.

Tôi đang đăng bài này vì tôi cảm thấy rằng nếu tôi có thể làm điều gì đó tốt hơn * tốt nhất cho các bạn, tôi không nghĩ thử thách này đã kết thúc. Để so sánh, có một số thiết kế tuyệt đẹp trên allRGB mà tôi xem xét vượt quá mức đạt được ở đây và tôi không biết họ đã làm như thế nào.

*) vẫn sẽ được quyết định bằng phiếu bầu

Thuật toán này:

  1. Bắt đầu với một (vài) hạt giống, với màu sắc càng gần với màu đen.
  2. Giữ một danh sách tất cả các pixel không được chú ý và 8 - được kết nối với một điểm đã truy cập.
  3. Chọn một điểm ** ngẫu nhiên từ danh sách đó
  4. Tính màu trung bình của tất cả các pixel được tính [Chỉnh sửa ... trong ô vuông 9x9 bằng hạt nhân Gaussian] được kết nối với nó 8 (đây là lý do tại sao nó trông rất mượt) Nếu không tìm thấy, hãy lấy màu đen.
  5. trong một khối 3x3x3 xung quanh màu này, tìm kiếm một màu không sử dụng.
    • Khi nhiều màu được tìm thấy, lấy màu tối nhất.
    • Khi nhiều màu tối bằng nhau được tìm thấy, lấy một màu ngẫu nhiên trong số đó.
    • Khi không tìm thấy gì, hãy cập nhật phạm vi tìm kiếm thành 5x5x5, 7x7x7, v.v. Lặp lại từ 5.
  6. Vẽ pixel, cập nhật danh sách và lặp lại từ 3

Tôi cũng đã thử nghiệm các xác suất khác nhau của việc chọn điểm ứng viên dựa trên việc đếm số lượng pixel đã chọn có bao nhiêu pixel đã chọn, nhưng nó chỉ làm chậm thuật toán mà không làm cho nó đẹp hơn. Thuật toán hiện tại không sử dụng xác suất và chọn một điểm ngẫu nhiên từ danh sách. Điều này khiến các điểm có rất nhiều hàng xóm nhanh chóng lấp đầy, khiến nó chỉ là một quả bóng rắn đang phát triển với một cạnh mờ. Điều này cũng ngăn chặn sự không có sẵn của các màu lân cận nếu các kẽ hở sẽ được lấp đầy sau này trong quy trình.

Hình ảnh là hình xuyến.

Java

Tải xuống: com.digitalmodularthư viện

package demos;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Arrays;

import com.digitalmodular.utilities.RandomFunctions;
import com.digitalmodular.utilities.gui.ImageFunctions;
import com.digitalmodular.utilities.swing.window.PixelImage;
import com.digitalmodular.utilities.swing.window.PixelWindow;

/**
 * @author jeronimus
 */
// Date 2014-02-28
public class AllColorDiffusion extends PixelWindow implements Runnable {
    private static final int    CHANNEL_BITS    = 7;

    public static void main(String[] args) {
        int bits = CHANNEL_BITS * 3;
        int heightBits = bits / 2;
        int widthBits = bits - heightBits;

        new AllColorDiffusion(CHANNEL_BITS, 1 << widthBits, 1 << heightBits);
    }

    private final int           width;
    private final int           height;
    private final int           channelBits;
    private final int           channelSize;

    private PixelImage          img;
    private javax.swing.Timer   timer;

    private boolean[]           colorCube;
    private long[]              foundColors;
    private boolean[]           queued;
    private int[]               queue;
    private int                 queuePointer    = 0;
    private int                 remaining;

    public AllColorDiffusion(int channelBits, int width, int height) {
        super(1024, 1024 * height / width);

        RandomFunctions.RND.setSeed(0);

        this.width = width;
        this.height = height;
        this.channelBits = channelBits;
        channelSize = 1 << channelBits;
    }

    @Override
    public void initialized() {
        img = new PixelImage(width, height);

        colorCube = new boolean[channelSize * channelSize * channelSize];
        foundColors = new long[channelSize * channelSize * channelSize];
        queued = new boolean[width * height];
        queue = new int[width * height];
        for (int i = 0; i < queue.length; i++)
            queue[i] = i;

        new Thread(this).start();
    }

    @Override
    public void resized() {}

    @Override
    public void run() {
        timer = new javax.swing.Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                draw();
            }
        });

        while (true) {
            img.clear(0);
            init();
            render();
        }

        // System.exit(0);
    }

    private void init() {
        RandomFunctions.RND.setSeed(0);

        Arrays.fill(colorCube, false);
        Arrays.fill(queued, false);
        remaining = width * height;

        // Initial seeds (need to be the darkest colors, because of the darkest
        // neighbor color search algorithm.)
        setPixel(width / 2 + height / 2 * width, 0);
        remaining--;
    }

    private void render() {
        timer.start();

        for (; remaining > 0; remaining--) {
            int point = findPoint();
            int color = findColor(point);
            setPixel(point, color);
        }

        timer.stop();
        draw();

        try {
            ImageFunctions.savePNG(System.currentTimeMillis() + ".png", img.image);
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    void draw() {
        g.drawImage(img.image, 0, 0, getWidth(), getHeight(), 0, 0, width, height, null);
        repaintNow();
    }

    private int findPoint() {
        while (true) {
            // Time to reshuffle?
            if (queuePointer == 0) {
                for (int i = queue.length - 1; i > 0; i--) {
                    int j = RandomFunctions.RND.nextInt(i);
                    int temp = queue[i];
                    queue[i] = queue[j];
                    queue[j] = temp;
                    queuePointer = queue.length;
                }
            }

            if (queued[queue[--queuePointer]])
                return queue[queuePointer];
        }
    }

    private int findColor(int point) {
        int x = point & width - 1;
        int y = point / width;

        // Calculate the reference color as the average of all 8-connected
        // colors.
        int r = 0;
        int g = 0;
        int b = 0;
        int n = 0;
        for (int j = -1; j <= 1; j++) {
            for (int i = -1; i <= 1; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                if (img.pixels[point] != 0) {
                    int pixel = img.pixels[point];

                    r += pixel >> 24 - channelBits & channelSize - 1;
                    g += pixel >> 16 - channelBits & channelSize - 1;
                    b += pixel >> 8 - channelBits & channelSize - 1;
                    n++;
                }
            }
        }
        r /= n;
        g /= n;
        b /= n;

        // Find a color that is preferably darker but not too far from the
        // original. This algorithm might fail to take some darker colors at the
        // start, and when the image is almost done the size will become really
        // huge because only bright reference pixels are being searched for.
        // This happens with a probability of 50% with 6 channelBits, and more
        // with higher channelBits values.
        //
        // Try incrementally larger distances from reference color.
        for (int size = 2; size <= channelSize; size *= 2) {
            n = 0;

            // Find all colors in a neighborhood from the reference color (-1 if
            // already taken).
            for (int ri = r - size; ri <= r + size; ri++) {
                if (ri < 0 || ri >= channelSize)
                    continue;
                int plane = ri * channelSize * channelSize;
                int dr = Math.abs(ri - r);
                for (int gi = g - size; gi <= g + size; gi++) {
                    if (gi < 0 || gi >= channelSize)
                        continue;
                    int slice = plane + gi * channelSize;
                    int drg = Math.max(dr, Math.abs(gi - g));
                    int mrg = Math.min(ri, gi);
                    for (int bi = b - size; bi <= b + size; bi++) {
                        if (bi < 0 || bi >= channelSize)
                            continue;
                        if (Math.max(drg, Math.abs(bi - b)) > size)
                            continue;
                        if (!colorCube[slice + bi])
                            foundColors[n++] = Math.min(mrg, bi) << channelBits * 3 | slice + bi;
                    }
                }
            }

            if (n > 0) {
                // Sort by distance from origin.
                Arrays.sort(foundColors, 0, n);

                // Find a random color amongst all colors equally distant from
                // the origin.
                int lowest = (int)(foundColors[0] >> channelBits * 3);
                for (int i = 1; i < n; i++) {
                    if (foundColors[i] >> channelBits * 3 > lowest) {
                        n = i;
                        break;
                    }
                }

                int nextInt = RandomFunctions.RND.nextInt(n);
                return (int)(foundColors[nextInt] & (1 << channelBits * 3) - 1);
            }
        }

        return -1;
    }

    private void setPixel(int point, int color) {
        int b = color & channelSize - 1;
        int g = color >> channelBits & channelSize - 1;
        int r = color >> channelBits * 2 & channelSize - 1;
        img.pixels[point] = 0xFF000000 | ((r << 8 | g) << 8 | b) << 8 - channelBits;

        colorCube[color] = true;

        int x = point & width - 1;
        int y = point / width;
        queued[point] = false;
        for (int j = -1; j <= 1; j++) {
            for (int i = -1; i <= 1; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                if (img.pixels[point] == 0) {
                    queued[point] = true;
                }
            }
        }
    }
}
  • 512 × 512
  • 1 hạt giống ban đầu
  • 1 giây

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

  • 2048 × 1024
  • hơi lát gạch để máy tính để bàn 1920 × 1080
  • 30 giây
  • tiêu cực trong photoshop

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

  • 2048 × 1024
  • 8 hạt
  • 27 giây

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

  • 512 × 512
  • 40 hạt ngẫu nhiên
  • 6 giây

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

  • 4096 × 4096
  • 1 hạt giống
  • Các vệt trở nên sắc nét hơn đáng kể (vì trông chúng có thể cắt một con cá thành sashimi)
  • Có vẻ như nó đã hoàn thành sau 20 phút, nhưng ... không thể hoàn thành vì một số lý do, vì vậy bây giờ tôi đang chạy 7 trường hợp song song qua đêm.

[Xem bên dưới]

[Chỉnh sửa]
** Tôi phát hiện ra rằng phương pháp chọn pixel của tôi hoàn toàn không ngẫu nhiên. Tôi nghĩ rằng có một hoán vị ngẫu nhiên của không gian tìm kiếm sẽ là ngẫu nhiên và nhanh hơn so với ngẫu nhiên thực sự (vì một điểm sẽ không được chọn hai lần một cách tình cờ. Tuy nhiên, bằng cách nào đó, thay thế nó bằng ngẫu nhiên thực sự, tôi liên tục nhận được nhiều đốm nhiễu hơn trong hình ảnh của mình.

[mã phiên bản 2 bị xóa vì tôi vượt quá giới hạn 30.000 ký tự]

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

  • Đã tăng khối tìm kiếm ban đầu lên 5x5x5

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

  • Thậm chí lớn hơn, 9x9x9

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

  • Tai nạn 1. Vô hiệu hóa hoán vị để không gian tìm kiếm luôn tuyến tính.

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

  • Tai nạn 2. Đã thử một kỹ thuật tìm kiếm mới bằng cách sử dụng hàng đợi fifo. Vẫn phải phân tích điều này nhưng tôi nghĩ nó đáng để chia sẻ.

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

  • Luôn chọn trong X pixel không sử dụng từ trung tâm
  • X dao động từ 0 đến 8192 trong các bước của 256

Không thể tải lên hình ảnh: "Rất tiếc! Điều gì đó tồi tệ đã xảy ra! Không phải bạn, đó là chúng tôi. Đây là lỗi của chúng tôi." Hình ảnh quá lớn đối với imgur. Đang thử ở nơi khác ...

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

Thử nghiệm với gói lập lịch tôi tìm thấy trong digitalmodularthư viện để xác định thứ tự các pixel được xử lý (thay vì khuếch tán).

package demos;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Arrays;

import com.digitalmodular.utilities.RandomFunctions;
import com.digitalmodular.utilities.gui.ImageFunctions;
import com.digitalmodular.utilities.gui.schedulers.ScheduledPoint;
import com.digitalmodular.utilities.gui.schedulers.Scheduler;
import com.digitalmodular.utilities.gui.schedulers.XorScheduler;
import com.digitalmodular.utilities.swing.window.PixelImage;
import com.digitalmodular.utilities.swing.window.PixelWindow;

/**
 * @author jeronimus
 */
// Date 2014-02-28
public class AllColorDiffusion3 extends PixelWindow implements Runnable {
    private static final int    CHANNEL_BITS    = 7;

    public static void main(String[] args) {

        int bits = CHANNEL_BITS * 3;
        int heightBits = bits / 2;
        int widthBits = bits - heightBits;

        new AllColorDiffusion3(CHANNEL_BITS, 1 << widthBits, 1 << heightBits);
    }

    private final int           width;
    private final int           height;
    private final int           channelBits;
    private final int           channelSize;

    private PixelImage          img;
    private javax.swing.Timer   timer;
    private Scheduler           scheduler   = new XorScheduler();

    private boolean[]           colorCube;
    private long[]              foundColors;

    public AllColorDiffusion3(int channelBits, int width, int height) {
        super(1024, 1024 * height / width);

        this.width = width;
        this.height = height;
        this.channelBits = channelBits;
        channelSize = 1 << channelBits;
    }

    @Override
    public void initialized() {
        img = new PixelImage(width, height);

        colorCube = new boolean[channelSize * channelSize * channelSize];
        foundColors = new long[channelSize * channelSize * channelSize];

        new Thread(this).start();
    }

    @Override
    public void resized() {}

    @Override
    public void run() {
        timer = new javax.swing.Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                draw();
            }
        });

        // for (double d = 0.2; d < 200; d *= 1.2)
        {
            img.clear(0);
            init(0);
            render();
        }

        // System.exit(0);
    }

    private void init(double param) {
        // RandomFunctions.RND.setSeed(0);

        Arrays.fill(colorCube, false);

        // scheduler = new SpiralScheduler(param);
        scheduler.init(width, height);
    }

    private void render() {
        timer.start();

        while (scheduler.getProgress() != 1) {
            int point = findPoint();
            int color = findColor(point);
            setPixel(point, color);
        }

        timer.stop();
        draw();

        try {
            ImageFunctions.savePNG(System.currentTimeMillis() + ".png", img.image);
        }
        catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    void draw() {
        g.drawImage(img.image, 0, 0, getWidth(), getHeight(), 0, 0, width, height, null);
        repaintNow();
        setTitle(Double.toString(scheduler.getProgress()));
    }

    private int findPoint() {
        ScheduledPoint p = scheduler.poll();

        // try {
        // Thread.sleep(1);
        // }
        // catch (InterruptedException e) {
        // }

        return p.x + width * p.y;
    }

    private int findColor(int point) {
        // int z = 0;
        // for (int i = 0; i < colorCube.length; i++)
        // if (!colorCube[i])
        // System.out.println(i);

        int x = point & width - 1;
        int y = point / width;

        // Calculate the reference color as the average of all 8-connected
        // colors.
        int r = 0;
        int g = 0;
        int b = 0;
        int n = 0;
        for (int j = -3; j <= 3; j++) {
            for (int i = -3; i <= 3; i++) {
                point = (x + i & width - 1) + width * (y + j & height - 1);
                int f = (int)Math.round(10000 * Math.exp((i * i + j * j) * -0.4));
                if (img.pixels[point] != 0) {
                    int pixel = img.pixels[point];

                    r += (pixel >> 24 - channelBits & channelSize - 1) * f;
                    g += (pixel >> 16 - channelBits & channelSize - 1) * f;
                    b += (pixel >> 8 - channelBits & channelSize - 1) * f;
                    n += f;
                }
                // System.out.print(f + "\t");
            }
            // System.out.println();
        }
        if (n > 0) {
            r /= n;
            g /= n;
            b /= n;
        }

        // Find a color that is preferably darker but not too far from the
        // original. This algorithm might fail to take some darker colors at the
        // start, and when the image is almost done the size will become really
        // huge because only bright reference pixels are being searched for.
        // This happens with a probability of 50% with 6 channelBits, and more
        // with higher channelBits values.
        //
        // Try incrementally larger distances from reference color.
        for (int size = 2; size <= channelSize; size *= 2) {
            n = 0;

            // Find all colors in a neighborhood from the reference color (-1 if
            // already taken).
            for (int ri = r - size; ri <= r + size; ri++) {
                if (ri < 0 || ri >= channelSize)
                    continue;
                int plane = ri * channelSize * channelSize;
                int dr = Math.abs(ri - r);
                for (int gi = g - size; gi <= g + size; gi++) {
                    if (gi < 0 || gi >= channelSize)
                        continue;
                    int slice = plane + gi * channelSize;
                    int drg = Math.max(dr, Math.abs(gi - g));
                    // int mrg = Math.min(ri, gi);
                    long srg = ri * 299L + gi * 436L;
                    for (int bi = b - size; bi <= b + size; bi++) {
                        if (bi < 0 || bi >= channelSize)
                            continue;
                        if (Math.max(drg, Math.abs(bi - b)) > size)
                            continue;
                        if (!colorCube[slice + bi])
                            // foundColors[n++] = Math.min(mrg, bi) <<
                            // channelBits * 3 | slice + bi;
                            foundColors[n++] = srg + bi * 114L << channelBits * 3 | slice + bi;
                    }
                }
            }

            if (n > 0) {
                // Sort by distance from origin.
                Arrays.sort(foundColors, 0, n);

                // Find a random color amongst all colors equally distant from
                // the origin.
                int lowest = (int)(foundColors[0] >> channelBits * 3);
                for (int i = 1; i < n; i++) {
                    if (foundColors[i] >> channelBits * 3 > lowest) {
                        n = i;
                        break;
                    }
                }

                int nextInt = RandomFunctions.RND.nextInt(n);
                return (int)(foundColors[nextInt] & (1 << channelBits * 3) - 1);
            }
        }

        return -1;
    }

    private void setPixel(int point, int color) {
        int b = color & channelSize - 1;
        int g = color >> channelBits & channelSize - 1;
        int r = color >> channelBits * 2 & channelSize - 1;
        img.pixels[point] = 0xFF000000 | ((r << 8 | g) << 8 | b) << 8 - channelBits;

        colorCube[color] = true;
    }
}
  • Góc (8)

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

  • Góc (64)

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

  • CRT

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

  • Run lên

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

  • Hoa (5, X), trong đó X dao động từ 0,5 đến 20 trong các bước của X = X × 1,2

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

  • Phép chia lấy phần dư

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

  • Kim tự tháp

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

  • Xuyên tâm

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

  • Ngẫu nhiên

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

  • Đường quét

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

  • Xoắn ốc (X), trong đó X dao động từ 0,1 đến 200 theo các bước của X = X × 1,2
  • Bạn có thể thấy nó nằm trong khoảng từ Radial đến Angular (5)

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

  • Tách

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

  • SquareSpirus

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

  • XOR

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

Thức ăn mới

  • Hiệu quả của việc lựa chọn màu sắc bởi max(r, g, b)

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

  • Hiệu quả của việc lựa chọn màu sắc bởi min(r, g, b)
  • Lưu ý rằng cái này có chính xác các tính năng / chi tiết giống như ở trên, chỉ với các màu khác nhau! (cùng hạt giống ngẫu nhiên)

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

  • Hiệu quả của việc lựa chọn màu sắc bởi max(r, min(g, b))

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

  • Hiệu ứng chọn màu theo giá trị xám 299*r + 436*g + 114*b

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

  • Hiệu quả của việc lựa chọn màu sắc bởi 1*r + 10*g + 100*b

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

  • Hiệu quả của việc lựa chọn màu sắc bởi 100*r + 10*g + 1*b

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

  • Tai nạn hạnh phúc khi 299*r + 436*g + 114*btràn vào số nguyên 32 bit

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

  • Biến thể 3, với giá trị xám và bộ lập lịch xuyên tâm

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

  • Tôi quên cách tôi tạo ra cái này

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

  • Bộ lập lịch CRT cũng có một lỗi tràn số nguyên hạnh phúc (đã cập nhật ZIP), điều này khiến nó bắt đầu nửa chừng, với hình ảnh 512 × 512, thay vì ở giữa. Đây là những gì nó được cho là trông giống như:

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

  • InverseSpiralScheduler(64) (Mới)

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

  • Một XOR khác

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

  • Kết thúc 4096 thành công đầu tiên sau khi sửa lỗi. Tôi nghĩ rằng đây là phiên bản 3 SpiralScheduler(1)hoặc một cái gì đó

nhập mô tả hình ảnh ở đây (50 MB !!)

  • Phiên bản 1 4096, nhưng tôi vô tình để lại tiêu chí màu sắc trên max()

nhập mô tả hình ảnh ở đây (50 MB !!)

  • 4096, bây giờ với min()
  • Lưu ý rằng cái này có chính xác các tính năng / chi tiết giống như ở trên, chỉ với các màu khác nhau! (cùng hạt giống ngẫu nhiên)
  • Thời gian: quên ghi lại nhưng dấu thời gian của tệp là 3 phút sau hình ảnh trước

nhập mô tả hình ảnh ở đây (50 MB !!)


Mát mẻ. Hình ảnh cuối cùng của bạn tương tự như một ý tưởng thứ hai mà tôi đã đưa ra, mặc dù tôi có cảm giác tôi sẽ không đẹp như thế. BTW, có một cái hay ho tương tự tại allrgb.com/diffusive .
Jason C

Nó chỉ có ý nghĩa như một lời trêu ghẹo, nhưng tôi đã chỉnh sửa nó vì sợ bị gắn cờ, điều này rõ ràng đã xảy ra :)
Mark Jeronimus

2
Ngay cả những tai nạn trông cũng đẹp :). Khối màu có vẻ như là một ý tưởng rất tốt và tốc độ kết xuất của bạn thật tuyệt vời, so với của tôi. Một số thiết kế trên allrgb có một mô tả hay, ví dụ allrgb.com/dla. Tôi ước mình có nhiều thời gian hơn để làm nhiều thí nghiệm hơn, có rất nhiều khả năng ...
fejesjoco

Tôi gần như quên mất, tôi chỉ tải lên một số hình ảnh lớn của tôi. Tôi nghĩ rằng một trong số họ, khói cầu vồng / mực tràn ra, tốt hơn bất cứ thứ gì trên allrgb :). Tôi đồng ý, những người khác không quá ấn tượng, đó là lý do tại sao tôi tạo một video để tạo ra nhiều thứ khác từ họ :).
fejesjoco

Đã thêm mã nguồn và liên kết đến thư viện Digisoft, vì vậy bạn thực sự có thể biên dịch mã của tôi
Mark Jeronimus

72

C ++ w / Qt

Tôi thấy bạn phiên bản:

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

sử dụng phân phối bình thường cho màu sắc:

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

hoặc đầu tiên được sắp xếp theo màu đỏ / màu (với độ lệch nhỏ hơn):

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

hoặc một số bản phân phối khác:

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

Phân phối Cauchy (hsl / đỏ):

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

cols được sắp xếp theo độ sáng (hsl):

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

mã nguồn cập nhật - tạo hình ảnh thứ 6:

int main() {
    const int c = 256*128;
    std::vector<QRgb> data(c);
    QImage image(256, 128, QImage::Format_RGB32);

    std::default_random_engine gen;
    std::normal_distribution<float> dx(0, 2);
    std::normal_distribution<float> dy(0, 1);

    for(int i = 0; i < c; ++i) {
        data[i] = qRgb(i << 3 & 0xF8, i >> 2 & 0xF8, i >> 7 & 0xF8);
    }
    std::sort(data.begin(), data.end(), [] (QRgb a, QRgb b) -> bool {
        return QColor(a).hsvHue() < QColor(b).hsvHue();
    });

    int i = 0;
    while(true) {
        if(i % 10 == 0) { //no need on every iteration
            dx = std::normal_distribution<float>(0, 8 + 3 * i/1000.f);
            dy = std::normal_distribution<float>(0, 4 + 3 * i/1000.f);
        }
        int x = (int) dx(gen);
        int y = (int) dy(gen);
        if(x < 256 && x >= 0 && y >= 0 && y < 128) {
            if(!image.pixel(x, y)) {
                image.setPixel(x, y, data[i]);
                if(i % (c/100) == 1) {
                    std::cout << (int) (100.f*i/c) << "%\n";
                }
                if(++i == c) break;
            }
        }
    }
    image.save("tmp.png");
    return 0;
}

Làm rất tốt Tuy nhiên, có thể không image.pixel(x, y) == 0thất bại và ghi đè lên pixel được đặt đầu tiên?
Mark Jeronimus

@ Zom-B: có thể, nhưng sau đó cái cuối cùng sẽ có màu đen, vì vậy nó nằm trong quy tắc ..
Jaa-c

Không có vấn đề quy tắc mặc dù. Tôi chỉ nghĩ rằng bạn có thể đã bỏ lỡ nó. Cũng có thể đếm từ 1 rồi. Tôi yêu những người khác của bạn!
Mark Jeronimus

@ Zom-B: cảm ơn, tôi có thể thêm một vài thứ nữa, tôi rất thích nó: P
Jaa-c

Người có hai vòng tròn và người bên dưới cùng nhau trông giống như một con khỉ.
Jason C

64

Trong Java:

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;

import javax.imageio.ImageIO;

public class ImgColor {

    private static class Point {
        public int x, y;
        public color c;

        public Point(int x, int y, color c) {
            this.x = x;
            this.y = y;
            this.c = c;
        }
    }

    private static class color {
        char r, g, b;

        public color(int i, int j, int k) {
            r = (char) i;
            g = (char) j;
            b = (char) k;
        }
    }

    public static LinkedList<Point> listFromImg(String path) {
        LinkedList<Point> ret = new LinkedList<>();
        BufferedImage bi = null;
        try {
            bi = ImageIO.read(new File(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int x = 0; x < 4096; x++) {
            for (int y = 0; y < 4096; y++) {
                Color c = new Color(bi.getRGB(x, y));
                ret.add(new Point(x, y, new color(c.getRed(), c.getGreen(), c.getBlue())));
            }
        }
        Collections.shuffle(ret);
        return ret;
    }

    public static LinkedList<color> allColors() {
        LinkedList<color> colors = new LinkedList<>();
        for (int r = 0; r < 256; r++) {
            for (int g = 0; g < 256; g++) {
                for (int b = 0; b < 256; b++) {
                    colors.add(new color(r, g, b));
                }
            }
        }
        Collections.shuffle(colors);
        return colors;
    }

    public static Double cDelta(color a, color b) {
        return Math.pow(a.r - b.r, 2) + Math.pow(a.g - b.g, 2) + Math.pow(a.b - b.b, 2);
    }

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        LinkedList<Point> orig = listFromImg(args[0]);
        LinkedList<color> toDo = allColors();

        Point p = null;
        while (orig.size() > 0 && (p = orig.pop()) != null) {
            color chosen = toDo.pop();
            for (int i = 0; i < Math.min(100, toDo.size()); i++) {
                color c = toDo.pop();
                if (cDelta(c, p.c) < cDelta(chosen, p.c)) {
                    toDo.add(chosen);
                    chosen = c;
                } else {
                    toDo.add(c);
                }
            }
            img.setRGB(p.x, p.y, new Color(chosen.r, chosen.g, chosen.b).getRGB());
        }
        try {
            ImageIO.write(img, "PNG", new File(args[1]));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

và một hình ảnh đầu vào:

vong linh

Tôi tạo ra một cái gì đó như thế này:

axitLemur

phiên bản không nén tại đây: https://www.mediafire.com/?7g3fetvaqhoqgh8

Máy tính của tôi mất khoảng 30 phút để thực hiện một hình ảnh 4096 ^ 2, đây là một cải tiến lớn trong 32 ngày thực hiện đầu tiên của tôi.


1
ouch; 32 ngày không nghe có vẻ buồn cười ..... thuật toán trung bình trong câu trả lời của fejesjocos trên 4k trước khi tối ưu hóa có thể mất nhiều tháng
masterX244

5
Tôi yêu lông mày punk của anh ấy!
Cấp sông St

45

Java với BubbleSort

(thường thì Bubbledort không thích lắm nhưng với thử thách này, cuối cùng nó cũng có cách sử dụng :) tạo ra một dòng với tất cả các yếu tố cách nhau 4096 bước sau đó xáo trộn nó; việc sắp xếp đã đi qua và mỗi lượt thích được thêm 1 vào giá trị của chúng trong khi được sắp xếp để kết quả là bạn có các giá trị được sắp xếp và tất cả các màu

Đã cập nhật Sourcecode để loại bỏ các sọc lớn đó
(cần một số phép thuật bitwise: P)

class Pix
{
    public static void main(String[] devnull) throws Exception
    {
        int chbits=8;
        int colorsperchannel=1<<chbits;
        int xsize=4096,ysize=4096;
        System.out.println(colorsperchannel);
        int[] x = new int[xsize*ysize];//colorstream

        BufferedImage i = new BufferedImage(xsize,ysize, BufferedImage.TYPE_INT_RGB);
        List<Integer> temp = new ArrayList<>();
        for (int j = 0; j < 4096; j++)
        {
            temp.add(4096*j);
        }
        int[] temp2=new int[4096];

        Collections.shuffle(temp,new Random(9263));//intended :P looked for good one
        for (int j = 0; j < temp.size(); j++)
        {
            temp2[j]=(int)(temp.get(j));
        }
        x = spezbubblesort(temp2, 4096);
        int b=-1;
        int b2=-1;
        for (int j = 0; j < x.length; j++)
        {
            if(j%(4096*16)==0)b++;
            if(j%(4096)==0)b2++;
            int h=j/xsize;
            int w=j%xsize;
            i.setRGB(w, h, x[j]&0xFFF000|(b|(b2%16)<<8));
            x[j]=x[j]&0xFFF000|(b|(b2%16)<<8);
        }  

        //validator sorting and checking that all values only have 1 difference
        Arrays.sort(x);
        int diff=0;
        for (int j = 1; j < x.length; j++)
        {
            int ndiff=x[j]-x[j-1];
            if(ndiff!=diff)
            {
                System.out.println(ndiff);
            }
            diff=ndiff;

        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB24.bmp"));
        ImageIO.write(i, "bmp", out);

    }
    public static int[] spezbubblesort(int[] vals,int lines)
    {
        int[] retval=new int[vals.length*lines];
        for (int i = 0; i < lines; i++)
        {
            retval[(i<<12)]=vals[0];
            for (int j = 1; j < vals.length; j++)
            {
                retval[(i<<12)+j]=vals[j];
                if(vals[j]<vals[j-1])
                {

                    int temp=vals[j-1];
                    vals[j-1]=vals[j];
                    vals[j]=temp;
                }
                vals[j-1]=vals[j-1]+1;
            }
            vals[lines-1]=vals[lines-1]+1;
        }
        return retval;
    }
}

Kết quả:

Phiên bản cũ

class Pix
{
    public static void main(String[] devnull) throws Exception
    {
        int[] x = new int[4096*4096];//colorstream
        int idx=0;
        BufferedImage i = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        //GENCODE
        List<Integer> temp = new ArrayList<>();
        for (int j = 0; j < 4096; j++)
        {
            temp.add(4096*j);
        }
        int[] temp2=new int[4096];

        Collections.shuffle(temp,new Random(9263));//intended :P looked for good one
        for (int j = 0; j < temp.size(); j++)
        {
            temp2[j]=(int)(temp.get(j));
        }
        x = spezbubblesort(temp2, 4096);
        for (int j = 0; j < x.length; j++)
        {
            int h=j/4096;
            int w=j%4096;
            i.setRGB(w, h, x[j]);
        }
        //validator sorting and checking that all values only have 1 difference
        Arrays.sort(x);
        int diff=0;
        for (int j = 1; j < x.length; j++)
        {
            int ndiff=x[j]-x[j-1];
            if(ndiff!=diff)
            {
                System.out.println(ndiff);
            }
            diff=ndiff;

        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream("RGB24.bmp"));
        ImageIO.write(i, "bmp", out);
    }
    public static int[] spezbubblesort(int[] vals,int lines)
    {
        int[] retval=new int[vals.length*lines];
        for (int i = 0; i < lines; i++)
        {
            retval[(i<<12)]=vals[0];
            for (int j = 1; j < vals.length; j++)
            {
                retval[(i<<12)+j]=vals[j];
                if(vals[j]<vals[j-1])
                {

                    int temp=vals[j-1];
                    vals[j-1]=vals[j];
                    vals[j]=temp;
                }
                vals[j-1]=vals[j-1]+1;
            }
            vals[lines-1]=vals[lines-1]+1;
        }
        return retval;
    }
}

xem trước đầu ra


Đã có phiên bản QuickSort trên trang allRGB.
Mark Jeronimus

1
@ Zom-B Quicksort là một thuật toán khác với
Bubbledort

43

C

Tạo ra một cơn lốc, vì những lý do tôi không hiểu, với các khung chẵn và lẻ chứa các xoáy hoàn toàn khác nhau.

Đây là bản xem trước của 50 khung hình lẻ đầu tiên:

xem trước xoáy

Hình ảnh mẫu được chuyển đổi từ PPM sang bản phủ màu hoàn chỉnh:

hình ảnh mẫu

Sau này, khi tất cả hòa quyện thành màu xám, bạn vẫn có thể thấy nó quay tròn: chuỗi dài hơn .

Mã như sau. Để chạy, bao gồm số khung, ví dụ:

./vortex 35 > 35.ppm

Tôi đã sử dụng điều này để có được một GIF hoạt hình:

chuyển đổi -delay 10 `ls * .ppm | sắp xếp -n | xargs` -loop 0 vortex.gif
#include <stdlib.h>
#include <stdio.h>

#define W 256
#define H 128

typedef struct {unsigned char r, g, b;} RGB;

int S1(const void *a, const void *b)
{
    const RGB *p = a, *q = b;
    int result = 0;

    if (!result)
        result = (p->b + p->g * 6 + p->r * 3) - (q->b + q->g * 6 + q->r * 3);

    return result;
}

int S2(const void *a, const void *b)
{
    const RGB *p = a, *q = b;
    int result = 0;

    if (!result)
        result = p->b * 6 - p->g;
    if (!result)
        result = p->r - q->r;
    if (!result)
        result = p->g - q->b * 6;

    return result;
}

int main(int argc, char *argv[])
{
    int i, j, n;
    RGB *rgb = malloc(sizeof(RGB) * W * H);
    RGB c[H];

    for (i = 0; i < W * H; i++)
    {
        rgb[i].b = (i & 0x1f) << 3;
        rgb[i].g = ((i >> 5) & 0x1f) << 3;
        rgb[i].r = ((i >> 10) & 0x1f) << 3;
    }

    qsort(rgb, H * W, sizeof(RGB), S1);

    for (n = 0; n < atoi(argv[1]); n++)
    {
        for (i = 0; i < W; i++)
        {
            for (j = 0; j < H; j++)
                c[j] = rgb[j * W + i];
            qsort(c, H, sizeof(RGB), S2);
            for (j = 0; j < H; j++)
                rgb[j * W + i] = c[j];
        }

        for (i = 0; i < W * H; i += W)
            qsort(rgb + i, W, sizeof(RGB), S2);
    }

    printf("P6 %d %d 255\n", W, H);
    fwrite(rgb, sizeof(RGB), W * H, stdout);

    free(rgb);

    return 0;
}

53
Bạn biết đó là C khi điều đó xảy ra vì "lý do tôi không hiểu".
Nit

2
Vâng, thường thì tôi biết những gì sẽ xảy ra, nhưng ở đây tôi chỉ chơi xung quanh để xem những gì tôi có thể nhận được, và chuỗi trật tự không kết thúc trong chuỗi hỗn loạn này đã xuất hiện.

8
Nó xoáy vì hàm so sánh của bạn không tuân theo bất đẳng thức tam giác. Ví dụ: r> b, b> g, g> r. Tôi thậm chí không thể chuyển nó sang Java vì nó hợp nhất dựa vào chính tài sản này, vì vậy tôi nhận được ngoại lệ "Phương pháp so sánh vi phạm hợp đồng chung của nó!"
Mark Jeronimus

2
Tôi sẽ thử p->b * 6 - q->g;nhưng nếu nó phá hỏng dòng xoáy, sẽ không sửa nó!

4
+1 vì lý do tôi không hiểu.
Jason C

40

Java

Biến thể của một bộ chọn màu trong 512x512. Mã thanh lịch thì không , nhưng tôi thích những bức ảnh đẹp:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;

public class EighteenBitColors {

    static boolean shuffle_block = false;
    static int shuffle_radius = 0;

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
        for(int r=0;r<64;r++)
            for(int g=0;g<64;g++)
                for(int b=0;b<64;b++)
                    img.setRGB((r * 8) + (b / 8), (g * 8) + (b % 8), ((r * 4) << 8 | (g * 4)) << 8 | (b * 4));

        if(shuffle_block)
            blockShuffle(img);
        else
            shuffle(img, shuffle_radius);

        try {           
            ImageIO.write(img, "png", new File(getFileName()));
        } catch(IOException e){
            System.out.println("suck it");
        }
    }

    public static void shuffle(BufferedImage img, int radius){
        if(radius < 1)
            return;
        int width = img.getWidth();
        int height = img.getHeight();
        Random rand = new Random();
        for(int x=0;x<512;x++){
            for(int y=0;y<512;y++){
                int xx = -1;
                int yy = -1;
                while(xx < 0 || xx >= width){
                    xx = x + rand.nextInt(radius*2+1) - radius;
                }
                while(yy < 0 || yy >= height){
                    yy = y + rand.nextInt(radius*2+1) - radius;
                }
                int tmp = img.getRGB(xx, yy);
                img.setRGB(xx, yy, img.getRGB(x, y));
                img.setRGB(x,y,tmp);
            }
        }
    }

    public static void blockShuffle(BufferedImage img){
        int tmp;
        Random rand = new Random();
        for(int bx=0;bx<8;bx++){
            for(int by=0;by<8;by++){
                for(int x=0;x<64;x++){
                    for(int y=0;y<64;y++){
                        int xx = bx*64+x;
                        int yy = by*64+y;
                        int xxx = bx*64+rand.nextInt(64);
                        int yyy = by*64+rand.nextInt(64);
                        tmp = img.getRGB(xxx, yyy);
                        img.setRGB(xxx, yyy, img.getRGB(xx, yy));
                        img.setRGB(xx,yy,tmp);
                    }
                }
            }
        }
    }

    public static String getFileName(){
        String fileName = "allrgb_";
        if(shuffle_block){
            fileName += "block";
        } else if(shuffle_radius > 0){
            fileName += "radius_" + shuffle_radius;
        } else {
            fileName += "no_shuffle";
        }
        return fileName + ".png";
    }
}

Như đã viết, nó xuất ra:

không xáo trộn

Nếu bạn chạy nó shuffle_block = true, nó sẽ xáo trộn các màu trong mỗi khối 64x64:

khối xáo trộn

Khác, nếu bạn chạy nó shuffle_radius > 0, nó xáo trộn từng pixel với một pixel ngẫu nhiên shuffle_radiustrong x / y. Sau khi chơi với nhiều kích cỡ khác nhau, tôi thích bán kính 32 pixel, vì nó làm mờ các đường mà không di chuyển các thứ xung quanh quá nhiều:

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


3
ooh những bức ảnh này là đẹp nhất
Sevenseacat

Chúng thật sự rất tuyệt
Matthew

37

Chế biến

Tôi mới bắt đầu với C (đã lập trình bằng các ngôn ngữ khác) nhưng thấy đồ họa trong Visual C khó theo dõi, vì vậy tôi đã tải xuống chương trình Xử lý này được sử dụng bởi @ace.

Đây là mã của tôi và thuật toán của tôi.

void setup(){
  size(256,128);
  background(0);
  frameRate(1000000000);
  noLoop();
 }

int x,y,r,g,b,c;
void draw() {
  for(y=0;y<128;y++)for(x=0;x<128;x++){
    r=(x&3)+(y&3)*4;
    g=x>>2;
    b=y>>2;
    c=0;
    //c=x*x+y*y<10000? 1:0; 
    stroke((r^16*c)<<3,g<<3,b<<3);
    point(x,y);
    stroke((r^16*(1-c))<<3,g<<3,b<<3);
    point(255-x,y);  
  } 
}

Thuật toán

Bắt đầu với các ô vuông 4 x 4 của tất cả các kết hợp có thể có của 32 giá trị xanh lục và xanh lam, theo x, y. định dạng, tạo một hình vuông 128x128 Mỗi hình vuông 4 x 4 có 16 pixel, vì vậy hãy tạo một hình ảnh phản chiếu bên cạnh nó để cung cấp 32 pixel cho mỗi kết hợp có thể có của màu xanh lá cây và màu xanh lam, cho mỗi hình ảnh bên dưới.

(kỳ lạ là màu xanh lá cây đầy đủ trông sáng hơn màu lục lam hoàn toàn. Đây phải là ảo ảnh quang học. được làm rõ trong các bình luận)

Trong hình vuông bên trái, thêm các giá trị màu đỏ 0-15. Đối với hình vuông bên phải, XOR các giá trị này với 16, để tạo các giá trị 16-31.

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

Đầu ra 256x128

Điều này cho đầu ra trong hình ảnh trên cùng dưới đây.

Tuy nhiên, mọi pixel khác với hình ảnh phản chiếu của nó chỉ ở bit đáng kể nhất của giá trị màu đỏ. Vì vậy, tôi có thể áp dụng một điều kiện với biến c, để đảo ngược XOR, có tác dụng tương tự như trao đổi hai pixel này.

Một ví dụ về điều này được đưa ra trong hình dưới cùng (nếu chúng ta bỏ qua dòng mã hiện đang được nhận xét.)

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

512 x 512 - Cống hiến cho Marylin của Andy Warhol

Lấy cảm hứng từ câu trả lời của Quincunx cho câu hỏi này với một "nụ cười tà ác" trong các vòng tròn đỏ tự do, đây là phiên bản của bức tranh nổi tiếng của tôi. Bản gốc thực sự có 25 Marylins màu và 25 Marylins đen trắng và là sự tôn vinh của Warhol đối với Marylin sau cái chết không tưởng của cô. Xem http://en.wikipedia.org/wiki/Mallyn_Diptych

Tôi đã thay đổi các chức năng khác nhau sau khi phát hiện ra rằng Xử lý kết xuất các chức năng mà tôi đã sử dụng trong 256x128 dưới dạng bán trong suốt. Những cái mới mờ đục.

Và mặc dù hình ảnh không hoàn toàn là thuật toán, tôi thích nó.

int x,y,r,g,b,c;
PImage img;
color p;
void setup(){
  size(512,512);
  background(0);
  img = loadImage("marylin256.png");
  frameRate(1000000000);
  noLoop();
 }

void draw() {

   image(img,0,0);

   for(y=0;y<256;y++)for(x=0;x<256;x++){
      // Note the multiplication by 0 in the next line. 
      // Replace the 0 with an 8 and the reds are blended checkerboard style
      // This reduces the grain size, but on balance I decided I like the grain.
      r=((x&3)+(y&3)*4)^0*((x&1)^(y&1));
      g=x>>2;
      b=y>>2; 
      c=brightness(get(x,y))>100? 32:0;
      p=color((r^c)<<2,g<<2,b<<2);
      set(x,y,p);
      p=color((r^16^c)<<2,g<<2,b<<2);
      set(256+x,y,p);  
      p=color((r^32^c)<<2,g<<2,b<<2);
      set(x,256+y,p);
      p=color((r^48^c)<<2,g<<2,b<<2);
      set(256+x,256+y,p);  
 } 
 save("warholmarylin.png");

}

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

512x512 Chạng vạng trên một hồ nước với những ngọn núi ở phía xa

Ở đây, một bức tranh đầy đủ thuật toán. Tôi đã chơi xung quanh với việc thay đổi màu nào tôi điều chỉnh với điều kiện, nhưng tôi chỉ quay lại kết luận rằng màu đỏ hoạt động tốt nhất. Tương tự như ảnh Marylin, tôi vẽ các ngọn núi trước, sau đó chọn độ sáng từ ảnh đó để ghi đè lên hình ảnh RGB dương, trong khi sao chép sang nửa âm. Một sự khác biệt nhỏ là đáy của nhiều ngọn núi (vì tất cả chúng đều được vẽ cùng kích thước) kéo dài bên dưới khu vực đọc, do đó, khu vực này chỉ bị cắt trong quá trình đọc (do đó mang lại ấn tượng mong muốn về các ngọn núi có kích thước khác nhau. )

Trong phần này, tôi sử dụng một ô 8x4 gồm 32 màu đỏ cho phần dương và 32 màu đỏ còn lại cho phần âm.

Lưu ý lệnh expRate frameRate (1) ở cuối mã của tôi. Tôi phát hiện ra rằng nếu không có lệnh này, Xử lý sẽ sử dụng 100% một lõi CPU của tôi, mặc dù nó đã vẽ xong. Theo như tôi có thể nói là không có chức năng Ngủ, tất cả những gì bạn có thể làm là giảm tần suất bỏ phiếu.

int i,j,x,y,r,g,b,c;
PImage img;
color p;
void setup(){
  size(512,512);
  background(255,255,255);
  frameRate(1000000000);
  noLoop();
 }

void draw() {
  for(i=0; i<40; i++){
    x=round(random(512));
    y=round(random(64,256));
    for(j=-256; j<256; j+=12) line(x,y,x+j,y+256);  
  }
  for(y=0;y<256;y++)for(x=0;x<512;x++){
    r=(x&7)+(y&3)*8;
    b=x>>3;
    g=(255-y)>>2;
    c=brightness(get(x,y))>100? 32:0;
    p=color((r^c)<<2,g<<2,b<<2);
    set(x,y,p);
    p=color((r^32^c)<<2,g<<2,b<<2);
    set(x,511-y,p);  
  }
  save("mountainK.png");
  frameRate(1);
}

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


Bởi vì nó không hoàn toàn màu lục lam. Đó là (0,217,217). Tất cả 32 kết hợp đều có mặt, chỉ không kéo dài [0,255]. Chỉnh sửa: Bạn đang sử dụng các bước của 7 nhưng tôi không thể tìm thấy mã này trong mã. Phải là một điều chế biến.
Mark Jeronimus

@steveverrill Trong Đang xử lý, bạn có thể làm save("filename.png")để lưu bộ đệm khung hiện tại vào hình ảnh. Các định dạng hình ảnh khác cũng được hỗ trợ. Nó sẽ giúp bạn tránh những rắc rối khi chụp ảnh màn hình. Hình ảnh được lưu vào thư mục của bản phác thảo.
Jason C

@Jasonc cảm ơn vì tiền boa, tôi chắc chắn phải có cách, nhưng tôi không nghĩ mình sẽ chỉnh sửa những thứ này. Tôi đã để khung xung quanh các hình ảnh một phần để tách chúng ra (2 tệp cho những hình ảnh nhỏ như vậy là quá mức cần thiết.) Tôi muốn thực hiện một số hình ảnh trong 512x512 (và có một ý tưởng cụ thể tôi có) vì vậy tôi sẽ tải chúng lên bạn đề nghị.
Cấp sông St

1
@steveverrill Haha, Warhols là một liên lạc tốt đẹp.
Jason C

@ Zom-B Xử lý dường như thực hiện nhiều điều (khó chịu) không được đề cập trong tài liệu của nó: không sử dụng đầy đủ 256 giá trị kênh màu logic trong đầu ra vật lý của nó, pha trộn màu sắc khi bạn không muốn, sử dụng lõi đầy đủ của CPU của tôi ngay cả sau khi vẽ xong. Vẫn đơn giản để đi vào và bạn có thể giải quyết những vấn đề này một khi bạn biết chúng ở đó (ngoại trừ vấn đề đầu tiên, tôi chưa giải quyết được điều đó ...)
Level River St

35

Tôi chỉ sắp xếp tất cả các màu 16 bit (5r, 6g, 5b) trên một đường cong Hilbert trong JavaScript.

màu đường cong hilbert

Hình ảnh trước đây (không phải đường cong Hilbert):

đường cong hilbert

Mã thông báo: jsfiddle.net/LCsLQ/3

JavaScript

// ported code from http://en.wikipedia.org/wiki/Hilbert_curve
function xy2d (n, p) {
    p = {x: p.x, y: p.y};
    var r = {x: 0, y: 0},
        s,
        d=0;
    for (s=(n/2)|0; s>0; s=(s/2)|0) {
        r.x = (p.x & s) > 0 ? 1 : 0;
        r.y = (p.y & s) > 0 ? 1 : 0;
        d += s * s * ((3 * r.x) ^ r.y);
        rot(s, p, r);
    }
    return d;
}

//convert d to (x,y)
function d2xy(n, d) {
    var r = {x: 0, y: 0},
        p = {x: 0, y: 0},
        s,
        t=d;
    for (s=1; s<n; s*=2) {
        r.x = 1 & (t/2);
        r.y = 1 & (t ^ rx);
        rot(s, p, r);
        p.x += s * r.x;
        p.y += s * r.y;
        t /= 4;
    }
    return p;
}

//rotate/flip a quadrant appropriately
function rot(n, p, r) {
    if (r.y === 0) {
        if (r.x === 1) {
            p.x = n-1 - p.x;
            p.y = n-1 - p.y;
        }

        //Swap x and y
        var t  = p.x;
        p.x = p.y;
        p.y = t;
    }
}
function v2rgb(v) {
    return ((v & 0xf800) << 8) | ((v & 0x7e0) << 5) | ((v & 0x1f) << 3); 
}
function putData(arr, size, coord, v) {
    var pos = (coord.x + size * coord.y) * 4,
        rgb = v2rgb(v);

    arr[pos] = (rgb & 0xff0000) >> 16;
    arr[pos + 1] = (rgb & 0xff00) >> 8;
    arr[pos + 2] = rgb & 0xff;
    arr[pos + 3] = 0xff;
}
var size = 256,
    context = a.getContext('2d'),
    data = context.getImageData(0, 0, size, size);

for (var i = 0; i < size; i++) {
    for (var j = 0; j < size; j++) {
        var p = {x: j, y: i};
        putData(data.data, size, p, xy2d(size, p));
    }
}
context.putImageData(data, 0, 0);

Chỉnh sửa : Hóa ra có một lỗi trong hàm của tôi để tính đường cong Hilbert và nó không chính xác; cụ thể là r.x = (p.x & s) > 0; r.y = (p.y & s) > 0;đổi thànhr.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

Chỉnh sửa 2: Một fractal khác:

sierpinky

http://jsfiddle.net/jej2d/5/


Đẹp! Chào mừng đến với PPCG.
Jonathan Van Matre

Nó trông như thế nào khi đi bộ qua khối màu cũng là một đường cong Hilbert 3D? Chỉnh sửa bước sóng . ai đó đã làm điều đó
Mark Jeronimus

35

C #: Tối ưu hóa tương tự cục bộ lặp

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace AllColors
{
    class Program
    {
        static Random _random = new Random();

        const int ImageWidth = 256;
        const int ImageHeight = 128;
        const int PixelCount = ImageWidth * ImageHeight;
        const int ValuesPerChannel = 32;
        const int ChannelValueDelta = 256 / ValuesPerChannel;

        static readonly int[,] Kernel;
        static readonly int KernelWidth;
        static readonly int KernelHeight;

        static Program()
        {
            // Version 1
            Kernel = new int[,] { { 0, 1, 0, },
                                  { 1, 0, 1, },
                                  { 0, 1, 0, } };
            // Version 2
            //Kernel = new int[,] { { 0, 0, 1, 0, 0 },
            //                      { 0, 2, 3, 2, 0 },
            //                      { 1, 3, 0, 3, 1 },
            //                      { 0, 2, 3, 2, 0 },
            //                      { 0, 0, 1, 0, 0 } };
            // Version 3
            //Kernel = new int[,] { { 3, 0, 0, 0, 3 },
            //                      { 0, 1, 0, 1, 0 },
            //                      { 0, 0, 0, 0, 0 },
            //                      { 0, 1, 0, 1, 0 },
            //                      { 3, 0, 0, 0, 3 } };
            // Version 4
            //Kernel = new int[,] { { -9, -9, -9, -9, -9 },
            //                      {  1,  2,  3,  2,  1 },
            //                      {  2,  3,  0,  3,  2 },
            //                      {  1,  2,  3,  2,  1 },
            //                      {  0,  0,  0,  0,  0 } };
            // Version 5
            //Kernel = new int[,] { { 0, 0, 1, 0, 0, 0, 0 },
            //                      { 0, 1, 2, 1, 0, 0, 0 },
            //                      { 1, 2, 3, 0, 1, 0, 0 },
            //                      { 0, 1, 2, 0, 0, 0, 0 },
            //                      { 0, 0, 1, 0, 0, 0, 0 } };
            KernelWidth = Kernel.GetLength(1);
            KernelHeight = Kernel.GetLength(0);

            if (KernelWidth % 2 == 0 || KernelHeight % 2 == 0)
            {
                throw new InvalidOperationException("Invalid kernel size");
            }
        }

        private static Color[] CreateAllColors()
        {
            int i = 0;
            Color[] colors = new Color[PixelCount];
            for (int r = 0; r < ValuesPerChannel; r++)
            {
                for (int g = 0; g < ValuesPerChannel; g++)
                {
                    for (int b = 0; b < ValuesPerChannel; b++)
                    {
                        colors[i] = Color.FromArgb(255, r * ChannelValueDelta, g * ChannelValueDelta, b * ChannelValueDelta);
                        i++;
                    }
                }
            }
            return colors;
        }

        private static void Shuffle(Color[] colors)
        {
            // Knuth-Fisher-Yates shuffle
            for (int i = colors.Length - 1; i > 0; i--)
            {
                int n = _random.Next(i + 1);
                Swap(colors, i, n);
            }
        }

        private static void Swap(Color[] colors, int index1, int index2)
        {
            var temp = colors[index1];
            colors[index1] = colors[index2];
            colors[index2] = temp;
        }

        private static Bitmap ToBitmap(Color[] pixels)
        {
            Bitmap bitmap = new Bitmap(ImageWidth, ImageHeight);
            int x = 0;
            int y = 0;
            for (int i = 0; i < PixelCount; i++)
            {
                bitmap.SetPixel(x, y, pixels[i]);
                x++;
                if (x == ImageWidth)
                {
                    x = 0;
                    y++;
                }
            }
            return bitmap;
        }

        private static int GetNeighborDelta(Color[] pixels, int index1, int index2)
        {
            return GetNeighborDelta(pixels, index1) + GetNeighborDelta(pixels, index2);
        }

        private static int GetNeighborDelta(Color[] pixels, int index)
        {
            Color center = pixels[index];
            int sum = 0;
            for (int x = 0; x < KernelWidth; x++)
            {
                for (int y = 0; y < KernelHeight; y++)
                {
                    int weight = Kernel[y, x];
                    if (weight == 0)
                    {
                        continue;
                    }

                    int xOffset = x - (KernelWidth / 2);
                    int yOffset = y - (KernelHeight / 2);
                    int i = index + xOffset + yOffset * ImageWidth;

                    if (i >= 0 && i < PixelCount)
                    {
                        sum += GetDelta(pixels[i], center) * weight;
                    }
                }
            }

            return sum;
        }

        private static int GetDelta(Color c1, Color c2)
        {
            int sum = 0;
            sum += Math.Abs(c1.R - c2.R);
            sum += Math.Abs(c1.G - c2.G);
            sum += Math.Abs(c1.B - c2.B);
            return sum;
        }

        private static bool TryRandomSwap(Color[] pixels)
        {
            int index1 = _random.Next(PixelCount);
            int index2 = _random.Next(PixelCount);

            int delta = GetNeighborDelta(pixels, index1, index2);
            Swap(pixels, index1, index2);
            int newDelta = GetNeighborDelta(pixels, index1, index2);

            if (newDelta < delta)
            {
                return true;
            }
            else
            {
                // Swap back
                Swap(pixels, index1, index2);
                return false;
            }
        }

        static void Main(string[] args)
        {
            string fileNameFormat = "{0:D10}.png";
            var image = CreateAllColors();
            ToBitmap(image).Save("start.png");
            Shuffle(image);
            ToBitmap(image).Save(string.Format(fileNameFormat, 0));

            long generation = 0;
            while (true)
            {
                bool swapped = TryRandomSwap(image);
                if (swapped)
                {
                    generation++;
                    if (generation % 1000 == 0)
                    {
                        ToBitmap(image).Save(string.Format(fileNameFormat, generation));
                    }
                }
            }
        }
    }
}

Ý tưởng

Đầu tiên chúng ta bắt đầu với một shuffle ngẫu nhiên:

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

Sau đó, chúng tôi chọn ngẫu nhiên hai pixel và trao đổi chúng. Nếu điều này không làm tăng sự tương đồng của các pixel với hàng xóm của chúng, chúng tôi trao đổi lại và thử lại. Chúng tôi lặp lại quá trình này nhiều lần.

Chỉ sau một vài thế hệ (5000), sự khác biệt không rõ ràng ...

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

Nhưng nó càng chạy lâu hơn (25000), ...

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

... Các mẫu nhất định bắt đầu xuất hiện (100000).

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

Sử dụng các định nghĩa khác nhau cho vùng lân cận , chúng ta có thể ảnh hưởng đến các mẫu này và liệu chúng có ổn định hay không. Đây Kernellà một ma trận tương tự như ma trận được sử dụng cho các bộ lọc trong xử lý ảnh . Nó chỉ định các trọng số của từng lân cận được sử dụng để tính toán đồng bằng RGB.

Các kết quả

Dưới đây là một số kết quả tôi tạo ra. Các video cho thấy quá trình lặp lại (1 khung == 1000 thế hệ), nhưng đáng buồn là chất lượng không phải là tốt nhất (vimeo, YouTube, v.v. không hỗ trợ đúng kích thước nhỏ như vậy). Sau này tôi có thể cố gắng tạo ra những video có chất lượng tốt hơn.

0 1 0
1 X 1
0 1 0

185000 thế hệ:

nhập mô tả hình ảnh ở đây Video (00:06)


0 0 1 0 0
0 2 3 2 0
1 3 X 3 1
0 2 3 2 0
0 0 1 0 0

243000 thế hệ:

nhập mô tả hình ảnh ở đây Video (00:07)


3 0 0 0 3
0 1 0 1 0
0 0 X 0 0
0 1 0 1 0
3 0 0 0 3

230000 thế hệ:

nhập mô tả hình ảnh ở đây Video (00:07)


0 0 1 0 0 0 0
0 1 2 1 0 0 0
1 2 3 X 1 0 0
0 1 2 0 0 0 0
0 0 1 0 0 0 0

Hạt nhân này là thú vị bởi vì sự bất đối xứng của nó, các mẫu không ổn định và toàn bộ hình ảnh di chuyển sang bên phải khi các thế hệ đi qua.

2331000 thế hệ:

nhập mô tả hình ảnh ở đây Video (01:10)


Kết quả lớn (512x512)

Sử dụng các hạt nhân ở trên với kích thước hình ảnh lớn hơn sẽ tạo ra các mẫu cục bộ giống nhau, trải rộng trên tổng diện tích lớn hơn. Một hình ảnh 512x512 mất từ ​​1 đến 2 triệu thế hệ để ổn định.

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


OK, bây giờ chúng ta hãy nghiêm túc và tạo các mẫu lớn hơn, ít cục bộ hơn với hạt nhân xuyên tâm 15x15:

0 0 0 0 0 1 1 1 1 1 0 0 0 0 0
0 0 0 1 1 2 2 2 2 2 1 1 0 0 0
0 0 1 2 2 3 3 3 3 3 2 2 1 0 0
0 1 2 2 3 4 4 4 4 4 3 2 2 1 0
0 1 2 3 4 4 5 5 5 4 4 3 2 1 0
1 2 3 4 4 5 6 6 6 5 4 4 3 2 1
1 2 3 4 5 6 7 7 7 6 5 4 3 2 1
1 2 3 4 5 6 7 X 7 6 5 4 3 2 1
1 2 3 4 5 6 7 7 7 6 5 4 3 2 1
1 2 3 4 4 5 6 6 6 5 4 4 3 2 1
0 1 2 3 4 4 5 5 5 4 4 3 2 1 0
0 1 2 2 3 4 4 4 4 4 3 2 2 1 0
0 0 1 2 2 3 3 3 3 3 2 2 1 0 0
0 0 0 1 1 2 2 2 2 2 1 1 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0

Điều này làm tăng đáng kể thời gian tính toán trên mỗi thế hệ. 1,71 triệu thế hệ và 20 giờ sau:

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


1
Mất một lúc để đến đó, nhưng kết quả cuối cùng khá tốt đẹp.
Primo

Sự trùng hợp thú vị, tôi có một bài viết về cùng chủ đề này: nayuki.io/page/simulation-anneals-demo
Nayuki

30

Java

Với một vài biến thể trong câu trả lời khác của tôi, chúng ta có thể nhận được một số kết quả đầu ra rất thú vị.

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);

        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096; x++) {
                points.add(new Point(x, y));
            }
        }
        Collections.sort(points, new Comparator<Point>() {

            @Override
            public int compare(Point t, Point t1) {
                int compareVal = (Integer.bitCount(t.x) + Integer.bitCount(t.y))
                        - (Integer.bitCount(t1.x) + Integer.bitCount(t1.y));
                return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
            }

        });
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Mã quan trọng là đây:

Collections.sort(points, new Comparator<Point>() {

    @Override
    public int compare(Point t, Point t1) {
        int compareVal = (Integer.bitCount(t.x) + Integer.bitCount(t.y))
                - (Integer.bitCount(t1.x) + Integer.bitCount(t1.y));
        return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
    }

});

Đầu ra (ảnh chụp màn hình):

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

Thay đổi bộ so sánh này:

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x + t.y))
            - (Integer.bitCount(t1.x + t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

Và chúng tôi nhận được điều này:

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

Một biến thể khác:

public int compare(Point t, Point t1) {
    int compareVal = (t.x + t.y)
            - (t1.x + t1.y);
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

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

Một biến thể khác (nhắc nhở tôi về automata di động):

public int compare(Point t, Point t1) {
    int compareVal = (t1.x - t.y)
            + (t.x - t1.y);
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

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

Một biến thể khác (yêu thích cá nhân mới):

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x ^ t.y))
            - (Integer.bitCount(t1.x ^ t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

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

Trông nó rất dễ thương. XOR rất đẹp, đặc biệt là chụp gần:

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

Cận cảnh khác:

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

Và bây giờ là Tam giác Sierpinki, nghiêng:

public int compare(Point t, Point t1) {
    int compareVal = (Integer.bitCount(t.x | t.y))
            - (Integer.bitCount(t1.x | t1.y));
    return compareVal < 0 ? -1 : compareVal == 0 ? 0 : 1;
}

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


8
Hình ảnh đầu tiên trông giống như một bức ảnh chết CPU hoặc bộ nhớ
Nick T

@NickT Đây là một bộ nhớ chết (theo Google Images) cho độ tương phản: files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg
Justin

4
Đúng rồi, bộ nhớ quá vô dụng ... có lẽ là bộ xử lý rất đa lõi rồi: extremetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg
Nick T

1
Tôi thực sự thích những cái sau này. Rất rối mắt nhưng với một cấu trúc tổ chức cơ bản. Tôi muốn một tấm thảm dệt như thiết kế XOR đó!
Jonathan Van Matre

Đây là thực sự mát mẻ; họ gợi cho tôi nhớ về một game arcade bị hỏng hoặc một nes.
Jason C

29

Java

Tôi thực sự không chắc chắn làm thế nào để tạo ra các màu 15 hoặc 18 bit, vì vậy tôi chỉ bỏ đi một chút ít quan trọng nhất của mỗi byte kênh để tạo ra 2 ^ 18 màu 24 bit khác nhau. Hầu hết các nhiễu được loại bỏ bằng cách sắp xếp, nhưng loại bỏ nhiễu hiệu quả có vẻ như nó sẽ yêu cầu so sánh nhiều hơn chỉ hai yếu tố tại một thời điểm theo cách so sánh. Tôi sẽ thử thao tác bằng hạt nhân lớn hơn, nhưng trong lúc này, đây là điều tốt nhất tôi có thể làm.

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

Nhấp để xem hình ảnh HD # 2

Hình ảnh độ phân giải thấp # 2

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.*;

public class ColorSpan extends JFrame{
    private int h, w = h = 512;
    private BufferedImage image = 
            new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
    private WritableRaster raster = image.getRaster();
    private DataBufferInt dbInt = (DataBufferInt) 
            (raster.getDataBuffer());
    private int[] data = dbInt.getData();

    private JLabel imageLabel = new JLabel(new ImageIcon(image));
    private JPanel bordered = new JPanel(new BorderLayout());


    public <T> void transpose(ArrayList<T> objects){
        for(int i = 0; i < w; i++){
            for(int j = 0; j < i; j++){
                Collections.swap(objects,i+j*w,j+i*h);
            }
        }
    }

    public <T> void sortByLine(ArrayList<T> objects, Comparator<T> comp){
        for(int i = 0; i < h; i++){
            Collections.sort(objects.subList(i*w, (i+1)*w), comp);
        }
    }

    public void init(){
        ArrayList<Integer> colors = new ArrayList<Integer>();
        for(int i = 0, max = 1<<18; i < max; i++){
            int r = i>>12, g = (i>>6)&63, b = i&63;
            colors.add(((r<<16)+(g<<8)+b)<<2);
        }

        Comparator<Integer> comp1 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255;
                /*double thA = Math.acos(gA*2d/255-1),
                        thB = Math.acos(gB*2d/255-1);*/
                double thA = Math.atan2(rA/255d-.5,gA/255d-.5),
                        thB = Math.atan2(rB/255d-.5,gB/255d-.5);
                return -Double.compare(thA,thB);
            }
        }, comp2 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255,
                    bA = a&255, bB = b&255;
                double dA = Math.hypot(gA-rA,bA-rA),
                        dB = Math.hypot(gB-rB,bB-rB);
                return Double.compare(dA,dB);
            }
        }, comp3 = new Comparator<Integer>(){
            public int compare(Integer left, Integer right){
                int a = left.intValue(), b = right.intValue();

                int rA = a>>16, rB = b>>16,
                    gA = (a>>8)&255, gB = (b>>8)&255,
                    bA = a&255, bB = b&255;

                    return Integer.compare(rA+gA+bA,rB+gB+bB);
            }
        };

        /* Start: Image 1 */
        Collections.sort(colors, comp2);
        transpose(colors);
        sortByLine(colors,comp2);
        transpose(colors);
        sortByLine(colors,comp1);
        transpose(colors);
        sortByLine(colors,comp2);
        sortByLine(colors,comp3);
        /* End: Image 1 */

        /* Start: Image 2 */
        Collections.sort(colors, comp1);
        sortByLine(colors,comp2);

        transpose(colors);
        sortByLine(colors,comp2);
        transpose(colors);
        sortByLine(colors,comp1);
        transpose(colors);
        sortByLine(colors,comp1);
        /* End: Image 2 */

        int index = 0;
        for(Integer color : colors){
            int cInt = color.intValue();
            data[index] = cInt;
            index++;
        }

    }

    public ColorSpan(){
        super("512x512 Unique Colors");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        init();

        bordered.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
        bordered.add(imageLabel,BorderLayout.CENTER);
        add(bordered,BorderLayout.CENTER);
        pack();

    }

    public static void main(String[] args){
        new ColorSpan().setVisible(true);
    }
}

1
Cái thứ hai đó thực sự xứng đáng để có phiên bản 4096 x 4096 24bit ...
trichoplax

Imgur đã xử lý hình ảnh trong khoảng nửa giờ. Tôi đoán có lẽ nó đang cố nén nó. Dù sao, tôi đã thêm một liên kết: SSend.it/hj4ovh
John P

2
Có vấn đề với việc tải xuống.
SuperJedi224

28

Scala

Tôi đặt hàng tất cả các màu bằng cách đi theo Đường cong Hilbert 3 chiều thông qua Hệ thống L . Sau đó, tôi đi bộ các pixel trong hình ảnh đầu ra dọc theo Đường cong Hilbert 2 chiều và bố trí tất cả các màu.

Đầu ra 512 x 512:

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

Đây là mã. Hầu hết nó chỉ bao gồm logic và toán học di chuyển qua ba chiều thông qua cao độ / cuộn / ngáp. Tôi chắc chắn có một cách tốt hơn để làm phần đó, nhưng ồ.

import scala.annotation.tailrec
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.File

object AllColors {

  case class Vector(val x: Int, val y: Int, val z: Int) {
    def applyTransformation(m: Matrix): Vector = {
      Vector(m.r1.x * x + m.r1.y * y + m.r1.z * z, m.r2.x * x + m.r2.y * y + m.r2.z * z, m.r3.x * x + m.r3.y * y + m.r3.z * z)
    }
    def +(v: Vector): Vector = {
      Vector(x + v.x, y + v.y, z + v.z)
    }
    def unary_-(): Vector = Vector(-x, -y, -z)
  }

  case class Heading(d: Vector, s: Vector) {
    def roll(positive: Boolean): Heading = {
      val (axis, b) = getAxis(d)
      Heading(d, s.applyTransformation(rotationAbout(axis, !(positive ^ b))))
    }

    def yaw(positive: Boolean): Heading = {
      val (axis, b) = getAxis(s)
      Heading(d.applyTransformation(rotationAbout(axis, positive ^ b)), s)
    }

    def pitch(positive: Boolean): Heading = {
      if (positive) {
        Heading(s, -d)
      } else {
        Heading(-s, d)
      }
    }

    def applyCommand(c: Char): Heading = c match {
      case '+' => yaw(true)
      case '-' => yaw(false)
      case '^' => pitch(true)
      case 'v' => pitch(false)
      case '>' => roll(true)
      case '<' => roll(false)
    }
  }

  def getAxis(v: Vector): (Char, Boolean) = v match {
    case Vector(1, 0, 0) => ('x', true)
    case Vector(-1, 0, 0) => ('x', false)
    case Vector(0, 1, 0) => ('y', true)
    case Vector(0, -1, 0) => ('y', false)
    case Vector(0, 0, 1) => ('z', true)
    case Vector(0, 0, -1) => ('z', false)
  }

  def rotationAbout(axis: Char, positive: Boolean) = (axis, positive) match {
    case ('x', true) => XP
    case ('x', false) => XN
    case ('y', true) => YP
    case ('y', false) => YN
    case ('z', true) => ZP
    case ('z', false) => ZN
  }

  case class Matrix(val r1: Vector, val r2: Vector, val r3: Vector)

  val ZP = Matrix(Vector(0,-1,0),Vector(1,0,0),Vector(0,0,1))
  val ZN = Matrix(Vector(0,1,0),Vector(-1,0,0),Vector(0,0,1))

  val XP = Matrix(Vector(1,0,0),Vector(0,0,-1),Vector(0,1,0))
  val XN = Matrix(Vector(1,0,0),Vector(0,0,1),Vector(0,-1,0))

  val YP = Matrix(Vector(0,0,1),Vector(0,1,0),Vector(-1,0,0))
  val YN = Matrix(Vector(0,0,-1),Vector(0,1,0),Vector(1,0,0))

  @tailrec def applyLSystem(current: Stream[Char], rules: Map[Char, List[Char]], iterations: Int): Stream[Char] = {
    if (iterations == 0) {
      current
    } else {
      val nextStep = current flatMap { c => rules.getOrElse(c, List(c)) }
      applyLSystem(nextStep, rules, iterations - 1)
    }
  }

  def walk(x: Vector, h: Heading, steps: Stream[Char]): Stream[Vector] = steps match {
    case Stream() => Stream(x)
    case 'f' #:: rest => x #:: walk(x + h.d, h, rest)
    case c #:: rest => walk(x, h.applyCommand(c), rest)
  }

  def hilbert3d(n: Int): Stream[Vector] = {
    val rules = Map('x' -> "^>x<f+>>x<<f>>x<<+fvxfxvf+>>x<<f>>x<<+f>x<^".toList)
    val steps = applyLSystem(Stream('x'), rules, n) filterNot (_ == 'x')
    walk(Vector(0, 0, 0), Heading(Vector(1, 0, 0), Vector(0, 1, 0)), steps)
  }

  def hilbert2d(n: Int): Stream[Vector] = {
    val rules = Map('a' -> "-bf+afa+fb-".toList, 'b' -> "+af-bfb-fa+".toList)
    val steps = applyLSystem(Stream('a'), rules, n) filterNot (c => c == 'a' || c == 'b')
    walk(Vector(0, 0, 0), Heading(Vector(1, 0, 0), Vector(0, 0, 1)), steps)
  }

  def main(args: Array[String]): Unit = {
    val n = 4
    val img = new BufferedImage(1 << (3 * n), 1 << (3 * n), BufferedImage.TYPE_INT_RGB)
    hilbert3d(n * 2).zip(hilbert2d(n * 3)) foreach { case (Vector(r,g,b), Vector(x,y,_)) => img.setRGB(x, y, (r << (24 - 2 * n)) | (g << (16 - 2 * n)) | (b << (8 - 2 * n))) }
    ImageIO.write(img, "png", new File(s"out_$n.png"))
  }
}

28

C #

Wow, những điều thực sự thú vị trong thử thách này. Tôi đã thực hiện một cú đâm này tại C # và tạo ra một hình ảnh 4096x4096 trong khoảng 3 phút (CPU i7) bằng cách sử dụng mọi màu đơn thông qua logic Đi bộ ngẫu nhiên.

Ok, vì vậy cho mã. Sau khi nản lòng với hàng giờ nghiên cứu và cố gắng tạo ra từng màu HSL bằng cách sử dụng các vòng lặp trong mã, tôi quyết định tạo một tệp phẳng để đọc màu HSL từ đó. Những gì tôi đã làm là tạo từng màu RGB đơn lẻ vào Danh sách, sau đó tôi đặt hàng theo Hue, Luminosity, rồi Saturation. Sau đó, tôi đã lưu Danh sách vào một tệp văn bản. ColorData chỉ là một lớp nhỏ tôi đã viết chấp nhận màu RGB và cũng lưu trữ tương đương HSL. Mã này là một người ăn RAM HUGE. Được sử dụng khoảng 4GB RAM lol.

public class RGB
{
    public double R = 0;
    public double G = 0;
    public double B = 0;
    public override string ToString()
    {
        return "RGB:{" + (int)R + "," + (int)G + "," + (int)B + "}";
    }
}
public class HSL
{
    public double H = 0;
    public double S = 0;
    public double L = 0;
    public override string ToString()
    {
        return "HSL:{" + H + "," + S + "," + L + "}";
    }
}
public class ColorData
{
    public RGB rgb;
    public HSL hsl;
    public ColorData(RGB _rgb)
    {
        rgb = _rgb;
        var _hsl = ColorHelper._color_rgb2hsl(new double[]{rgb.R,rgb.G,rgb.B});
        hsl = new HSL() { H = _hsl[0], S = _hsl[1], L = _hsl[2] };
    }
    public ColorData(double[] _rgb)
    {
        rgb = new RGB() { R = _rgb[0], G = _rgb[1], B = _rgb[2] };
        var _hsl = ColorHelper._color_rgb2hsl(_rgb);
        hsl = new HSL() { H = _hsl[0], S = _hsl[1], L = _hsl[2] };
    }
    public override string ToString()
    {
        return rgb.ToString() + "|" + hsl.ToString();
    }
    public int Compare(ColorData cd)
    {
        if (this.hsl.H > cd.hsl.H)
        {
            return 1;
        }
        if (this.hsl.H < cd.hsl.H)
        {
            return -1;
        }

        if (this.hsl.S > cd.hsl.S)
        {
            return 1;
        }
        if (this.hsl.S < cd.hsl.S)
        {
            return -1;
        }

        if (this.hsl.L > cd.hsl.L)
        {
            return 1;
        }
        if (this.hsl.L < cd.hsl.L)
        {
            return -1;
        }
        return 0;
    }
}
public static class ColorHelper
{


    public static void MakeColorFile(string savePath)
    {
        List<ColorData> Colors = new List<ColorData>();
        System.IO.File.Delete(savePath);

        for (int r = 0; r < 256; r++)
        {
            for (int g = 0; g < 256; g++)
            {
                for (int b = 0; b < 256; b++)
                {
                    double[] rgb = new double[] { r, g, b };
                    ColorData cd = new ColorData(rgb);
                    Colors.Add(cd);
                }
            }
        }
        Colors = Colors.OrderBy(x => x.hsl.H).ThenBy(x => x.hsl.L).ThenBy(x => x.hsl.S).ToList();

        string cS = "";
        using (System.IO.StreamWriter fs = new System.IO.StreamWriter(savePath))
        {

            foreach (var cd in Colors)
            {
                cS = cd.ToString();
                fs.WriteLine(cS);
            }
        }
    }


    public static IEnumerable<Color> NextColorHThenSThenL()
    {
        HashSet<string> used = new HashSet<string>();
        double rMax = 720;
        double gMax = 700;
        double bMax = 700;
        for (double r = 0; r <= rMax; r++)
        {
            for (double g = 0; g <= gMax; g++)
            {
                for (double b = 0; b <= bMax; b++)
                {
                    double h = (r / (double)rMax);
                    double s = (g / (double)gMax);
                    double l = (b / (double)bMax);
                    var c = _color_hsl2rgb(new double[] { h, s, l });
                    Color col = Color.FromArgb((int)c[0], (int)c[1], (int)c[2]);
                    string key = col.R + "-" + col.G + "-" + col.B;
                    if (!used.Contains(key))
                    {
                        used.Add(key);
                        yield return col;
                    }
                    else
                    {
                        continue;
                    }
                }
            }
        }
    }

    public static Color HSL2RGB(double h, double s, double l){
        double[] rgb= _color_hsl2rgb(new double[] { h, s, l });
        return Color.FromArgb((int)rgb[0], (int)rgb[1], (int)rgb[2]);
    }
    public static double[] _color_rgb2hsl(double[] rgb)
    {
        double r = rgb[0]; double g = rgb[1]; double b = rgb[2];
        double min = Math.Min(r, Math.Min(g, b));
        double max = Math.Max(r, Math.Max(g, b));
        double delta = max - min;
        double l = (min + max) / 2.0;
        double s = 0;
        if (l > 0 && l < 1)
        {
            s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
        }
        double h = 0;
        if (delta > 0)
        {
            if (max == r && max != g) h += (g - b) / delta;
            if (max == g && max != b) h += (2 + (b - r) / delta);
            if (max == b && max != r) h += (4 + (r - g) / delta);
            h /= 6;
        } return new double[] { h, s, l };
    }


    public static double[] _color_hsl2rgb(double[] hsl)
    {
        double h = hsl[0];
        double s = hsl[1];
        double l = hsl[2];
        double m2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
        double m1 = l * 2 - m2;
        return new double[]{255*_color_hue2rgb(m1, m2, h + 0.33333),
           255*_color_hue2rgb(m1, m2, h),
           255*_color_hue2rgb(m1, m2, h - 0.33333)};
    }


    public static double _color_hue2rgb(double m1, double m2, double h)
    {
        h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
        if (h * (double)6 < 1) return m1 + (m2 - m1) * h * (double)6;
        if (h * (double)2 < 1) return m2;
        if (h * (double)3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * (double)6;
        return m1;
    }


}

Với điều đó ra khỏi đường đi. Tôi đã viết một lớp để có được màu tiếp theo từ tệp được tạo. Nó cho phép bạn thiết lập màu sắc bắt đầu và kết thúc màu sắc. Trong thực tế, điều đó có thể và có lẽ nên được khái quát theo bất kỳ kích thước nào mà tệp được sắp xếp trước. Ngoài ra tôi nhận ra rằng để tăng hiệu suất ở đây, tôi có thể chỉ cần đặt các giá trị RGB vào tệp và giữ mỗi dòng ở một độ dài cố định. Bằng cách đó, tôi có thể dễ dàng chỉ định bù byte thay vì lặp qua mỗi dòng cho đến khi tôi đạt đến dòng tôi muốn bắt đầu. Nhưng đó không phải là một thành tích lớn đối với tôi. Nhưng đây là lớp học

public class HSLGenerator
{

    double hEnd = 1;
    double hStart = 0;

    double colCount = 256 * 256 * 256;

    public static Color ReadRGBColorFromLine(string line)
    {
        string sp1 = line.Split(new string[] { "RGB:{" }, StringSplitOptions.None)[1];
        string sp2 = sp1.Split('}')[0];
        string[] sp3 = sp2.Split(',');
        return Color.FromArgb(Convert.ToInt32(sp3[0]), Convert.ToInt32(sp3[1]), Convert.ToInt32(sp3[2]));
    }
    public IEnumerable<Color> GetNextFromFile(string colorFile)
    {
        int currentLine = -1;
        int startLine = Convert.ToInt32(hStart * colCount);
        int endLine = Convert.ToInt32(hEnd * colCount);
        string line = "";
        using(System.IO.StreamReader sr = new System.IO.StreamReader(colorFile))
        {

            while (!sr.EndOfStream)
            {
                line = sr.ReadLine();
                currentLine++;
                if (currentLine < startLine) //begin at correct offset
                {
                    continue;
                }
                yield return ReadRGBColorFromLine(line);
                if (currentLine > endLine) 
                {
                    break;
                }
            }
    }

    HashSet<string> used = new HashSet<string>();

    public void SetHueLimits(double hueStart, double hueEnd)
    {
        hEnd = hueEnd;
        hStart = hueStart;
    }
}

Vì vậy, bây giờ chúng ta có tệp màu, và chúng ta có cách để đọc tệp, bây giờ chúng ta thực sự có thể tạo ra hình ảnh. Tôi đã sử dụng một lớp mà tôi tìm thấy để tăng hiệu suất cài đặt pixel trong bitmap, được gọi là LockBitmap. Nguồn LockBitmap

Tôi đã tạo một lớp Vector2 nhỏ để lưu trữ các vị trí tọa độ

public class Vector2
{
    public int X = 0;
    public int Y = 0;
    public Vector2(int x, int y)
    {
        X = x;
        Y = y;
    }
    public Vector2 Center()
    {
        return new Vector2(X / 2, Y / 2);
    }
    public override string ToString()
    {
        return X.ToString() + "-" + Y.ToString();
    }
}

Và tôi cũng đã tạo một lớp có tên SearchArea, rất hữu ích cho việc tìm các pixel lân cận. Bạn chỉ định pixel bạn muốn tìm hàng xóm, giới hạn tìm kiếm bên trong và kích thước của "ô vuông lân cận" để tìm kiếm. Vì vậy, nếu kích thước là 3, điều đó có nghĩa là bạn đang tìm kiếm một hình vuông 3x3, với pixel được chỉ định ngay chính giữa.

public class SearchArea
{
    public int Size = 0;
    public Vector2 Center;
    public Rectangle Bounds;

    public SearchArea(int size, Vector2 center, Rectangle bounds)
    {
        Center = center;
        Size = size;
        Bounds = bounds;
    }
    public bool IsCoordinateInBounds(int x, int y)
    {
        if (!IsXValueInBounds(x)) { return false; }
        if (!IsYValueInBounds(y)) { return false; }
        return true;

    }
    public bool IsXValueInBounds(int x)
    {
        if (x < Bounds.Left || x >= Bounds.Right) { return false; }
        return true;
    }
    public bool IsYValueInBounds(int y)
    {
        if (y < Bounds.Top || y >= Bounds.Bottom) { return false; }
        return true;
    }

}

Đây là lớp học thực sự chọn người hàng xóm tiếp theo. Về cơ bản có 2 chế độ tìm kiếm. A) Hình vuông đầy đủ, B) chỉ chu vi hình vuông. Đây là một tối ưu hóa mà tôi đã thực hiện để ngăn tìm kiếm lại toàn bộ hình vuông sau khi nhận ra hình vuông đã đầy. DepthMap là một tối ưu hóa hơn nữa để ngăn chặn việc tìm kiếm cùng một hình vuông nhiều lần. Tuy nhiên, tôi đã không tối ưu hóa hoàn toàn điều này. Mỗi cuộc gọi đến GetNeighbor sẽ luôn thực hiện tìm kiếm hình vuông đầy đủ trước tiên. Tôi biết tôi có thể tối ưu hóa điều này để chỉ thực hiện tìm kiếm theo chu vi sau khi hoàn thành hình vuông đầy đủ ban đầu. Tôi chỉ chưa thực hiện được tối ưu hóa đó và thậm chí không có nó, mã này khá nhanh. Các dòng "khóa" được nhận xét là bởi vì tôi đã sử dụng Parallel.ForEach tại một thời điểm, nhưng nhận ra rằng tôi phải viết nhiều mã hơn tôi muốn cho lol đó.

public class RandomWalkGenerator
{
    HashSet<string> Visited = new HashSet<string>();
    Dictionary<string, int> DepthMap = new Dictionary<string, int>();
    Rectangle Bounds;
    Random rnd = new Random();
    public int DefaultSearchSize = 3;
    public RandomWalkGenerator(Rectangle bounds)
    {
        Bounds = bounds;
    }
    private SearchArea GetSearchArea(Vector2 center, int size)
    {
        return new SearchArea(size, center, Bounds);
    }

    private List<Vector2> GetNeighborsFullSearch(SearchArea srchArea, Vector2 coord)
    {
        int radius = (int)Math.Floor((double)((double)srchArea.Size / (double)2));
        List<Vector2> pixels = new List<Vector2>();
        for (int rX = -radius; rX <= radius; rX++)
        {
            for (int rY = -radius; rY <= radius; rY++)
            {
                if (rX == 0 && rY == 0) { continue; } //not a new coordinate
                int x = rX + coord.X;
                int y = rY + coord.Y;
                if (!srchArea.IsCoordinateInBounds(x, y)) { continue; }
                var key = x + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, y));
                    }
                }
            }
        }
        if (pixels.Count == 0)
        {
            int depth = 0;
            string vecKey = coord.ToString();
            if (!DepthMap.ContainsKey(vecKey))
            {
                DepthMap.Add(vecKey, depth);
            }
            else
            {
                depth = DepthMap[vecKey];
            }

            var size = DefaultSearchSize + 2 * depth;
            var sA = GetSearchArea(coord, size);
            pixels = GetNeighborsPerimeterSearch(sA, coord, depth);
        }
        return pixels;
    }
    private Rectangle GetBoundsForPerimeterSearch(SearchArea srchArea, Vector2 coord)
    {
        int radius = (int)Math.Floor((decimal)(srchArea.Size / 2));
        Rectangle r = new Rectangle(-radius + coord.X, -radius + coord.Y, srchArea.Size, srchArea.Size);
        return r;
    }
    private List<Vector2> GetNeighborsPerimeterSearch(SearchArea srchArea, Vector2 coord, int depth = 0)
    {
        string vecKey = coord.ToString();
        if (!DepthMap.ContainsKey(vecKey))
        {
            DepthMap.Add(vecKey, depth);
        }
        else
        {
            DepthMap[vecKey] = depth;
        }
        Rectangle bounds = GetBoundsForPerimeterSearch(srchArea, coord);
        List<Vector2> pixels = new List<Vector2>();
        int depthMax = 1500;

        if (depth > depthMax)
        {
            return pixels;
        }

        int yTop = bounds.Top;
        int yBot = bounds.Bottom;

        //left to right scan
        for (int x = bounds.Left; x < bounds.Right; x++)
        {

            if (srchArea.IsCoordinateInBounds(x, yTop))
            {
                var key = x + "-" + yTop;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, yTop));
                    }
                }
            }
            if (srchArea.IsCoordinateInBounds(x, yBot))
            {
                var key = x + "-" + yBot;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(x, yBot));
                    }
                }
            }
        }

        int xLeft = bounds.Left;
        int xRight = bounds.Right;
        int yMin = bounds.Top + 1;
        int yMax = bounds.Bottom - 1;
        //top to bottom scan
        for (int y = yMin; y < yMax; y++)
        {
            if (srchArea.IsCoordinateInBounds(xLeft, y))
            {
                var key = xLeft + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(xLeft, y));
                    }
                }
            }
            if (srchArea.IsCoordinateInBounds(xRight, y))
            {
                var key = xRight + "-" + y;
                // lock (Visited)
                {
                    if (!Visited.Contains(key))
                    {
                        pixels.Add(new Vector2(xRight, y));
                    }
                }
            }
        }

        if (pixels.Count == 0)
        {
            var size = srchArea.Size + 2;
            var sA = GetSearchArea(coord, size);
            pixels = GetNeighborsPerimeterSearch(sA, coord, depth + 1);
        }
        return pixels;
    }
    private List<Vector2> GetNeighbors(SearchArea srchArea, Vector2 coord)
    {
        return GetNeighborsFullSearch(srchArea, coord);
    }
    public Vector2 ChooseNextNeighbor(Vector2 coord)
    {
        SearchArea sA = GetSearchArea(coord, DefaultSearchSize);
        List<Vector2> neighbors = GetNeighbors(sA, coord);
        if (neighbors.Count == 0)
        {
            return null;
        }
        int idx = rnd.Next(0, neighbors.Count);
        Vector2 elm = neighbors.ElementAt(idx);
        string key = elm.ToString();
        // lock (Visited)
        {
            Visited.Add(key);
        }
        return elm;
    }
}

Được rồi, vậy bây giờ đây là lớp tạo ra hình ảnh

public class RandomWalk
{
    Rectangle Bounds;
    Vector2 StartPath = new Vector2(0, 0);
    LockBitmap LockMap;
    RandomWalkGenerator rwg;
    public int RandomWalkSegments = 1;
    string colorFile = "";

    public RandomWalk(int size, string _colorFile)
    {
        colorFile = _colorFile;
        Bounds = new Rectangle(0, 0, size, size);
        rwg = new RandomWalkGenerator(Bounds);
    }
    private void Reset()
    {
        rwg = new RandomWalkGenerator(Bounds);
    }
    public void CreateImage(string savePath)
    {
        Reset();
        Bitmap bmp = new Bitmap(Bounds.Width, Bounds.Height);
        LockMap = new LockBitmap(bmp);
        LockMap.LockBits();
        if (RandomWalkSegments == 1)
        {
            RandomWalkSingle();
        }
        else
        {
            RandomWalkMulti(RandomWalkSegments);
        }
        LockMap.UnlockBits();
        bmp.Save(savePath);

    }
    public void SetStartPath(int X, int Y)
    {
        StartPath.X = X;
        StartPath.Y = Y;
    }
    private void RandomWalkMulti(int buckets)
    {

        int Buckets = buckets;
        int PathsPerSide = (Buckets + 4) / 4;
        List<Vector2> Positions = new List<Vector2>();

        var w = Bounds.Width;
        var h = Bounds.Height;
        var wInc = w / Math.Max((PathsPerSide - 1),1);
        var hInc = h / Math.Max((PathsPerSide - 1),1);

        //top
        for (int i = 0; i < PathsPerSide; i++)
        {
            var x = Math.Min(Bounds.Left + wInc * i, Bounds.Right - 1);
            Positions.Add(new Vector2(x, Bounds.Top));
        }
        //bottom
        for (int i = 0; i < PathsPerSide; i++)
        {
            var x = Math.Max(Bounds.Right -1 - wInc * i, 0);
            Positions.Add(new Vector2(x, Bounds.Bottom - 1));
        }
        //right and left
        for (int i = 1; i < PathsPerSide - 1; i++)
        {
            var y = Math.Min(Bounds.Top + hInc * i, Bounds.Bottom - 1);
            Positions.Add(new Vector2(Bounds.Left, y));
            Positions.Add(new Vector2(Bounds.Right - 1, y));
        }
        Positions = Positions.OrderBy(x => Math.Atan2(x.X, x.Y)).ToList();
        double cnt = 0;
        List<IEnumerator<bool>> _execs = new List<IEnumerator<bool>>();
        foreach (Vector2 startPath in Positions)
        {
            double pct = cnt / (Positions.Count);
            double pctNext = (cnt + 1) / (Positions.Count);

            var enumer = RandomWalkHueSegment(pct, pctNext, startPath).GetEnumerator();

            _execs.Add(enumer);
            cnt++;
        }

        bool hadChange = true;
        while (hadChange)
        {
            hadChange = false;
            foreach (var e in _execs)
            {
                if (e.MoveNext())
                {
                    hadChange = true;
                }
            }
        }

    }
    private IEnumerable<bool> RandomWalkHueSegment(double hueStart, double hueEnd, Vector2 startPath)
    {
        var colors = new HSLGenerator();
        colors.SetHueLimits(hueStart, hueEnd);
        var colorFileEnum = colors.GetNextFromFile(colorFile).GetEnumerator();
        Vector2 coord = new Vector2(startPath.X, startPath.Y);
        LockMap.SetPixel(coord.X, coord.Y, ColorHelper.HSL2RGB(0, 0, 0));

        while (true)
        {
            if (!colorFileEnum.MoveNext())
            {
                break;
            }
            var rgb = colorFileEnum.Current;
            coord = ChooseNextNeighbor(coord);
            if (coord == null)
            {
                break;
            }
            LockMap.SetPixel(coord.X, coord.Y, rgb);
            yield return true;

        }
    }
    private void RandomWalkSingle()
    {
        Vector2 coord = new Vector2(StartPath.X, StartPath.Y);
        LockMap.SetPixel(coord.X, coord.Y, ColorHelper.HSL2RGB(0, 0, 0));
        int cnt = 1;
        var colors = new HSLGenerator();
        var colorFileEnum = colors.GetNextFromFile(colorFile).GetEnumerator();
        while (true)
        {
            if (!colorFileEnum.MoveNext())
            {
                return;
            }
            var rgb = colorFileEnum.Current;
            var newCoord = ChooseNextNeighbor(coord);
            coord = newCoord;
            if (newCoord == null)
            {
                return;
            }
            LockMap.SetPixel(newCoord.X, newCoord.Y, rgb);
            cnt++;

        }

    }

    private Vector2 ChooseNextNeighbor(Vector2 coord)
    {
        return rwg.ChooseNextNeighbor(coord);
    }


}

Và đây là một ví dụ thực hiện:

class Program
{
    static void Main(string[] args)
    {
        {
           // ColorHelper.MakeColorFile();
          //  return;
        }
        string colorFile = "colors.txt";
        var size = new Vector2(1000,1000);
        var ctr = size.Center();
        RandomWalk r = new RandomWalk(size.X,colorFile);
        r.RandomWalkSegments = 8;
        r.SetStartPath(ctr.X, ctr.Y);
        r.CreateImage("test.bmp");

    }
}

Nếu RandomWalkSegments = 1, thì về cơ bản, nó chỉ bắt đầu đi bất cứ nơi nào bạn nói với nó và bắt đầu ở màu đầu tiên trong tệp.

Đây không phải là mã sạch nhất tôi sẽ thừa nhận, nhưng nó chạy khá nhanh!

Cắt đầu ra

3 con đường

128 đường dẫn

BIÊN TẬP:

Vì vậy, tôi đã học về OpenGL và Shader. Tôi đã tạo ra 4096x4096 bằng cách sử dụng mọi màu sắc rực rỡ trên GPU với 2 tập lệnh đổ bóng đơn giản. Đầu ra thật nhàm chán, nhưng hình dung ai đó có thể thấy điều này thú vị và nảy ra một số ý tưởng hay:

Vertex Shader

attribute vec3 a_position;
varying vec2 vTexCoord;
   void main() {
      vTexCoord = (a_position.xy + 1) / 2;
      gl_Position = vec4(a_position, 1);
  }

Shader mảnh

void main(void){
    int num = int(gl_FragCoord.x*4096.0 + gl_FragCoord.y);
    int h = num % 256;
    int s = (num/256) % 256;
    int l = ((num/256)/256) % 256;
    vec4 hsl = vec4(h/255.0,s/255.0,l/255.0,1.0);
    gl_FragColor = hsl_to_rgb(hsl); // you need to implement a conversion method
}

Chỉnh sửa (15/10/16): Chỉ muốn đưa ra bằng chứng về khái niệm thuật toán di truyền. Tôi VẪN chạy mã này 24 giờ sau trên bộ màu ngẫu nhiên 100x100, nhưng cho đến nay đầu ra rất đẹp!nhập mô tả hình ảnh ở đây

Chỉnh sửa (26/10/2016): Tôi đã chạy mã thuật toán di truyền trong 12 ngày nay..và nó vẫn tối ưu hóa đầu ra. Về cơ bản, nó hội tụ đến một số tối thiểu cục bộ nhưng rõ ràng vẫn đang tìm thấy nhiều cải tiến hơn:nhập mô tả hình ảnh ở đây

Chỉnh sửa: 8/12/17 - Tôi đã viết một thuật toán đi bộ ngẫu nhiên mới - về cơ bản, bạn chỉ định một số "người đi bộ", nhưng thay vì đi bộ ngẫu nhiên - họ sẽ chọn ngẫu nhiên một người đi bộ khác và tránh chúng (chọn pixel tiếp theo xa nhất ) - hoặc đi về phía họ (chọn pixel có sẵn tiếp theo gần họ nhất). Một ví dụ về đầu ra thang độ xám ở đây (Tôi sẽ thực hiện kết xuất màu 4096x4096 đầy đủ sau khi tôi tô màu!):nhập mô tả hình ảnh ở đây


4
Một chút muộn màng, nhưng chào mừng bạn đến với PPCG! Đây là một bài viết đầu tiên tuyệt vời.
một spaghetto

1
Cảm ơn bạn! Tôi mong muốn hoàn thành nhiều thử thách hơn! Gần đây tôi đã làm nhiều công cụ mã hóa hình ảnh hơn, đó là sở thích mới của tôi
applejacks01

Wow những điều này thật tuyệt vời; Tôi rất vui vì tôi đã trở lại bài viết này ngày hôm nay và kiểm tra tất cả những thứ sau này.
Jason C

Cảm ơn bạn! Bây giờ tôi thực sự đang thực hiện một số mã hóa thuật toán di truyền để tạo ra các gradient thú vị. Về cơ bản, lấy 10000 màu, tạo thành lưới 100x100. Đối với mỗi pixel, lấy các pixel lân cận. Đối với mỗi, hãy lấy khoảng cách CIEDE2000. Tổng hợp mà lên. Tổng cộng cho tất cả 10000 pixel. Thuật toán di truyền cố gắng giảm tổng số tiền đó. Nó chậm, nhưng đối với hình ảnh 20x20 thì đầu ra của nó thực sự thú vị
applejacks01

Tôi rất đặc biệt yêu thích đầu ra của giải pháp này.
r_alex_hall

22

Canvas canvas + JavaScript

Tôi gọi nó là randoGraph và bạn có thể tạo bao nhiêu bạn muốn ở đây

Vài ví dụ:

ví dụ 1

ví dụ 2

ví dụ 3

ví dụ 4

ví dụ 5

ví dụ 6

ví dụ 7

Ví dụ: trong Firefox, bạn có thể nhấp chuột phải vào khung vẽ (khi kết thúc) và lưu nó dưới dạng hình ảnh. Sản xuất hình ảnh 4096x4096 là một loại vấn đề do giới hạn bộ nhớ của một số trình duyệt.

Ý tưởng khá đơn giản nhưng mỗi hình ảnh là duy nhất. Đầu tiên chúng ta tạo bảng màu. Sau đó, bắt đầu bằng điểm X, chúng tôi chọn các màu ngẫu nhiên từ bảng màu và vị trí cho chúng (mỗi lần chúng tôi chọn một màu chúng tôi sẽ xóa nó khỏi bảng màu) và chúng tôi ghi lại nơi chúng tôi đặt nó không đặt vào cùng một vị trí pixel tiếp theo.

Đối với mỗi pixel tiếp xúc với điều đó, chúng tôi tạo ra một số (X) màu có thể và sau đó chúng tôi chọn pixel phù hợp nhất với pixel đó. Điều này diễn ra cho đến khi hình ảnh hoàn thành.

Mã HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="el">
<head>
<script type="text/javascript" src="randoGraph.js"></script>
</head>
<body>
    <canvas id="randoGraphCanvas"></canvas> 
</body>
</html>

Và JavaScript cho randoGraph.js

window.onload=function(){
    randoGraphInstance = new randoGraph("randoGraphCanvas",256,128,1,1);
    randoGraphInstance.setRandomness(500, 0.30, 0.11, 0.59);
    randoGraphInstance.setProccesses(10);
    randoGraphInstance.init(); 
}

function randoGraph(canvasId,width,height,delay,startings)
{
    this.pixels = new Array();
    this.colors = new Array(); 
    this.timeouts = new Array(); 
    this.randomFactor = 500;
    this.redFactor = 0.30;
    this.blueFactor = 0.11;
    this.greenFactor  = 0.59;
    this.processes = 1;
    this.canvas = document.getElementById(canvasId); 
    this.pixelsIn = new Array(); 
    this.stopped = false;

    this.canvas.width = width;
    this.canvas.height = height;
    this.context = this.canvas.getContext("2d");
    this.context.clearRect(0,0, width-1 , height-1);
    this.shadesPerColor = Math.pow(width * height, 1/3);
    this.shadesPerColor = Math.round(this.shadesPerColor * 1000) / 1000;

    this.setRandomness = function(randomFactor,redFactor,blueFactor,greenFactor)
    {
        this.randomFactor = randomFactor;
        this.redFactor = redFactor;
        this.blueFactor = blueFactor;
        this.greenFactor = greenFactor;
    }

    this.setProccesses = function(processes)
    {
        this.processes = processes;
    }

    this.init = function()
    {
        if(this.shadesPerColor > 256 || this.shadesPerColor % 1 > 0) 
        { 
            alert("The dimensions of the image requested to generate are invalid. The product of width multiplied by height must be a cube root of a integer number up to 256."); 
        }
        else 
        {
            var steps = 256 / this.shadesPerColor;
            for(red = steps / 2; red <= 255;)
            {
                for(blue = steps / 2; blue <= 255;)
                {
                    for(green = steps / 2; green <= 255;)
                    {   
                        this.colors.push(new Color(Math.round(red),Math.round(blue),Math.round(green)));
                        green = green + steps;
                    }
                    blue = blue + steps; 
                }
                red = red + steps; 
            }   

            for(var i = 0; i < startings; i++)
            {
                var color = this.colors.splice(randInt(0,this.colors.length - 1),1)[0];
                var pixel = new Pixel(randInt(0,width - 1),randInt(0,height - 1),color);
                this.addPixel(pixel);       
            }

            for(var i = 0; i < this.processes; i++)
            {
                this.timeouts.push(null);
                this.proceed(i);
            }
        }
    }

    this.proceed = function(index) 
    { 
        if(this.pixels.length > 0)
        {
            this.proceedPixel(this.pixels.splice(randInt(0,this.pixels.length - 1),1)[0]);
            this.timeouts[index] = setTimeout(function(that){ if(!that.stopped) { that.proceed(); } },this.delay,this);
        }
    }

    this.proceedPixel = function(pixel)
    {
        for(var nx = pixel.getX() - 1; nx < pixel.getX() + 2; nx++)
        {
            for(var ny = pixel.getY() - 1; ny < pixel.getY() + 2; ny++)
            {
                if(! (this.pixelsIn[nx + "x" + ny] == 1 || ny < 0 || nx < 0 || nx > width - 1 || ny > height - 1 || (nx == pixel.getX() && ny == pixel.getY())) )
                {
                    var color = this.selectRelevantColor(pixel.getColor());
                    var newPixel = new Pixel(nx,ny,color);
                    this.addPixel(newPixel);
                }
            }
        }   
    }

    this.selectRelevantColor = function(color)
    {
        var relevancies = new Array(); 
        var relColors = new Array(); 
        for(var i = 0; i < this.randomFactor && i < this.colors.length; i++)
        {
            var index = randInt(0,this.colors.length - 1);
            var c = this.colors[index];
            var relevancy = Math.pow( ((c.getRed()-color.getRed()) * this.redFactor) , 2)
            + Math.pow( ((c.getBlue()-color.getBlue()) * this.blueFactor), 2)
            + Math.pow( ((c.getGreen()-color.getGreen()) * this.greenFactor) , 2);
            relevancies.push(relevancy); 
            relColors[relevancy+"Color"] = index;
        }
        return this.colors.splice(relColors[relevancies.min()+"Color"],1)[0]
    }

    this.addPixel = function(pixel)
    {
        this.pixels.push(pixel);
        this.pixelsIn[pixel.getX() + "x" + pixel.getY() ] = 1;
        var color = pixel.getColor();
        this.context.fillStyle = "rgb("+color.getRed()+","+color.getBlue()+","+color.getGreen()+")";
        this.context.fillRect( pixel.getX(), pixel.getY(), 1, 1);   
    }

    var toHex = function toHex(num) 
    {
        num = Math.round(num);
        var hex = num.toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }

    this.clear = function()
    {
        this.stopped = true;
    }
}

function Color(red,blue,green)
{   
    this.getRed = function() { return red; } 
    this.getBlue = function() { return blue; } 
    this.getGreen = function() { return green; } 
}

function Pixel(x,y,color)
{   
    this.getX = function() { return x; } 
    this.getY = function() { return y; } 
    this.getColor = function() { return color; } 
}


function randInt(min, max) 
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}


// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
Array.prototype.min = function() 
{
      return Math.min.apply(null, this);
};

// @see http://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array
Object.size = function(obj) 
{
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

Điều đó thật tuyệt nhưng có vẻ như câu trả lời C # từ fejesjoco . Có phải chỉ là tình cờ?
AL

1
Các thuật toán ở đây và bất cứ ai cũng có thể đọc và hiểu đó là thực sự khác nhau. Câu trả lời này được công bố sau khi câu trả lời C # từ fejesjoco tuyên bố là người chiến thắng thúc đẩy kết quả của nó tốt như thế nào. Sau đó, tôi nghĩ rằng một cách tiếp cận hoàn toàn khác về xử lý và chọn màu lân cận, và đây là nó. Tất nhiên cả hai câu trả lời đều có cùng một cơ sở, như phân phối đồng đều các màu được sử dụng dọc theo phổ khả kiến, khái niệm màu liên quan và điểm bắt đầu, có thể làm mờ đi những cơ sở mà ai đó có thể nghĩ rằng hình ảnh được tạo ra có sự tương đồng trong một số trường hợp.
konstantinosX

Được rồi, tôi xin lỗi nếu bạn nghĩ rằng tôi đang chỉ trích câu trả lời của bạn. Tôi chỉ tự hỏi nếu bạn được truyền cảm hứng từ câu trả lời của fejesjoco vì kết quả đầu ra trông tương tự.
AL

1
Các phương thức xác định của một lớp bên trong hàm tạo thay vì sử dụng chuỗi nguyên mẫu thực sự không hiệu quả, đặc biệt nếu lớp được sử dụng nhiều lần. Nhận xét rất thú vị Patrick Roberts. Bạn có bất kỳ tài liệu tham khảo với ví dụ xác nhận điều đó? , Tôi chân thành muốn biết liệu yêu cầu này có bất kỳ cơ sở nào không (để ngừng sử dụng nó), và đó là gì.
konstantinosX

2
Về việc sử dụng nguyên mẫu: nó hoạt động theo cách tương tự như một phương thức tĩnh. Khi bạn có hàm được định nghĩa trong nghĩa đen của đối tượng, mọi đối tượng mới bạn tạo cũng phải tạo một bản sao mới của hàm và lưu trữ chúng với thể hiện đối tượng đó (vì vậy 16 triệu đối tượng màu có nghĩa là 16 triệu bản sao của chính xác chức năng đó trong ký ức). Khi so sánh, sử dụng nguyên mẫu sẽ chỉ tạo ra nó một lần, để được liên kết với "lớp" chứ không phải là đối tượng. Điều này có lợi ích bộ nhớ rõ ràng cũng như lợi ích tốc độ tiềm năng.
Mwr247

20

Con trăn

Vì vậy, đây là giải pháp của tôi trong python, phải mất gần một giờ để tạo một cái, vì vậy có lẽ cần phải thực hiện một số tối ưu hóa:

import PIL.Image as Image
from random import shuffle
import math

def mulColor(color, factor):
    return (int(color[0]*factor), int(color[1]*factor), int(color[2]*factor))

def makeAllColors(arg):
    colors = []
    for r in range(0, arg):
        for g in range(0, arg):
            for b in range(0, arg):
                colors.append((r, g, b))
    return colors

def distance(color1, color2):
    return math.sqrt(pow(color2[0]-color1[0], 2) + pow(color2[1]-color1[1], 2) + pow(color2[2]-color1[2], 2))

def getClosestColor(to, colors):
    closestColor = colors[0]
    d = distance(to, closestColor)
    for color in colors:
        if distance(to, color) < d:
            closestColor = color
            d = distance(to, closestColor)
    return closestColor

imgsize = (256, 128)
#imgsize = (10, 10)
colors = makeAllColors(32)
shuffle(colors)
factor = 255.0/32.0
img = Image.new("RGB", imgsize, "white")
#start = (imgsize[0]/4, imgsize[1]/4)
start = (imgsize[0]/2, 0)
startColor = colors.pop()
img.putpixel(start, mulColor(startColor, factor))

#color = getClosestColor(startColor, colors)
#img.putpixel((start[0]+1, start[1]), mulColor(color, factor))

edgePixels = [(start, startColor)]
donePositions = [start]
for pixel in edgePixels:
    if len(colors) > 0:
        color = getClosestColor(pixel[1], colors)
    m = [(pixel[0][0]-1, pixel[0][1]), (pixel[0][0]+1, pixel[0][2]), (pixel[0][0], pixel[0][3]-1), (pixel[0][0], pixel[0][4]+1)]
    if len(donePositions) >= imgsize[0]*imgsize[1]:
    #if len(donePositions) >= 100:
        break
    for pos in m:
        if (not pos in donePositions):
            if not (pos[0]<0 or pos[1]<0 or pos[0]>=img.size[0] or pos[1]>=img.size[1]):
                img.putpixel(pos, mulColor(color, factor))
                #print(color)
                donePositions.append(pos)
                edgePixels.append((pos, color))
                colors.remove(color)
                if len(colors) > 0:
                    color = getClosestColor(pixel[1], colors)
    print((len(donePositions) * 1.0) / (imgsize[0]*imgsize[1]))
print len(donePositions)
img.save("colors.png")

Dưới đây là một số ví dụ đầu ra:

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


1
Trông giống như một số dạng sóng âm thanh điên rồ
Mark Jeronimus

19

Java

Tôi quyết định thử sức ở thử thách này. Tôi đã được truyền cảm hứng bởi câu trả lời này cho một mã golf khác. Chương trình của tôi tạo ra hình ảnh xấu hơn, nhưng chúng có tất cả các màu.

Ngoài ra, lần đầu tiên tôi chơi golf mã. :)

(Hình ảnh 4k quá lớn so với tốc độ tải lên nhỏ của tôi, tôi đã thử tải lên một nhưng sau một giờ nó không được tải lên. Bạn có thể tự tạo.)

Cận cảnh:

Tạo hình ảnh trong 70 giây trên máy của tôi, mất khoảng 1,5 GB bộ nhớ khi tạo

Main.java

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

import javax.imageio.ImageIO;


public class Main {
    static char[][] colors = new char[4096 * 4096][3];
    static short[][] pixels = new short[4096 * 4096][2];

    static short[][] iterMap = new short[4096][4096];  

    public static int mandel(double re0, double im0, int MAX_ITERS) {
        double re = re0;
        double im = im0;
        double _r;
        double _i;
        double re2;
        double im2;
        for (int iters = 0; iters < MAX_ITERS; iters++) {
            re2 = re * re;
            im2 = im * im;
            if (re2 + im2 > 4.0) {
                return iters;
            }
            _r = re;
            _i = im;
            _r = re2 - im2;
            _i = 2 * (re * im);
            _r += re0;
            _i += im0;
            re = _r;
            im = _i;
        }
        return MAX_ITERS;
    }

    static void shuffleArray(Object[] ar) {
        Random rnd = new Random();
        for (int i = ar.length - 1; i > 0; i--) {
          int index = rnd.nextInt(i + 1);
          // Simple swap
          Object a = ar[index];
          ar[index] = ar[i];
          ar[i] = a;
        }
      }

    public static void main(String[] args) {
        long startTime = System.nanoTime();

        System.out.println("Generating colors...");

        for (int i = 0; i < 4096 * 4096; i++) {
            colors[i][0] = (char)((i >> 16) & 0xFF); // Red
            colors[i][1] = (char)((i >> 8) & 0xFF);  // Green
            colors[i][2] = (char)(i & 0xFF);         // Blue
        }

        System.out.println("Sorting colors...");

        //shuffleArray(colors); // Not needed

        Arrays.sort(colors, new Comparator<char[]>() {
            @Override
            public int compare(char[] a, char[] b) {
                return (a[0] + a[1] + a[2]) - (b[0] + b[1] + b[2]);
            }
        });

        System.out.println("Generating fractal...");

        for (int y = -2048; y < 2048; y++) {
            for (int x = -2048; x < 2048; x++) {
                short iters = (short) mandel(x / 1024.0, y / 1024.0, 1024);
                iterMap[x + 2048][y + 2048] = iters;
            }
        }

        System.out.println("Organizing pixels in the image...");

        for (short x = 0; x < 4096; x++) {
            for (short y = 0; y < 4096; y++) {
                pixels[x * 4096 + y][0] = x;
                pixels[x * 4096 + y][1] = y;
            }
        }

        shuffleArray(pixels);

        Arrays.sort(pixels, new Comparator<short[]>() {
            @Override
            public int compare(short[] a, short[] b) {
                return iterMap[b[0]][b[1]] - iterMap[a[0]][a[1]];
            }
        });

        System.out.println("Writing image to BufferedImage...");

        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = img.createGraphics();

        for (int i = 0; i < 4096 * 4096; i++) {
            g.setColor(new Color(colors[i][0], colors[i][1], colors[i][2]));
            g.fillRect(pixels[i][0], pixels[i][1], 1, 1);
        }

        g.dispose();

        System.out.println("Writing image to file...");

        File imageFile = new File("image.png");

        try {
            ImageIO.write(img, "png", imageFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Done!");
        System.out.println("Took " + ((System.nanoTime() - startTime) / 1000000000.) + " seconds.");
        System.out.println();
        System.out.println("The result is saved in " + imageFile.getAbsolutePath());

    }

}

18

Toán học

colors = Table[
r = y*256 + x; {BitAnd[r, 2^^111110000000000]/32768., 
BitAnd[r, 2^^1111100000]/1024., BitAnd[r, 2^^11111]/32.}, {y, 0, 
127}, {x, 0, 255}];
SeedRandom[1337];
maxi = 5000000;
Monitor[For[i = 0, i < maxi, i++,
x1 = RandomInteger[{2, 255}];
x2 = RandomInteger[{2, 255}];
y1 = RandomInteger[{2, 127}];
y2 = RandomInteger[{2, 127}];
c1 = colors[[y1, x1]];
c2 = colors[[y2, x2]];
ca1 = (colors[[y1 - 1, x1]] + colors[[y1, x1 - 1]] + 
  colors[[y1 + 1, x1]] + colors[[y1, x1 + 1]])/4.;
ca2 = (colors[[y2 - 1, x2]] + colors[[y2, x2 - 1]] + 
  colors[[y2 + 1, x2]] + colors[[y2, x2 + 1]])/4.;
d1 = Abs[c1[[1]] - ca1[[1]]] + Abs[c1[[2]] - ca1[[2]]] + 
Abs[c1[[3]] - ca1[[3]]];
d1p = Abs[c2[[1]] - ca1[[1]]] + Abs[c2[[2]] - ca1[[2]]] + 
Abs[c2[[3]] - ca1[[3]]];
d2 = Abs[c2[[1]] - ca2[[1]]] + Abs[c2[[2]] - ca2[[2]]] + 
Abs[c2[[3]] - ca2[[3]]];
d2p = Abs[c1[[1]] - ca2[[1]]] + Abs[c1[[2]] - ca2[[2]]] + 
Abs[c1[[3]] - ca2[[3]]];
If[(d1p + d2p < 
  d1 + d2) || (RandomReal[{0, 1}] < 
   Exp[-Log10[i]*(d1p + d2p - (d1 + d2))] && i < 1000000),
temp = colors[[y1, x1]];
colors[[y1, x1]] = colors[[y2, x2]];
colors[[y2, x2]] = temp
]
], ProgressIndicator[i, {1, maxi}]]
Image[colors]

Kết quả (2x):

256x128 2x

Ảnh gốc 256x128

Biên tập:

bằng cách thay thế Log10 [i] bằng Log10 [i] / 5 bạn nhận được: nhập mô tả hình ảnh ở đây

Các mã trên có liên quan đến ủ mô phỏng. Nhìn theo cách này, hình ảnh thứ hai được tạo ra với "nhiệt độ" cao hơn trong 10 ^ 6 bước đầu tiên. "Nhiệt độ" cao hơn gây ra nhiều hoán vị giữa các pixel, trong khi ở hình ảnh đầu tiên, cấu trúc của hình ảnh được sắp xếp vẫn hơi hiện rõ.


17

JavaScript

Vẫn là một sinh viên và lần đầu tiên tôi đăng bài nên mã của tôi có thể lộn xộn và tôi không chắc chắn 100% rằng hình ảnh của tôi có đủ màu sắc cần thiết nhưng tôi rất hài lòng với kết quả của mình nên tôi nghĩ rằng tôi đã đăng chúng.

Tôi biết cuộc thi đã kết thúc nhưng tôi thực sự yêu thích kết quả của những điều này và tôi luôn yêu thích vẻ ngoài của các mê cung được tạo ra đệ quy nên tôi nghĩ thật tuyệt khi thấy người ta sẽ trông như thế nào nếu đặt pixel màu. Vì vậy, tôi bắt đầu bằng cách tạo ra tất cả các màu trong một mảng sau đó tôi thực hiện quay lui đệ quy trong khi bật các màu ra khỏi mảng.

Đây là JSFiddle của tôi http://jsfiddle.net/Kuligoawclaw/3VsCu/

// Global variables
const FPS = 60;// FrameRate
var canvas = null;
var ctx = null;

var bInstantDraw = false;
var MOVES_PER_UPDATE = 50; //How many pixels get placed down
var bDone = false;
var width; //canvas width
var height; //canvas height
var colorSteps = 32;

var imageData;
var grid;
var colors;

var currentPos;
var prevPositions;

// This is called when the page loads
function Init()
{
    canvas = document.getElementById('canvas'); // Get the HTML element with the ID of 'canvas'
    width = canvas.width;
    height = canvas.height;
    ctx = canvas.getContext('2d'); // This is necessary, but I don't know exactly what it does

    imageData = ctx.createImageData(width,height); //Needed to do pixel manipulation

    grid = []; //Grid for the labyrinth algorithm
    colors = []; //Array of all colors
    prevPositions = []; //Array of previous positions, used for the recursive backtracker algorithm

    for(var r = 0; r < colorSteps; r++)
    {
        for(var g = 0; g < colorSteps; g++)
        {
            for(var b = 0; b < colorSteps; b++)
            {
                colors.push(new Color(r * 255 / (colorSteps - 1), g * 255 / (colorSteps - 1), b * 255 / (colorSteps - 1)));
                //Fill the array with all colors
            }
        }
    }

    colors.sort(function(a,b)
    {
        if (a.r < b.r)
            return -1;
        if (a.r > b.r)
            return 1;
        if (a.g < b.g)
            return -1;
        if (a.g > b.g)
            return 1;
        if (a.b < b.b)
            return -1;
        if (a.b > b.b)
            return 1;
        return 0;
    });

    for(var x = 0; x < width; x++)
    {
        grid.push(new Array());
        for(var y = 0; y < height; y++)
        {
            grid[x].push(0); //Set up the grid
            //ChangePixel(imageData, x, y, colors[x + (y * width)]);
        }
    }

    currentPos = new Point(Math.floor(Math.random() * width),Math.floor(Math.random() * height)); 

    grid[currentPos.x][currentPos.y] = 1;
    prevPositions.push(currentPos);
    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop());

    if(bInstantDraw)
    {
        do
        {
            var notMoved = true;
            while(notMoved)
            {
                var availableSpaces = CheckForSpaces(grid);

                if(availableSpaces.length > 0)
                {
                    var test = availableSpaces[Math.floor(Math.random() * availableSpaces.length)];
                    prevPositions.push(currentPos);
                    currentPos = test;
                    grid[currentPos.x][currentPos.y] = 1;
                    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop());
                    notMoved = false;
                }
                else
                {
                    if(prevPositions.length != 0)
                    {
                        currentPos = prevPositions.pop();
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
        while(prevPositions.length > 0)

        ctx.putImageData(imageData,0,0);
    }
    else
    {
        setInterval(GameLoop, 1000 / FPS);
    }
}

// Main program loop
function GameLoop()
{
    Update();
    Draw();
}

// Game logic goes here
function Update()
{
    if(!bDone)
    {
        var counter = MOVES_PER_UPDATE;
        while(counter > 0) //For speeding up the drawing
        {
            var notMoved = true;
            while(notMoved)
            {
                var availableSpaces = CheckForSpaces(grid); //Find available spaces

                if(availableSpaces.length > 0) //If there are available spaces
                {
                    prevPositions.push(currentPos); //add old position to prevPosition array
                    currentPos = availableSpaces[Math.floor(Math.random() * availableSpaces.length)]; //pick a random available space
                    grid[currentPos.x][currentPos.y] = 1; //set that space to filled
                    ChangePixel(imageData, currentPos.x, currentPos.y, colors.pop()); //pop color of the array and put it in that space
                    notMoved = false;
                }
                else
                {
                    if(prevPositions.length != 0)
                    {
                        currentPos = prevPositions.pop(); //pop to previous position where spaces are available
                    }
                    else
                    {
                        bDone = true;
                        break;
                    }
                }
            }
            counter--;
        }
    }
}
function Draw()
{
    // Clear the screen
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.fillStyle='#000000';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    ctx.putImageData(imageData,0,0);
}

function CheckForSpaces(inGrid) //Checks for available spaces then returns back all available spaces
{
    var availableSpaces = [];

    if(currentPos.x > 0 && inGrid[currentPos.x - 1][currentPos.y] == 0)
    {
        availableSpaces.push(new Point(currentPos.x - 1, currentPos.y));
    }

    if(currentPos.x < width - 1 && inGrid[currentPos.x + 1][currentPos.y] == 0)
    {
        availableSpaces.push(new Point(currentPos.x + 1, currentPos.y));
    }

    if(currentPos.y > 0 && inGrid[currentPos.x][currentPos.y - 1] == 0)
    {
        availableSpaces.push(new Point(currentPos.x, currentPos.y - 1));
    }

    if(currentPos.y < height - 1 && inGrid[currentPos.x][currentPos.y + 1] == 0)
    {
        availableSpaces.push(new Point(currentPos.x, currentPos.y + 1));
    }

    return availableSpaces;
}

function ChangePixel(data, x, y, color) //Quick function to simplify changing pixels
{
    data.data[((x + (y * width)) * 4) + 0] = color.r;
    data.data[((x + (y * width)) * 4) + 1] = color.g;
    data.data[((x + (y * width)) * 4) + 2] = color.b;
    data.data[((x + (y * width)) * 4) + 3] = 255;
}

/*Needed Classes*/
function Point(xIn, yIn)
{
    this.x = xIn;
    this.y = yIn;
}

function Color(r, g, b)
{
    this.r = r;
    this.g = g;
    this.b = b;
    this.hue = Math.atan2(Math.sqrt(3) * (this.g - this.b), 2 * this.r - this.g, this.b);
    this.min = Math.min(this.r, this.g);
    this.min = Math.min(this.min, this.b);
    this.min /= 255;
    this.max = Math.max(this.r, this.g);
    this.max = Math.max(this.max, this.b);
    this.max /= 255;
    this.luminance = (this.min + this.max) / 2;
    if(this.min === this.max)
    {
        this.saturation = 0;
    }
    else if(this.luminance < 0.5)
    {
        this.saturation = (this.max - this.min) / (this.max + this.min);
    }
    else if(this.luminance >= 0.5)
    {
        this.saturation = (this.max - this.min) / (2 - this.max - this.min);
    }
}

Hình ảnh 256x128, màu sắc được sắp xếp màu đỏ-> xanh lá cây-> màu xanh
Màu sắc được sắp xếp RGB

Hình ảnh 256x128, màu sắc được sắp xếp màu xanh lam-> màu xanh lá cây-> màu đỏ
BGR Sắp xếp màu sắc

Hình ảnh 256x128, màu sắc được sắp xếp màu sắc-> độ chói-> độ bão hòa
Màu sắc được sắp xếp của HLS

Và cuối cùng là một GIF của một người được tạo ra
GIF mê cung màu


Màu sắc của bạn bị cắt ở những vùng sáng nhất, gây ra sự trùng lặp. Thay đổi r * Math.ceil(255 / (colorSteps - 1)thành r * Math.floor(255 / (colorSteps - 1), hoặc thậm chí tốt hơn: r * 255 / (colorSteps - 1)(chưa được kiểm tra, vì bạn không cung cấp jsfiddle)
Mark Jeronimus

Rất tiếc, vâng tôi có cảm giác sẽ gây ra sự cố, hy vọng nó đã được sửa chữa và xin lỗi về việc thiếu jsfiddle (Tôi không biết nó tồn tại!) Cảm ơn!
Kuligoawgie

Tôi thích đầu ra hỗn loạn / nhiễu tìm kiếm của giải pháp này và một giải pháp khác tạo ra đầu ra tương tự.
r_alex_hall

17

C #

Vì vậy, tôi bắt đầu thực hiện điều này giống như một bài tập thú vị và kết thúc với một đầu ra mà ít nhất với tôi trông khá gọn gàng. Sự khác biệt chính trong giải pháp của tôi đối với (ít nhất) hầu hết những người khác là tôi đang tạo ra chính xác số lượng màu cần thiết để bắt đầu và cách đều nhau các thế hệ từ màu trắng tinh khiết sang màu đen thuần khiết. Tôi cũng thiết lập màu sắc làm việc theo hình xoắn ốc hướng vào trong và chọn màu tiếp theo dựa trên mức trung bình của màu khác nhau giữa tất cả các lân cận đã được đặt.

Đây là một đầu ra mẫu nhỏ mà tôi đã sản xuất cho đến nay, tôi đang làm việc với kết xuất 4k nhưng tôi hy vọng nó sẽ mất một ngày để hoàn thành.

Đây là một mẫu của đầu ra spec ở 256x128:

Thông số kết xuất

Một số hình ảnh lớn hơn với thời gian kết xuất vẫn hợp lý:

Kết xuất ở 360 x 240

Lần chạy thứ hai ở 360 x 240 tạo ra hình ảnh mượt mà hơn nhiều

Kết xuất số 2 ở 360 x 240

Sau khi cải thiện hiệu suất, tôi có thể chạy kết xuất HD mất 2 ngày, tôi vẫn chưa bỏ được 4k nhưng phải mất hàng tuần.

Kết xuất HD

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace SandBox
{
    class Program
    {
        private static readonly List<Point> directions = new List<Point>
        {
            new Point(1, 0),
            new Point(0, 1),
            new Point(-1, 0),
            new Point(0, -1)
        };

        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                HelpFile();
                return;
            }
            try
            {
                var config = new ColorGeneratorConfig
                {
                    XLength = int.Parse(args[0]),
                    YLength = int.Parse(args[1])
                };

                Console.WriteLine("Starting image generation with:");
                Console.WriteLine($"\tDimensions:\t\t{config.XLength} X {config.YLength}");
                Console.WriteLine($"\tSteps Per Channel:\t{config.NumOfSteps}");
                Console.WriteLine($"\tStep Size:\t\t{config.ColorStep}");
                Console.WriteLine($"\tSteps to Skip:\t\t{config.StepsToSkip}\n");

                var runner = new TaskRunner();
                var colors = runner.Run(() => GenerateColorList(config), "color selection");
                var pixels = runner.Run(() => BuildPixelArray(colors, config), "pixel array generation");
                runner.Run(() => OutputBitmap(pixels, config), "bitmap creation");
            }
            catch (Exception ex)
            {
               HelpFile("There was an issue in execution");
            }

            Console.ReadLine();
        }

        private static void HelpFile(string errorMessage = "")
        {
            const string Header = "Generates an image with every pixel having a unique color";
            Console.WriteLine(errorMessage == string.Empty ? Header : $"An error has occured: {errorMessage}\n Ensure the Arguments you have provided are valid");
            Console.WriteLine();
            Console.WriteLine($"{AppDomain.CurrentDomain.FriendlyName} X Y");
            Console.WriteLine();
            Console.WriteLine("X\t\tThe Length of the X dimension eg: 256");
            Console.WriteLine("Y\t\tThe Length of the Y dimension eg: 128");
        }

        public static List<Color> GenerateColorList(ColorGeneratorConfig config)
        {

            //Every iteration of our color generation loop will add the iterationfactor to this accumlator which is used to know when to skip
            decimal iterationAccumulator = 0;

            var colors = new List<Color>();
            for (var r = 0; r < config.NumOfSteps; r++)
                for (var g = 0; g < config.NumOfSteps; g++)
                    for (var b = 0; b < config.NumOfSteps; b++)
                    {
                        iterationAccumulator += config.IterationFactor;

                        //If our accumulator has reached 1, then subtract one and skip this iteration
                        if (iterationAccumulator > 1)
                        {
                            iterationAccumulator -= 1;
                            continue;
                        }

                        colors.Add(Color.FromArgb(r*config.ColorStep, g*config.ColorStep,b*config.ColorStep));
                    }
            return colors;
        }

        public static Color?[,] BuildPixelArray(List<Color> colors, ColorGeneratorConfig config)
        {
            //Get a random color to start with.
            var random = new Random(Guid.NewGuid().GetHashCode());
            var nextColor = colors[random.Next(colors.Count)];

            var pixels = new Color?[config.XLength, config.YLength];
            var currPixel = new Point(0, 0);

            var i = 0;

            //Since we've only generated exactly enough colors to fill our image we can remove them from the list as we add them to our image and stop when none are left.
            while (colors.Count > 0)
            {
                i++;

                //Set the current pixel and remove the color from the list.
                pixels[currPixel.X, currPixel.Y] = nextColor;
                colors.RemoveAt(colors.IndexOf(nextColor));

                //Our image generation works in an inward spiral generation GetNext point will retrieve the next pixel given the current top direction.
                var nextPixel = GetNextPoint(currPixel, directions.First());

                //If this next pixel were to be out of bounds (for first circle of spiral) or hit a previously generated pixel (for all other circles)
                //Then we need to cycle the direction and get a new next pixel
                if (nextPixel.X >= config.XLength || nextPixel.Y >= config.YLength || nextPixel.X < 0 || nextPixel.Y < 0 ||
                    pixels[nextPixel.X, nextPixel.Y] != null)
                {
                    var d = directions.First();
                    directions.RemoveAt(0);
                    directions.Add(d);
                    nextPixel = GetNextPoint(currPixel, directions.First());
                }

                //This code sets the pixel to pick a color for and also gets the next color
                //We do this at the end of the loop so that we can also support haveing the first pixel set outside of the loop
                currPixel = nextPixel;

                if (colors.Count == 0) continue;

                var neighbours = GetNeighbours(currPixel, pixels, config);
                nextColor = colors.AsParallel().Aggregate((item1, item2) => GetAvgColorDiff(item1, neighbours) <
                                                                            GetAvgColorDiff(item2, neighbours)
                    ? item1
                    : item2);
            }

            return pixels;
        }

        public static void OutputBitmap(Color?[,] pixels, ColorGeneratorConfig config)
        {
            //Now that we have generated our image in the color array we need to copy it into a bitmap and save it to file.
            var image = new Bitmap(config.XLength, config.YLength);

            for (var x = 0; x < config.XLength; x++)
                for (var y = 0; y < config.YLength; y++)
                    image.SetPixel(x, y, pixels[x, y].Value);

            using (var file = new FileStream($@".\{config.XLength}X{config.YLength}.png", FileMode.Create))
            {
                image.Save(file, ImageFormat.Png);
            }
        }

        static Point GetNextPoint(Point current, Point direction)
        {
            return new Point(current.X + direction.X, current.Y + direction.Y);
        }

        static List<Color> GetNeighbours(Point current, Color?[,] grid, ColorGeneratorConfig config)
        {
            var list = new List<Color>();
            foreach (var direction in directions)
            {
                var xCoord = current.X + direction.X;
                var yCoord = current.Y + direction.Y;
                if (xCoord < 0 || xCoord >= config.XLength|| yCoord < 0 || yCoord >= config.YLength)
                {
                    continue;
                }
                var cell = grid[xCoord, yCoord];
                if (cell.HasValue) list.Add(cell.Value);
            }
            return list;
        }

        static double GetAvgColorDiff(Color source, IList<Color> colors)
        {
            return colors.Average(color => GetColorDiff(source, color));
        }

        static int GetColorDiff(Color color1, Color color2)
        {
            var redDiff = Math.Abs(color1.R - color2.R);
            var greenDiff = Math.Abs(color1.G - color2.G);
            var blueDiff = Math.Abs(color1.B - color2.B);
            return redDiff + greenDiff + blueDiff;
        }
    }

    public class ColorGeneratorConfig
    {
        public int XLength { get; set; }
        public int YLength { get; set; }

        //Get the number of permutations for each color value base on the required number of pixels.
        public int NumOfSteps
            => (int)Math.Ceiling(Math.Pow((ulong)XLength * (ulong)YLength, 1.0 / ColorDimensions));

        //Calculate the increment for each step
        public int ColorStep
            => 255 / (NumOfSteps - 1);

        //Because NumOfSteps will either give the exact number of colors or more (never less) we will sometimes to to skip some
        //this calculation tells how many we need to skip
        public decimal StepsToSkip
            => Convert.ToDecimal(Math.Pow(NumOfSteps, ColorDimensions) - XLength * YLength);

        //This factor will be used to as evenly as possible spread out the colors to be skipped so there are no large gaps in the spectrum
        public decimal IterationFactor => StepsToSkip / Convert.ToDecimal(Math.Pow(NumOfSteps, ColorDimensions));

        private double ColorDimensions => 3.0;
    }

    public class TaskRunner
    {
        private Stopwatch _sw;
        public TaskRunner()
        {
            _sw = new Stopwatch();
        }

        public void Run(Action task, string taskName)
        {
            Console.WriteLine($"Starting {taskName}...");
            _sw.Start();
            task();
            _sw.Stop();
            Console.WriteLine($"Finished {taskName}. Elapsed(ms): {_sw.ElapsedMilliseconds}");
            Console.WriteLine();
            _sw.Reset();
        }

        public T Run<T>(Func<T> task, string taskName)
        {
            Console.WriteLine($"Starting {taskName}...");
            _sw.Start();
            var result = task();
            _sw.Stop();
            Console.WriteLine($"Finished {taskName}. Elapsed(ms): {_sw.ElapsedMilliseconds}");
            Console.WriteLine();
            _sw.Reset();
            return result;
        }
    }
}

Nếu bất cứ ai có bất kỳ suy nghĩ nào về cách cải thiện hiệu suất của thuật toán lựa chọn màu sắc, vui lòng cho tôi biết, vì nó hiển thị 360 * 240 kết xuất mất khoảng 15 phút. Tôi không tin rằng nó có thể song song nhưng tôi tự hỏi liệu có cách nào nhanh hơn để có được độ lệch màu thấp nhất không.
Phaeze

Làm thế nào để một hình ảnh 360 * 240 tạo thành 'tất cả các màu'? Làm thế nào bạn sản xuất cbrt (360 * 240) = 44.208377983684639269357874002958 màu cho mỗi thành phần?
Mark Jeronimus

Ngôn ngữ này là gì? Ngẫu nhiên sắp xếp danh sách và Ngẫu nhiên là một ý tưởng tồi bất kể, vì tùy thuộc vào thuật toán và việc thực hiện, nó có thể gây ra kết quả sai lệch hoặc ngoại lệ cho biết "Comparison method violates its general contract!": vì hợp đồng nêu rõ điều đó (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. Để chọn ngẫu nhiên một danh sách, sử dụng một số phương pháp Shuffle được cung cấp. ( colors.Shuffle()?)
Mark Jeronimus

@MarkJeronimus Tôi thừa nhận tôi đã bỏ lỡ thông số về hình ảnh 256x128, tôi sẽ thực hiện các biểu hiện đơn giản bằng các kích thước đó, tôi đang tập trung vào mỗi pixel là một khía cạnh màu sắc duy nhất của thử thách và hiển thị lớn hơn như các lần gửi khác đã thực hiện.
Phaeze

@MarkJeronimus Tôi nhận ra loại sắp xếp ngẫu nhiên là xấu, trên thực tế có một bình luận nói càng nhiều. Đây chỉ là phần còn lại của một cách tiếp cận khác mà tôi bắt đầu thực hiện và tôi đang ưu tiên thực hiện các kết xuất lớn khi chúng mất một thời gian rất dài.
Phaeze

16

Đi

Đây là một cái khác từ tôi, tôi nghĩ rằng nó mang lại kết quả thú vị hơn:

package main

import (
    "image"
    "image/color"
    "image/png"
    "os"

    "math"
    "math/rand"
)

func distance(c1, c2 color.Color) float64 {
    r1, g1, b1, _ := c1.RGBA()
    r2, g2, b2, _ := c2.RGBA()
    rd, gd, bd := int(r1)-int(r2), int(g1)-int(g2), int(b1)-int(b2)
    return math.Sqrt(float64(rd*rd + gd*gd + bd*bd))
}

func main() {
    allcolor := image.NewRGBA(image.Rect(0, 0, 256, 128))
    for y := 0; y < 128; y++ {
        for x := 0; x < 256; x++ {
            allcolor.Set(x, y, color.RGBA{uint8(x%32) * 8, uint8(y%32) * 8, uint8(x/32+(y/32*8)) * 8, 255})
        }
    }

    for y := 0; y < 128; y++ {
        for x := 0; x < 256; x++ {
            rx, ry := rand.Intn(256), rand.Intn(128)

            c1, c2 := allcolor.At(x, y), allcolor.At(rx, ry)
            allcolor.Set(x, y, c2)
            allcolor.Set(rx, ry, c1)
        }
    }

    for i := 0; i < 16384; i++ {
        for y := 0; y < 128; y++ {
            for x := 0; x < 256; x++ {
                xl, xr := (x+255)%256, (x+1)%256
                cl, c, cr := allcolor.At(xl, y), allcolor.At(x, y), allcolor.At(xr, y)
                dl, dr := distance(cl, c), distance(c, cr)
                if dl < dr {
                    allcolor.Set(xl, y, c)
                    allcolor.Set(x, y, cl)
                }

                yu, yd := (y+127)%128, (y+1)%128
                cu, c, cd := allcolor.At(x, yu), allcolor.At(x, y), allcolor.At(x, yd)
                du, dd := distance(cu, c), distance(c, cd)
                if du < dd {
                    allcolor.Set(x, yu, c)
                    allcolor.Set(x, y, cu)
                }
            }
        }
    }

    filep, err := os.Create("EveryColor.png")
    if err != nil {
        panic(err)
    }
    err = png.Encode(filep, allcolor)
    if err != nil {
        panic(err)
    }
    filep.Close()
}

Nó bắt đầu với cùng một mẫu với gif trong câu trả lời khác của tôi . Sau đó, nó xáo trộn nó vào đây:

chỉ là tiếng ồn

Càng lặp nhiều lần, tôi chạy thuật toán so sánh hàng xóm khá mệt mỏi, mô hình cầu vồng càng trở nên rõ ràng.

Đây là 16384:

cầu vồng rất ồn ào ở 16384 lần lặp

Và 65536:

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


6
+1 Tôi thích rằng một mô hình xuất hiện từ đó; bạn nên làm một hình ảnh động của nó!
Jason C

16

Những hình ảnh này là "Cầu vồng của Langton". Chúng được vẽ khá đơn giản: khi con kiến ​​của Langton di chuyển, một màu được vẽ trên mỗi pixel trong lần đầu tiên pixel đó được nhìn thấy. Màu sắc để vẽ tiếp theo sau đó chỉ đơn giản là tăng thêm 1, đảm bảo rằng 2 ^ 15 màu được sử dụng, một màu cho mỗi pixel.

EDIT: Tôi đã tạo một phiên bản hiển thị hình ảnh 4096X4096, sử dụng 2 ^ 24 màu. Màu sắc cũng được 'phản chiếu' để chúng tạo ra các gradient đẹp, mịn. Tôi sẽ chỉ cung cấp liên kết đến họ vì chúng rất lớn (> 28 MB)

Cầu vồng lớn của Langton, quy tắc LR

Cầu vồng lớn của Langton, quy tắc LLRR

// Kết thúc chỉnh sửa.

Điều này cho bộ quy tắc LR cổ điển:

Cầu vồng của Langton

Đây là LLRR:

Cầu vồng LLRR của Langton

Cuối cùng, cái này sử dụng bộ quy tắc LRRRRRLLR:

Cầu vồng LRRRRLLR của Langton

Viết bằng C ++, sử dụng CImg cho đồ họa. Tôi cũng nên đề cập đến cách các màu được chọn: Đầu tiên, tôi sử dụng một dấu ngắn không dấu để chứa dữ liệu màu RGB. Mỗi lần một ô được hiển thị lần đầu tiên, tôi phải dịch chuyển các bit theo bội số của 5, VÀ 31, sau đó nhân với 8. Sau đó, màu ngắn không dấu được tăng lên 1. Điều này tạo ra các giá trị từ 0 đến 248 nhiều nhất. Tuy nhiên, tôi đã trừ giá trị này từ 255 trong các thành phần màu đỏ và màu xanh lam, do đó R và B là bội số của 8, bắt đầu từ 255, xuống còn 7:

c[0]=255-((color&0x1F)*8);
c[2]=255-(((color>>5)&0x1F)*8);
c[1]=(((color>>10)&0x1F)*8);

Tuy nhiên, điều này không áp dụng cho thành phần màu xanh lục, có bội số từ 8 đến 248. Trong mọi trường hợp, mỗi pixel nên chứa một màu duy nhất.

Dù sao, mã nguồn là dưới đây:

#include "CImg.h"
using namespace cimg_library;
CImgDisplay screen;
CImg<unsigned char> surf;
#define WIDTH 256
#define HEIGHT 128
#define TOTAL WIDTH*HEIGHT
char board[WIDTH][HEIGHT];


class ant
{
  public:
  int x,y;
  char d;
  unsigned short color;
  void init(int X, int Y,char D)
  {
    x=X;y=Y;d=D;
    color=0;
  }

  void turn()
  {
    ///Have to hard code for the rule set here.
    ///Make sure to set RULECOUNT to the number of rules!
    #define RULECOUNT 9
    //LRRRRRLLR
    char get=board[x][y];
    if(get==0||get==6||get==7){d+=1;}
    else{d-=1;}
    if(d<0){d=3;}
    else if(d>3){d=0;}
  }

  void forward()
  {
    if(d==0){x++;}
    else if(d==1){y--;}
    else if(d==2){x--;}
    else {y++;}
    if(x<0){x=WIDTH-1;}
    else if(x>=WIDTH){x=0;}
    if(y<0){y=HEIGHT-1;}
    else if(y>=HEIGHT){y=0;}
  }

  void draw()
  {
    if(board[x][y]==-1)
    {
      board[x][y]=0;
      unsigned char c[3];
      c[0]=255-((color&0x1F)*8);
      c[2]=255-(((color>>5)&0x1F)*8);
      c[1]=(((color>>10)&0x1F)*8);
      surf.draw_point(x,y,c);
      color++;
    }

    board[x][y]++;
    if(board[x][y]==RULECOUNT){board[x][y]=0;}

  }

  void step()
  {
    draw();
    turn();
    forward();
  }
};

void renderboard()
{
  unsigned char white[]={200,190,180};
  surf.draw_rectangle(0,0,WIDTH,HEIGHT,white);
  for(int x=0;x<WIDTH;x++)
  for(int y=0;y<HEIGHT;y++)
  {
    char get=board[x][y];
    if(get==1){get=1;unsigned char c[]={255*get,255*get,255*get};
    surf.draw_point(x,y,c);}
    else if(get==0){get=0;unsigned char c[]={255*get,255*get,255*get};
    surf.draw_point(x,y,c);}
  }
}

int main(int argc, char** argv)
{

  screen.assign(WIDTH*3,HEIGHT*3);
  surf.assign(WIDTH,HEIGHT,1,3);
  ant a;
  a.init(WIDTH/2,HEIGHT/2,2);
  surf.fill(0);
  for(int x=0;x<WIDTH;x++)
  for(int y=0;y<HEIGHT;y++)
  {
    board[x][y]=-1;
  }

  while(a.color<TOTAL)
  {
    a.step();
  }

  screen=surf;
  while(screen.is_closed()==false)
  {
    screen.wait();
  }
  surf.save_bmp("LangtonsRainbow.bmp");
  return 0;
}

1
Chào mừng, và tham gia câu lạc bộ. Có lẽ thật thú vị khi thử các loại turmite khác từ code.google.com/p/ruletablerep repository / wiki / (Tôi đã tham gia vào đó)
Mark Jeronimus

3
Liên kết hình ảnh đã chết vì Dropbox đã giết Thư mục công cộng.
dùng2428118

15

Hồng ngọc

Tôi quyết định tôi sẽ tiếp tục và tạo ra PNG từ đầu, bởi vì tôi nghĩ điều đó sẽ thú vị. Mã này theo nghĩa đen là xuất ra nguyên dữ liệu nhị phân vào một tệp.

Tôi đã làm phiên bản 512x512. (Tuy nhiên, thuật toán này không thú vị lắm.) Nó hoàn thành sau khoảng 3 giây trên máy của tôi.

require 'zlib'

class RBPNG
  def initialize
    # PNG header
    @data = [137, 80, 78, 71, 13, 10, 26, 10].pack 'C*'
  end

  def chunk name, data = ''
    @data += [data.length].pack 'N'
    @data += name
    @data += data
    @data += [Zlib::crc32(name + data)].pack 'N'
  end

  def IHDR opts = {}
    opts = {bit_depth: 8, color_type: 6, compression: 0, filter: 0, interlace: 0}.merge opts
    raise 'IHDR - Missing width param' if !opts[:width]
    raise 'IHDR - Missing height param' if !opts[:height]

    self.chunk 'IHDR', %w[width height].map {|x| [opts[x.to_sym]].pack 'N'}.join +
                       %w[bit_depth color_type compression filter interlace].map {|x| [opts[x.to_sym]].pack 'C'}.join
  end

  def IDAT data; self.chunk 'IDAT', Zlib.deflate(data); end
  def IEND; self.chunk 'IEND'; end
  def write filename; IO.binwrite filename, @data; end
end

class Color
  attr_accessor :r, :g, :b, :a

  def initialize r = 0, g = 0, b = 0, a = 255
    if r.is_a? Array
      @r, @g, @b, @a = @r
      @a = 255 if !@a
    else
      @r = r
      @g = g
      @b = b
      @a = a
    end
  end

  def hex; '%02X' * 4 % [@r, @g, @b, @a]; end
  def rgbhex; '%02X' * 3 % [@r, @g, @b]; end
end

img = RBPNG.new
img.IHDR({width: 512, height: 512, color_type: 2})
#img.IDAT ['00000000FFFFFF00FFFFFF000000'].pack 'H*'
r = g = b = 0
data = Array.new(512){ Array.new(512){
  c = Color.new r, g, b
  r += 4
  if r == 256
    r = 0
    g += 4
    if g == 256
      g = 0
      b += 4
    end
  end
  c
} }
img.IDAT [data.map {|x| '00' + x.map(&:rgbhex).join }.join].pack 'H*'
img.IEND
img.write 'all_colors.png'

Đầu ra (trong all_colors.png ) (nhấp vào bất kỳ hình ảnh nào trong số này để phóng to chúng):

Đầu ra

Một số đầu ra gradient-ish thú vị hơn (bằng cách thay đổi dòng thứ 4 thành dòng cuối cùng thành }.shuffle } ):

Đầu ra 2

Và bằng cách thay đổi nó thành }.shuffle }.shuffle, bạn sẽ có được những dòng màu điên rồ:

Đầu ra 3


Đó là thực sự mát mẻ. Có cách nào để bạn có thể làm cho nó đẹp hơn không? Có thể ngẫu nhiên các pixel? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian Ok, đã được chỉnh sửa
Doorknob

Tốt hơn nhiều!!!!!!!

1
Điều gì sẽ xảy ra nếu bạn thay đổi dòng thứ 4 thành dòng cuối cùng }.shuffle }.shuffle }.shuffle?
John Odom

6
@ John Erm, lỗi cú pháp, có lẽ?
Doorknob

14

Con trăn

huyết tương

Sử dụng python để sắp xếp màu sắc theo độ chói, tạo ra mô hình độ chói và chọn màu thích hợp nhất. Các pixel được lặp theo thứ tự ngẫu nhiên sao cho độ chói ít thuận lợi hơn xảy ra một cách tự nhiên khi danh sách các màu có sẵn trở nên nhỏ hơn được trải đều trong ảnh.

#!/usr/bin/env python

from PIL import Image
from math import pi, sin, cos
import random

WIDTH = 256
HEIGHT = 128

img = Image.new("RGB", (WIDTH, HEIGHT))

colors = [(x >> 10, (x >> 5) & 31, x & 31) for x in range(32768)]
colors = [(x[0] << 3, x[1] << 3, x[2] << 3) for x in colors]
colors.sort(key=lambda x: x[0] * 0.2126 + x[1] * 0.7152 + x[2] * 0.0722)

def get_pixel(lum):
    for i in range(len(colors)):
        c = colors[i]
        if c[0] * 0.2126 + c[1] * 0.7152 + c[2] * 0.0722 > lum:
            break
    return colors.pop(i)

def plasma(x, y):
    x -= WIDTH / 2
    p = sin(pi * x / (32 + 10 * sin(y * pi / 32)))
    p *= cos(pi * y / 64)
    return 128 + 127 * p

xy = []
for x in range(WIDTH):
    for y in range(HEIGHT):
        xy.append((x, y))
random.shuffle(xy)

count = 0
for x, y in xy:
    l = int(plasma(x, y))
    img.putpixel((x, y), get_pixel(plasma(x, y)))
    count += 1
    if not count & 255:
        print "%d pixels rendered" % count

img.save("test.png")

13

Java

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096 ; x++) {
                points.add(new Point(x, y));
            }
        }
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Tôi đã đi 4096 cho 4096 bởi vì tôi không thể tìm ra làm thế nào để có được tất cả các màu mà không làm như vậy.

Đầu ra:

Quá lớn để phù hợp ở đây. Đây là một ảnh chụp màn hình:

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

Với một chút thay đổi, chúng ta có thể có được một bức tranh đẹp hơn:

Thêm Collections.shuffle(points, new Random(0));giữa việc tạo các điểm và thực hiện các màu sắc:

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);
        int num = 0;
        ArrayList<Point> points = new ArrayList<>();
        for (int y = 0; y < 4096; y++) {
            for (int x = 0; x < 4096 ; x++) {
                points.add(new Point(x, y));
            }
        }
        Collections.shuffle(points, new Random(0));
        for (Point p : points) {
            int x = p.x;
            int y = p.y;

            img.setRGB(x, y, num);
            num++;
        }
        try {
            ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

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

Cận cảnh:

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


29
Bạn gọi một đốm màu xám lớn là "đẹp"?
Doorknob

22
@Doorknob Vâng. Tôi gọi nó là rất đẹp. Tôi thấy thật tuyệt vời khi tất cả các màu sắc có thể được sắp xếp thành một đốm màu xám lớn. Tôi thấy blob thú vị hơn khi tôi phóng to. Với một chút chi tiết hơn, bạn có thể thấy rng của Java không ngẫu nhiên như thế nào. Khi chúng ta phóng to hơn nữa, như ảnh chụp màn hình thứ hai, sẽ thấy rõ có bao nhiêu màu trong vật đó. Khi tôi phóng to hơn nữa, nó trông giống như một chương trình Piet.
Justin

Tôi đã nhận được màu sắc trong các phiên bản nhỏ hơn bằng cách giảm các bit thấp hơn.
Mark Jeronimus

Vâng, các bit thấp hơn đối với r, gbcách riêng biệt, nhưng tôi đã đối phó với chúng như một số.
Justin

Tôi thấy bạn đã tìm ra ma thuật b1t trong câu trả lời tiếp theo của bạn. Về chủ đề, có thể là thử nghiệm thú vị với Randomlớp con của riêng bạn tạo ra các số ngẫu nhiên ít lý tưởng hơn.
Mark Jeronimus

13

C ++ 11

( Cập nhật: chỉ sau đó tôi mới nhận thấy rằng một cách tiếp cận tương tự đã được thử --- với sự kiên nhẫn hơn đối với số lần lặp lại.)

Đối với mỗi pixel, tôi xác định một tập hợp các pixel lân cận. Tôi xác định sự khác biệt giữa hai pixel là tổng bình phương của sự khác biệt R / G / B của chúng. Hình phạt của một pixel nhất định sau đó là tổng của sự khác biệt giữa pixel và các lân cận của nó.

Bây giờ, trước tiên tôi tạo một hoán vị ngẫu nhiên, sau đó bắt đầu chọn các cặp pixel ngẫu nhiên. Nếu hoán đổi hai pixel làm giảm tổng số hình phạt của tất cả các pixel, việc hoán đổi sẽ diễn ra. Tôi lặp lại điều này trong một triệu lần.

Đầu ra ở định dạng PPM mà tôi đã chuyển đổi thành PNG bằng các tiện ích tiêu chuẩn.

Nguồn:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <random>

static std::mt19937 rng;

class Pixel
{
public:
    int r, g, b;

    Pixel() : r(0), g(0), b(0) {}
    Pixel(int r, int g, int b) : r(r), g(g), b(b) {}

    void swap(Pixel& p)
    {
        int r = this->r,  g = this->g,    b = this->b;
        this->r = p.r;    this->g = p.g;  this->b = p.b;
        p.r = r;          p.g = g;        p.b = b;
    }
};

class Image
{
public:
    static const int width = 256;
    static const int height = 128;
    static const int step = 32;
    Pixel pixel[width*height];
    int penalty[width*height];
    std::vector<int>** neighbors;

    Image()
    {
        if (step*step*step != width*height)
        {
            std::cerr << "parameter mismatch" << std::endl;
            exit(EXIT_FAILURE);
        }

        neighbors = new std::vector<int>*[width*height];

        for (int i = 0; i < width*height; i++)
        {
            penalty[i] = -1;
            neighbors[i] = pixelNeighbors(i);
        }

        int i = 0;
        for (int r = 0; r < step; r++)
        for (int g = 0; g < step; g++)
        for (int b = 0; b < step; b++)
        {
            pixel[i].r = r * 255 / (step-1);
            pixel[i].g = g * 255 / (step-1);
            pixel[i].b = b * 255 / (step-1);
            i++;
        }
    }

    ~Image()
    {
        for (int i = 0; i < width*height; i++)
        {
            delete neighbors[i];
        }
        delete [] neighbors;
    }

    std::vector<int>* pixelNeighbors(const int pi)
    {
        // 01: X-shaped structure
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return abs(i) == abs(j); };
        //
        // 02: boring blobs
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return true; };
        //
        // 03: cross-shaped
        //const int iRad = 7, jRad = 7;
        //auto condition = [](int i, int j) { return i==0 || j == 0; };
        //
        // 04: stripes
        const int iRad = 1, jRad = 5;
        auto condition = [](int i, int j) { return i==0 || j == 0; };

        std::vector<int>* v = new std::vector<int>;

        int x = pi % width;
        int y = pi / width;

        for (int i = -iRad; i <= iRad; i++)
        for (int j = -jRad; j <= jRad; j++)
        {
            if (!condition(i,j))
                continue;

            int xx = x + i;
            int yy = y + j;

            if (xx < 0 || xx >= width || yy < 0 || yy >= height)
                continue;

            v->push_back(xx + yy*width);
        }

        return v;
    }

    void shuffle()
    {
        for (int i = 0; i < width*height; i++)
        {
            std::uniform_int_distribution<int> dist(i, width*height - 1);
            int j = dist(rng);
            pixel[i].swap(pixel[j]);
        }
    }

    void writePPM(const char* filename)
    {
        std::ofstream fd;
        fd.open(filename);
        if (!fd.is_open())
        {
            std::cerr << "failed to open file " << filename
                      << "for writing" << std::endl;
            exit(EXIT_FAILURE);
        }
        fd << "P3\n" << width << " " << height << "\n255\n";
        for (int i = 0; i < width*height; i++)
        {
            fd << pixel[i].r << " " << pixel[i].g << " " << pixel[i].b << "\n";
        }
        fd.close();
    }

    void updatePixelNeighborhoodPenalty(const int pi)
    {
        for (auto j : *neighbors[pi])
            updatePixelPenalty(j);
    }

    void updatePixelPenalty(const int pi)
    {
        auto pow2 = [](int x) { return x*x; };
        int pen = 0;
        Pixel* p1 = &pixel[pi];
        for (auto j : *neighbors[pi])
        {
            Pixel* p2 = &pixel[j];
            pen += pow2(p1->r - p2->r) + pow2(p1->g - p2->g) + pow2(p1->b - p2->b);
        }
        penalty[pi] = pen / neighbors[pi]->size();
    }

    int getPixelPenalty(const int pi)
    {
        if (penalty[pi] == (-1))
        {
            updatePixelPenalty(pi);
        }
        return penalty[pi];
    }

    int getPixelNeighborhoodPenalty(const int pi)
    {
        int sum = 0;
        for (auto j : *neighbors[pi])
        {
            sum += getPixelPenalty(j);
        }
        return sum;
    }

    void iterate()
    {
        std::uniform_int_distribution<int> dist(0, width*height - 1);       

        int i = dist(rng);
        int j = dist(rng);

        int sumBefore = getPixelNeighborhoodPenalty(i)
                        + getPixelNeighborhoodPenalty(j);

        int oldPenalty[width*height];
        std::copy(std::begin(penalty), std::end(penalty), std::begin(oldPenalty));

        pixel[i].swap(pixel[j]);
        updatePixelNeighborhoodPenalty(i);
        updatePixelNeighborhoodPenalty(j);

        int sumAfter = getPixelNeighborhoodPenalty(i)
                       + getPixelNeighborhoodPenalty(j);

        if (sumAfter > sumBefore)
        {
            // undo the change
            pixel[i].swap(pixel[j]);
            std::copy(std::begin(oldPenalty), std::end(oldPenalty), std::begin(penalty));
        }
    }
};

int main(int argc, char* argv[])
{
    int seed;
    if (argc >= 2)
    {
        seed = atoi(argv[1]);
    }
    else
    {
        std::random_device rd;
        seed = rd();
    }
    std::cout << "seed = " << seed << std::endl;
    rng.seed(seed);

    const int numIters = 1000000;
    const int progressUpdIvl = numIters / 100;
    Image img;
    img.shuffle();
    for (int i = 0; i < numIters; i++)
    {
        img.iterate();
        if (i % progressUpdIvl == 0)
        {
            std::cout << "\r" << 100 * i / numIters << "%";
            std::flush(std::cout);
        }
    }
    std::cout << "\rfinished!" << std::endl;
    img.writePPM("AllColors2.ppm");

    return EXIT_SUCCESS;
}

Thay đổi bước của hàng xóm cho kết quả khác nhau. Điều này có thể được điều chỉnh trong hàm Image :: pixelNeighbor (). Mã này bao gồm các ví dụ cho bốn tùy chọn: (xem nguồn)

ví dụ 01 ví dụ 02 ví dụ 03 ví dụ 04

Chỉnh sửa: một ví dụ khác tương tự như ví dụ thứ tư ở trên nhưng với nhân lớn hơn và nhiều lần lặp hơn:

ví dụ 05

Thêm một: sử dụng

const int iRad = 7, jRad = 7;
auto condition = [](int i, int j) { return (i % 2==0 && j % 2==0); };

và mười triệu lần lặp, tôi đã nhận được điều này:

ví dụ 06


11

Không phải là mã thanh lịch nhất, nhưng thú vị ở hai cách tính: Tính toán số lượng màu từ các kích thước (miễn là sản phẩm của các kích thước là sức mạnh của hai) và thực hiện công cụ không gian màu trippy:

void Main()
{
    var width = 256;
    var height = 128;
    var colorCount = Math.Log(width*height,2);
    var bitsPerChannel = colorCount / 3;
    var channelValues = Math.Pow(2,bitsPerChannel);
    var channelStep = (int)(256/channelValues);

    var colors = new List<Color>();

    var m1 = new double[,] {{0.6068909,0.1735011,0.2003480},{0.2989164,0.5865990,0.1144845},{0.00,0.0660957,1.1162243}};
    for(var r=0;r<255;r+=channelStep)
    for(var g=0;g<255;g+=channelStep)
    for(var b=0;b<255;b+=channelStep)   
    {
        colors.Add(Color.FromArgb(0,r,g,b));
    }
    var sortedColors = colors.Select((c,i)=>
                            ToLookupTuple(MatrixProduct(m1,new[]{c.R/255d,c.G/255d,c.B/255d}),i))
                            .Select(t=>new
                                            {
                                                x = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 : t.Item1/(t.Item1+t.Item2+t.Item3),
                                                y = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item2/(t.Item1+t.Item2+t.Item3),
                                                z = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item3/(t.Item1+t.Item2+t.Item3),
                                                Y = t.Item2,
                                                i = t.Item4
                                            })
                            .OrderBy(t=>t.x).Select(t=>t.i).ToList();
    if(sortedColors.Count != (width*height))
    {
        throw new Exception(string.Format("Some colors fell on the floor: {0}/{1}",sortedColors.Count,(width*height)));
    }
    using(var bmp = new Bitmap(width,height,PixelFormat.Format24bppRgb))
    {
        for(var i=0;i<colors.Count;i++)
        {
            var y = i % height;
            var x = i / height;

            bmp.SetPixel(x,y,colors[sortedColors[i]]);
        }
        //bmp.Dump(); //For LINQPad use
        bmp.Save("output.png");
    }
}
static Tuple<double,double,double,int>ToLookupTuple(double[] t, int index)
{
    return new Tuple<double,double,double,int>(t[0],t[1],t[2],index);
}

public static double[] MatrixProduct(double[,] matrixA,
    double[] vectorB)
{
    double[] result=new double[3];
    for (int i=0; i<3; ++i) // each row of A
        for (int k=0; k<3; ++k)
            result[i]+=matrixA[i,k]*vectorB[k];
    return result;
}

Một số biến thể thú vị có thể có chỉ bằng cách thay đổi mệnh đề OrderBy:

x:

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

y:

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

z:

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

Y:

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

Tôi ước tôi có thể tìm ra điều gì đã gây ra những dòng lẻ trong ba phần đầu


2
Những dòng lẻ đó có lẽ là sai lệch của một số phương pháp sắp xếp hoặc tra cứu (tìm kiếm nhị phân / quicksort?)
Mark Jeronimus

Tôi thực sự thực sự thích các dòng ở đây.
Jason C

11

Java

Đây là một ý tưởng tốt hơn nhiều. Đây là một số mã Java rất ngắn; phương thức chính chỉ dài 13 dòng:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

/**
 *
 * @author Quincunx
 */
public class AllColorImage {

    public static void main(String[] args) {
        BufferedImage img = new BufferedImage(4096, 4096, BufferedImage.TYPE_INT_RGB);

        for (int r = 0; r < 256; r++) {
            for (int g = 0; g < 256; g++) {
                for (int b = 0; b < 256; b++) {
                    img.setRGB(((r & 15) << 8) | g, ((r >>> 4) << 8 ) | b, (((r << 8) | g) << 8) | b);
                }
            }
        }
        try {
             ImageIO.write(img, "png", new File("Filepath"));
        } catch (IOException ex) {
            Logger.getLogger(AllColorImage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Tạo các khối "bộ chọn màu". Về cơ bản, trong khối thứ nhất r=0, trong khối thứ hai r=1, v.v ... Trong mỗi khối, ggia tăng đối với xbđối với y.

Tôi thực sự thích các toán tử bitwise. Hãy để tôi phá vỡ setRGBtuyên bố:

img.setRGB(((r & 15) << 8) | g, ((r >>> 4) << 8 ) | b, (((r << 8) | g) << 8) | b);

((r & 15) << 8) | g         is the x-coordinate to be set.
r & 15                      is the same thing as r % 16, because 16 * 256 = 4096
<< 8                        multiplies by 256; this is the distance between each block.
| g                         add the value of g to this.

((r >>> 4) << 8 ) | b       is the y-coordinate to be set.
r >>> 4                     is the same thing as r / 16.
<< 8 ) | b                  multiply by 256 and add b.

(((r << 8) | g) << 8) | b   is the value of the color to be set.
r << 8                      r is 8 bits, so shift it 8 bits to the left so that
| g                         we can add g to it.
<< 8                        shift those left 8 bits again, so that we can
| b                         add b

Do các toán tử bitwise, việc này chỉ mất 7 giây để hoàn thành. Nếu r & 15được thay thế bằngr % 16 , phải mất 9 giây.

Tôi đã chọn 4096 x 4096

Đầu ra (ảnh chụp màn hình, quá lớn khác):

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

Đầu ra với nụ cười xấu xa được vẽ trên đó bởi các vòng tròn tự do:

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


2
Liên kết với bản gốc để tôi có thể xác minh tính hợp lệ (đếm màu)
Mark Jeronimus

2
Cười ngả nghiêng! Tôi quên tôi có thể chạy mã Java. Hình ảnh đầu tiên trôi qua và tôi không thể tái tạo hình ảnh thứ hai (lol)
Mark Jeronimus

16
Các vòng tròn tự do đều có cùng màu, không đủ tiêu chuẩn. : P
Nick T

3
@Quincunx +1 nếu bạn có thể vẽ khuôn mặt đáng sợ mà vẫn giữ được yêu cầu về màu sắc!
Jason C

2
@JasonC Xem câu trả lời của tôi. Tín dụng đến Quincunx cho cảm hứng.
Cấp sông St
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.