모든 색상의 이미지


433

allrgb.com 의 이미지와 유사하게 각 픽셀이 고유 한 색상 인 이미지를 만듭니다 (색상이 두 번 사용되지 않고 색상이 누락되지 않음).

스크린 샷 또는 출력 파일 (PNG로 업로드)과 함께 이러한 이미지를 생성하는 프로그램을 제공하십시오.

  • 알고리즘 적으로 이미지를 만듭니다.
  • 이미지는 256 × 128 (또는 스크린 샷을 찍고 256 × 128로 저장할 수있는 그리드)이어야합니다
  • 모든 15 비트 색상 사용 *
  • 외부 입력이 허용되지 않음 (웹 쿼리, URL 또는 데이터베이스도 없음)
  • 내포 된 이미지는 허용 (화상 미세이다 소스 코드, 예를 들어 선택 Piet )
  • 디더링이 허용됩니다
  • 투표에서 이길 수는 있지만 짧은 코드 콘테스트는 아닙니다.
  • 실제로 도전에 직면한다면 512 × 512, 2048 × 1024 또는 4096 × 4096 (3 비트 단위로)하십시오.

득점은 투표로 이루어집니다. 가장 우아한 코드 및 / 또는 흥미로운 알고리즘으로 만든 가장 아름다운 이미지에 투표하십시오.

처음에는 멋진 이미지를 생성 한 다음 사용 가능한 색상 중 하나에 모든 픽셀을 맞추는 2 단계 알고리즘도 허용되지만 우아함을 얻지는 못합니다.

* 15 비트 색상은 32 개의 빨간색, 32 개의 녹색 및 32 개의 파란색을 등거리 단계와 동일한 범위로 혼합하여 만들 수있는 32768 색상입니다. 예 : 24 비트 이미지 (채널당 8 비트)에서 채널당 범위는 0..255 (또는 0..224)이므로 32 개의 동일한 간격으로 음영으로 나눕니다.

명확하게 말하면, 가능한 모든 이미지가 서로 다른 픽셀 위치에서 동일한 색상을 가지기 때문에 이미지 픽셀 배열은 순열이어야합니다. 나는 여기에 사소한 순열을 줄 것이다.

자바 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();
        }
    }
}

여기에 이미지 설명을 입력하십시오

우승자

7 일이 지났으므로 승자를 선언합니다

그러나 결코 이것이 끝났다고 생각하지 마십시오. 저와 모든 독자들은 항상 더 멋진 디자인을 환영합니다. 제작을 중단하지 마십시오.

승자 : 231 표로 fejesjoco


8
"디더링이 허용됩니다"라고 말하면 무슨 뜻입니까? "각 픽셀은 고유 한 색상"이라는 규칙에 대한 예외입니까? 그렇지 않은 경우 다른 방법으로 금지 된 것은 무엇입니까?
피터 테일러

1
즉, 패턴에 색상을 배치 할 수 있으므로 눈으로 볼 때 다른 색상으로 혼합됩니다. 예를 들어, allRGB 페이지의 "clearly all RGB"이미지와 그 밖의 많은 이미지를 참조하십시오.
Mark Jeronimus

8
나는 실제로 사소한 순열 예제가 눈을 즐겁게한다는 것을 알았습니다.
Jason C

2
@ Zom-B Man, 나는이 게시물을 좋아한다. 감사!
Jason C

7
아름다운 결과 / 답변!
EthanB

답변:


534

씨#

중간에 임의의 픽셀을 넣은 다음 가장 유사한 이웃에 임의의 픽셀을 배치하기 시작합니다. 두 가지 모드가 지원됩니다. 최소 선택시 한 번에 하나의 인접 픽셀 만 고려됩니다. 평균 선택의 경우 모든 (1..8)의 평균이 계산됩니다. 최소 선택은 다소 시끄럽고, 평균 선택은 더 흐려 지지만 실제로는 그림처럼 보입니다. 약간의 편집 후, 현재 다소 최적화 된 버전이 있습니다 (병렬 처리도 사용합니다).

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 픽셀 :

왼쪽 상단에서 시작하여 최소 선택 : 256x128 픽셀 :

중간에서 시작하여 256x128 픽셀, 평균 선택 :

다음은 최소 및 평균 선택이 어떻게 작동하는지 보여주는 두 개의 10 프레임 애니메이션 GIF입니다 (256 색으로 만 표시 할 수있는 GIF 형식으로 전환).

mimimum 선택 모드는 블로 브와 같은 작은 파면으로 커져서 모든 픽셀을 채 웁니다. 그러나 평균 모드에서는 두 개의 서로 다른 색의 가지가 나란히 자라기 시작하면 두 개의 다른 색에 충분히 가깝지 않기 때문에 작은 검은 색 틈이 생깁니다. 이러한 차이로 인해 파면이 수십 배 더 커지므로 알고리즘이 훨씬 느려집니다. 그러나 산호가 자라는 것처럼 보이기 때문에 좋습니다. 평균 모드를 낮추면 각각의 새로운 색상이 기존의 각 픽셀과 2-3 배 정도 비교되기 때문에 조금 더 빨라질 수 있습니다. 나는 그것을 최적화하는 다른 방법을 찾지 못한다. 나는 그것이 충분히 좋다고 생각한다.

그리고 큰 매력은 512x512 픽셀 렌더링, 중간 시작, 최소 선택입니다.

난 이걸로 멈출 수 없어! 위 코드에서 색상은 무작위로 정렬됩니다. 우리가 전혀 정렬하지 않거나 색조 ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue()))로 정렬하지 않으면 각각이 시작됩니다 (중간 시작 및 최소 선택).

산호 모양이 끝날 때까지 유지되는 또 다른 조합 : 30 프레임 애니메이션과 함께 평균 선택 순서가 지정된 색조 :

업데이트 : 준비되었습니다 !!!

당신은 고해상도를 원했고, 고해상도를 원했고, 참을성이 없었습니다. 이제 마침내 준비된 생산 품질을 발표하게되어 기쁩니다. 그리고 빅뱅, 멋진 1080p YouTube 동영상으로 출시하고 있습니다! 비디오를 보려면 여기를 클릭하십시오 . 괴짜 스타일을 홍보하기 위해 바이럴하게 만드십시오. 또한 내 블로그 ( http://joco.name/ )에 글을 게시하고 있습니다. 모든 흥미로운 세부 정보, 최적화, 비디오 제작 방법 등에 대한 기술 게시물이 있습니다. 마지막으로 소스를 공유하고 있습니다. 암호 GPL 하의 . 적절한 호스팅이 가장 적합한 곳이므로 커져서 더 이상 위의 답변을 편집하지 않습니다. 릴리스 모드에서 컴파일해야합니다! 이 프로그램은 많은 CPU 코어로 확장됩니다. 4Kx4K 렌더에는 약 2-3GB RAM이 필요합니다.

이제 5-10 시간 안에 큰 이미지를 렌더링 할 수 있습니다. 4Kx4K 렌더가 이미 있습니다. 나중에 게시하겠습니다. 이 프로그램은 많은 발전을 거듭했으며 수많은 최적화가 이루어졌습니다. 또한 누구나 쉽게 사용할 수 있도록 사용자 친화적으로 만들었으며 멋진 명령 줄이 있습니다. 이 프로그램은 결정적으로 무작위이므로 무작위 시드를 사용할 수 있으며 매번 동일한 이미지를 생성합니다.

다음은 큰 렌더링입니다.

내가 좋아하는 512 :


(출처 : joco.name )

비디오에 나타나는 2048 :


(출처 : joco.name )


(출처 : joco.name )


(출처 : joco.name )


(출처 : joco.name )

첫 번째 4096 렌더링 (TODO : 업로드 중이며 내 웹 사이트에서 큰 트래픽을 처리 할 수 ​​없으므로 일시적으로 재배치 됨) :


(출처 : joco.name )


(출처 : joco.name )


(출처 : joco.name )


(출처 : joco.name )


25
이제 멋지다!
Jaa-c

5
아주 좋은 :-D 이제 더 큰 것을 만드십시오!
squeamish ossifrage

20
당신은 진정한 예술가입니다! :)
AL

10
인쇄 비용은 얼마입니까?
primo

16
거대한 렌더링과 1080p 비디오를 작업하고 있습니다. 몇 시간 또는 며칠이 걸릴 것입니다. 누군가 큰 렌더링으로 인쇄물을 만들 수 있기를 바랍니다. 또는 티셔츠 : 한쪽의 코드, 다른 쪽의 이미지. 누구라도 준비 할 수 있습니까?
fejesjoco

248

가공

최신 정보!4096x4096 이미지!

두 프로그램을 결합하여 두 번째 게시물을이 게시물에 병합했습니다.

Dropbox 에서 선택한 이미지의 전체 모음을 찾을 수 있습니다 .(참고 : DropBox는 4096x4096 이미지의 미리보기를 생성 할 수 없습니다. 이미지를 클릭 한 다음 "다운로드"를 클릭하십시오.)

당신이 한 번만 본다면 이것 (타일)을보십시오! 여기는 원래 2048x1024로 축소되었습니다 (그리고 더 아래에 있습니다).

여기에 이미지 설명을 입력하십시오

이 프로그램은 컬러 큐브에서 무작위로 선택된 지점의 경로를 걷고 이미지에서 무작위로 선택된 경로로 그립니다. 많은 가능성이 있습니다. 구성 가능한 옵션은 다음과 같습니다.

  • 컬러 큐브 경로의 최대 길이입니다.
  • 컬러 큐브를 통과하는 최대 단계 (값이 클수록 차이가 커지지 만 끝이 좁을 때 끝까지가는 작은 경로 수는 최소화 됨)
  • 이미지 타일링.
  • 현재 두 가지 이미지 경로 모드가 있습니다.
    • 모드 1 (이 원본 게시물의 모드) : 이미지에서 사용되지 않은 픽셀 블록을 찾아 해당 블록으로 렌더링합니다. 블록은 무작위로 배치되거나 왼쪽에서 오른쪽으로 정렬 될 수 있습니다.
    • 모드 2 (이것으로 병합 한 두 번째 게시물의 모드) : 이미지에서 임의의 시작점을 선택하고 사용되지 않은 픽셀을 통해 경로를 따라 걷습니다. 사용 된 픽셀 주위를 걸을 수 있습니다. 이 모드의 옵션 :
      • 걸어 갈 방향 설정 (직교, 대각선 또는 둘 다).
      • 각 단계 후에 방향을 변경할지 (현재 시계 방향이지만 코드가 유연함) 또는 점유 된 픽셀을 만나면 방향을 바꿀지 여부.
      • 시계 방향 대신 방향 변경 순서를 섞는 옵션.

최대 4096x4096의 모든 크기에서 작동합니다.

전체 처리 스케치는 여기에서 찾을 수 있습니다. Tracer.zip

공간을 절약하기 위해 아래의 동일한 코드 블록에 모든 파일을 붙여 넣었습니다 (하나의 파일에서도 모두 여전히 유효한 스케치입니다). 사전 설정 중 하나를 사용하려면 gPreset할당 에서 색인을 변경하십시오 . 처리 r중에 이것을 실행하면 실행 중에을 눌러 새로운 이미지를 생성 할 수 있습니다 .

  • 업데이트 1 : 사용하지 않는 컬러 / 픽셀을 추적하고 알려진 픽셀을 검색하지 않도록 최적화 된 코드. 2048x1024 생성 시간을 10-30 분에서 약 15 초로 줄이고 4096x4096을 1-3 시간에서 약 1 분으로 줄였습니다. 드롭 박스 소스 및 소스가 아래에서 업데이트되었습니다.
  • 업데이트 2 : 4096x4096 이미지가 생성되지 않던 버그가 수정되었습니다.
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;
  }

}

내가 좋아하는 256x128 이미지의 전체 세트는 다음과 같습니다.

모드 1 :

원래 세트에서 내가 좋아하는 것 (max_path_length = 512, path_step = 2, 랜덤, 2x 표시, 256x128 링크 ) :

여기에 이미지 설명을 입력하십시오

기타 (왼쪽 2 개 정렬, 오른쪽 2 개 임의, 상단 2 개 경로 길이 제한, 하단 2 개 무제한) :

ordlimit randlimit ordnolimit randnolimit

이것은 바둑판 식으로 배열 될 수 있습니다 :

웅장한

모드 2 :

다이아 패 한 벌 꽃들 박스 페이드 Diagover 빅 다이아몬드 박스 2 샤드

이 타일들은 바둑판 식으로 배열 할 수 있습니다 :

큰 타일 다이아몬드 선물 포장

512x512 선택 :

모드 2에서 가장 좋아하는 Tileable diamonds; 이 중 하나에서 경로가 기존 객체 주위를 걷는 방법을 볼 수 있습니다.

여기에 이미지 설명을 입력하십시오

더 큰 경로 단계 및 최대 경로 길이, 타일 가능 :

여기에 이미지 설명을 입력하십시오

랜덤 모드 1, 타일 가능 :

여기에 이미지 설명을 입력하십시오

더 많은 선택 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

모든 512x512 렌더링은 dropbox 폴더 (* _64.png)에서 찾을 수 있습니다.

2048x1024 및 4096x4096 :

이들은 포함하기에 너무 커서 내가 찾은 모든 이미지 호스트를 1600x1200으로 드롭 다운합니다. 현재 4096x4096 이미지 세트를 렌더링 중이므로 곧 더 많은 이미지를 사용할 수 있습니다. 여기에 모든 링크를 포함시키는 대신 드롭 박스 폴더에서 링크를 확인하십시오 (* _128.png 및 * _256.png, 참고 : 4096x4096 링크가 드롭 박스 미리보기에 비해 너무 크면 "다운로드"를 클릭하십시오). 그래도 내가 가장 좋아하는 것 중 일부는 다음과 같습니다.

2048x1024 큰 tileable 다이아몬드 (이 게시물의 시작 부분에 연결된 것과 동일한 다이아몬드 )

2048x1024 다이아몬드 (이것이 마음에 듭니다!), 축소 :

여기에 이미지 설명을 입력하십시오

4096x4096 큰 타일 가능 다이아몬드 (마침내! Dropbox 링크에서 '다운로드'를 클릭하십시오. 미리보기에는 너무 큽니다).

4096x4096 큰 타일 가능 다이아몬드

4096x4096 랜덤 모드 1 : 여기에 이미지 설명을 입력하십시오

4096x4096 다른 멋진 것

업데이트 : 2048x1024 사전 설정 이미지 세트가 완료되고 보관함에 있습니다. 4096x4096 세트는 1 시간 이내에 완료해야합니다.

좋은 것들이 많이 있습니다. 게시 할 것을 고르는 데 어려움을 겪고 있으므로 폴더 링크를 확인하십시오!


6
그것은 일부 미네랄의 클로즈업 전망을 상기시킵니다.
Morwenn

3
콘테스트의 일부는 아니지만, 나는 이것이 멋진 것이라고 생각했다 . 나는 큰 가우시안 블러를 적용하고 포토샵에서 임의 모드 1 사진 중 하나에 자동 대비 향상을 적용했으며 멋진 바탕 화면 배경을 만들었습니다.
Jason C

2
우와,이 멋진 사진입니다!
sevenseacat

2
Gustav Klimt 텍스처를 상기시킵니다.
Kim

2
Dropbox에서 이미지를 핫 링크 할 수 있다는 것을 알고 있습니까? 다운로드 URL을 복사하고 dl=1token_hash=<something>부분을 제거하고 다음 과 같이 이미지에 대한 링크를 만드십시오 [![Alt text of my small preview image](https://i.stack.imgur.com/smallpreview.png)](https://dl.dropbox.com/linktoyourfullsiz‌​eimage.png). 또 다른 팁 : 이미지를 압축 할 수 있습니다 ( TruePNG ( 다운로드 )로 좋은 결과를 얻습니다 ). 이 이미지 에서 파일 크기의 28.1 %를 저장할 수있었습니다 .
user2428118

219

PIL이있는 Python

이것은 기반으로 뉴턴 프랙탈 특별히, Z의 Z의 → 5 - 1 . 5 개의 근이 있고 5 개의 수렴 점이 있기 때문에 사용 가능한 색 공간이 색조를 기준으로 5 개의 영역으로 분할됩니다. 개별 포인트는 먼저 수렴 포인트에 도달하는 데 필요한 반복 횟수를 기준으로 정렬 된 다음 해당 포인트까지의 거리를 기준으로 정렬되며 이전 값에는 더 빛나는 색상이 할당됩니다.

업데이트 : 4096x4096 큰가에 호스팅, 렌더링 allrgb.com .

원본 (33.7MB)

중심의 확대 (실제 크기) :

이 값을 사용하는 다른 관점

xstart = 0
ystart = 0

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

원본 (32.2MB)

그리고 다른 이들은 이것을 사용합니다 :

xstart = 0.5
ystart = 0.5

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

원본 (27.2MB)


생기

요청에 따라 확대 / 축소 애니메이션을 컴파일했습니다.

초점 : ( 0.50051 , -0.50051 )
줌 배율 : 2 1/5

검은 점을 확대하고 싶지 않기 때문에 초점이 약간 홀수입니다. 확대 / 축소 비율은 5 프레임마다 두 배가되도록 선택됩니다.

32x32 티저 :

256x256 버전은 여기에서 볼 수 있습니다 :
http://www.pictureshack.org/images/66172_frac.gif (5.4MB)

수학적으로 "자체로"확대되는 점이있을 수 있으며, 이는 무한한 애니메이션을 가능하게합니다. 식별 할 수 있으면 여기에 추가하겠습니다.


출처

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
마지막으로 프랙탈 :) 그 (것)들을 사랑하십시오. 또한 144 도의 녹색은 내가 가장 좋아하는 색입니다 (120 도의 순수한 녹색과는 반대로 지루합니다).
Mark Jeronimus

2
나는 몰라, 나는 실제로 AllRGB 버전을 더 좋아한다. 전체 휘도 공간을 사용해야하므로 그라디언트가 잘 강조됩니다.
Ilmari Karonen

2
+1 마지막으로 좋은 도형들! 마지막은 내가 개인적으로 좋아하는 것입니다. 비디오를 확대해야합니다! (@Quincunx : 너도 봤다. 1 일째부터 투표권을 가졌다!)
Jason C

1
@JasonC 애니메이션을 추가했습니다;)
primo

2
@primo 나는 늦었다는 것을 알고 있지만 단지이 이미지들이 훌륭하다고 말하고 싶었다.
Ashwin Gupta

130

사용자 fejesjoco의 알고리즘 에서이 아이디어를 얻었고 조금 놀고 싶었으므로 자체 알고리즘을 처음부터 작성하기 시작했습니다.

나는 당신들 중에서 가장 좋은 것보다 더 좋은 것을 만들 수 있다고 생각하기 때문에 이것을 게시하고 있습니다. 나는이 도전이 아직 끝나지 않았다고 생각합니다. 비교하기 위해 allRGB에는 멋진 수준의 디자인이 있으며 여기에서 도달 한 수준을 넘어서는 방법을 생각합니다.

*) 여전히 투표에 의해 결정됩니다

이 알고리즘 :

  1. 가능한 한 검은 색에 가까운 색상으로 (몇 가지) 시드로 시작하십시오.
  2. 방문하지 않은 지점과 방문한 지점에 8 개로 연결된 모든 픽셀 목록을 유지하십시오.
  3. 해당 목록에서 임의의 ** 포인트를 선택하십시오
  4. 계산 된 모든 픽셀의 평균 색상을 계산합니다 [편집 ... 가우시안 커널을 사용하여 9x9 정사각형으로] 8- 연결되어 있습니다 (이것이 너무 매끄럽게 보이는 이유입니다) .
  5. 이 색상을 중심으로 3x3x3 큐브에서 사용되지 않는 색상을 검색하십시오.
    • 여러 색상이 발견되면 가장 어두운 색상을 선택하십시오.
    • 똑같이 어두운 색이 여러 개 발견되면 그 중에서 임의의 색을 선택하십시오.
    • 아무것도 발견되지 않으면 검색 범위를 5x5x5, 7x7x7 등으로 업데이트하십시오. 5에서 반복하십시오.
  6. 픽셀 플롯, 업데이트 목록 및 3에서 반복

또한 선택한 픽셀의 방문 이웃 수를 계산하여 후보 포인트를 선택하는 다른 확률로 실험했지만 더 예쁘게하지 않고 알고리즘 속도를 늦췄습니다. 현재 알고리즘은 확률을 사용하지 않으며 목록에서 임의의 점을 선택합니다. 이로 인해 많은 이웃이있는 포인트가 빠르게 채워지면서 퍼지가 날아가는 확고한 공이됩니다. 또한 나중에 프로세스에서 틈새를 채울 경우 주변 색상을 사용할 수 없게됩니다.

이미지는 환상적입니다.

자바

다운로드 : com.digitalmodular라이브러리

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 종자
  • 1 초

여기에 이미지 설명을 입력하십시오

  • 2048 × 1024
  • 1920 × 1080 데스크탑에 약간 바둑판 식으로 배열
  • 30 초
  • 포토샵에서 부정적인

여기에 이미지 설명을 입력하십시오

  • 2048 × 1024
  • 씨앗 8 개
  • 27 초

여기에 이미지 설명을 입력하십시오

  • 512 × 512
  • 무작위 씨앗 40 개
  • 6 초

여기에 이미지 설명을 입력하십시오

  • 4096 × 4096
  • 씨앗 1 개
  • 줄무늬가 훨씬 더 선명 해집니다 (생선을 생선회로자를 수있는 것처럼 보입니다)
  • 20 분 안에 완료된 것처럼 보였지만 어떤 이유로 완료하지 못했습니다. 이제 밤새 7 개의 인스턴스를 병렬로 실행하고 있습니다.

[아래 참조]

[편집]
** 나는 픽셀을 선택하는 방법이 전혀 무작위 적이 지 않다는 것을 발견했다. 검색 공간의 임의 순열은 임의의 실제 임의보다 빠르다고 생각했습니다 (점은 우연히 두 번 선택되지 않기 때문에 어쨌든 실제 임의의 것으로 대체하면 이미지에서 더 많은 노이즈 반점이 발생합니다.

[30,000 자 이상으로 버전 2 코드가 제거되었습니다.]

여기에 이미지 설명을 입력하십시오

  • 초기 검색 큐브를 5x5x5로 증가

여기에 이미지 설명을 입력하십시오

  • 더 큰 9x9x9

여기에 이미지 설명을 입력하십시오

  • 사고 1. 순열을 비활성화하여 검색 공간이 항상 선형입니다.

여기에 이미지 설명을 입력하십시오

  • 사고 2. fifo 대기열을 사용하여 새로운 검색 기술을 시도했습니다. 여전히 이것을 분석해야하지만 공유 할 가치가 있다고 생각했습니다.

여기에 이미지 설명을 입력하십시오

  • 항상 중앙에서 X 미사용 픽셀 내에서 선택
  • X의 범위는 256 단계로 0에서 8192까지입니다.

이미지를 업로드 할 수 없습니다 : "죄송합니다! 뭔가 나쁜 일이 일어났습니다! 당신이 아닙니다. 우리입니다. 이것은 우리의 잘못입니다." 이미지가 이미지에 비해 너무 큽니다. 다른 곳에서 시도 중 ...

여기에 이미지 설명을 입력하십시오

digitalmodular라이브러리 에서 발견 된 스케줄러 패키지를 실험 하여 픽셀이 처리되는 순서를 (확산 대신) 확인했습니다.

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;
    }
}
  • 앵귤러 (8)

여기에 이미지 설명을 입력하십시오

  • 앵귤러 (64)

여기에 이미지 설명을 입력하십시오

  • CRT

여기에 이미지 설명을 입력하십시오

  • 떨림

여기에 이미지 설명을 입력하십시오

  • 꽃 (5, X), 여기서 X는 X = X × 1.2의 단계에서 0.5 내지 20의 범위이다.

여기에 이미지 설명을 입력하십시오

  • 모드

여기에 이미지 설명을 입력하십시오

  • 피타고라스

여기에 이미지 설명을 입력하십시오

  • 방사형

여기에 이미지 설명을 입력하십시오

  • 무작위

여기에 이미지 설명을 입력하십시오

  • 스캔 라인

여기에 이미지 설명을 입력하십시오

  • Spiral (X), 여기서 X는 0.1 ~ 200 범위에서 X = X × 1.2 단계
  • 방사형에서 각도까지의 범위를 볼 수 있습니다 (5)

여기에 이미지 설명을 입력하십시오

  • 스플릿

여기에 이미지 설명을 입력하십시오

  • SquareSpiral

여기에 이미지 설명을 입력하십시오

  • XOR

여기에 이미지 설명을 입력하십시오

새로운 눈 음식

  • 에 의해 색상 선택의 효과 max(r, g, b)

여기에 이미지 설명을 입력하십시오

  • 에 의해 색상 선택의 효과 min(r, g, b)
  • 이 기능은 위의 기능과 정확히 동일한 기능 / 세부 사항을 가지며 다른 색상으로 만 나타납니다! (같은 랜덤 시드)

여기에 이미지 설명을 입력하십시오

  • 에 의해 색상 선택의 효과 max(r, min(g, b))

여기에 이미지 설명을 입력하십시오

  • 회색 값에 의한 색상 선택 효과 299*r + 436*g + 114*b

여기에 이미지 설명을 입력하십시오

  • 에 의해 색상 선택의 효과 1*r + 10*g + 100*b

여기에 이미지 설명을 입력하십시오

  • 에 의해 색상 선택의 효과 100*r + 10*g + 1*b

여기에 이미지 설명을 입력하십시오

  • 299*r + 436*g + 114*b32 비트 정수로 오버플 로 된 경우 행복한 사고

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

  • 회색 값 및 방사형 스케줄러가있는 변형 3

여기에 이미지 설명을 입력하십시오

  • 나는 이것을 어떻게 만들 었는지 잊었다.

여기에 이미지 설명을 입력하십시오

  • CRT 스케줄러에는 해피 정수 오버플로 버그 (ZIP 업데이트)가 있었기 때문에 중앙 대신 512x512 이미지로 반쯤 시작했습니다. 이것은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

  • InverseSpiralScheduler(64) (새로운)

여기에 이미지 설명을 입력하십시오

  • 다른 XOR

여기에 이미지 설명을 입력하십시오

  • 버그 수정 후 첫 번째 성공적인 4096 렌더링. 나는 이것이 3 버전 SpiralScheduler(1)이거나 다른 것 같아

여기에 이미지 설명을 입력하십시오 (50MB !!)

  • 버전 1 4096이지만 실수로 색상 기준을 설정했습니다. max()

여기에 이미지 설명을 입력하십시오 (50MB !!)

  • 4096, 이제 min()
  • 이 기능은 위의 기능과 정확히 동일한 기능 / 세부 사항을 가지며 다른 색상으로 만 나타납니다! (같은 랜덤 시드)
  • 시간 : 기록하는 것을 잊었지만 파일 타임 스탬프는 이미지 이전 3 분 후

여기에 이미지 설명을 입력하십시오 (50MB !!)


시원한. 최종 이미지는 내가 던진 두 번째 아이디어와 비슷하지만 내 생각만큼 좋지는 않다고 생각합니다. BTW, allrgb.com/diffusive에 비슷한 멋진 것이 있습니다.
Jason C

그것은 단지 티저로 의미되었지만, 분명히 표시되는 것을 두려워하여 편집했습니다 :)
Mark Jeronimus

2
사고조차도 좋아 보인다 :). 컬러 큐브는 매우 좋은 생각처럼 보이고 렌더링 속도는 놀랍습니다. allrgb의 일부 디자인 (예 : allrgb.com/dla)에 대한 자세한 설명이 있습니다. 더 많은 실험을 할 시간이 더 있었으면 좋겠는데, 많은 가능성이 있습니다 ...
fejesjoco

나는 거의 잊어 버렸고 방금 큰 렌더링 중 일부를 업로드했습니다. 무지개 연기 / 흘린 잉크 중 하나가 allrgb의 모든 것보다 낫습니다. :) 나는 다른 사람들이 그렇게 놀랍지 않다는 것에 동의합니다. 그래서 나는 그들로부터 무언가를 더 만들기 위해 비디오를 만들었습니다. :).
fejesjoco

소스 코드와 Digisoft 라이브러리에 대한 링크를 추가하여 실제로 내 코드를 컴파일 할 수 있습니다
Mark Jeronimus

72

Qt를 가진 C ++

나는 당신에게 버전을 참조하십시오 :

여기에 이미지 설명을 입력하십시오

색상에 정규 분포를 사용하는 경우 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

또는 먼저 빨간색 / 색조로 정렬합니다 (편차가 더 작음).

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

또는 다른 배포판 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

코시 분포 (HSL / 레드) :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

가벼움 (hsl)별로 정렬 된 열 :

여기에 이미지 설명을 입력하십시오

업데이트 된 소스 코드-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;
}

잘 했어요 그러나 image.pixel(x, y) == 0첫 번째로 놓인 픽셀을 덮어 쓰지 않아도 될까요?
Mark Jeronimus

@ Zom-B : 가능하지만 마지막 것은 검은 색이어서 규칙 안에 있습니다.
Jaa-c

그래도 규칙 문제는 없습니다. 방금 당신이 그것을 놓친 것 같아요. 1부터 계산할 수도 있습니다. 나는 당신의 다른 사람을 사랑합니다!
Mark Jeronimus 2013

@ ZOM-B는 : 덕분에, 나는 몇 가지 더 추가 할 수 있습니다, 내가 좀 좋아 : P
JAA-C

두 개의 원이 있고 그 아래에있는 것은 원숭이 얼굴처럼 보입니다.
Jason C

64

자바에서 :

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

}

입력 이미지 :

여우 원숭이

나는 이와 같은 것을 생성한다 :

산성 여우 원숭이

압축되지 않은 버전은 여기 : https://www.mediafire.com/?7g3fetvaqhoqgh8

4096 ^ 2 이미지를 만드는 데 약 30 분이 소요됩니다. 이는 처음 구현 한 32 일에 비해 크게 개선 된 것입니다.


1
아야; 32 일이 우스운 소리로 들리지 않았다 ..... 최적화하기 전에 4k에 대한 fejesjocos의 평균 알고리즘은 아마도 여러 달이 걸렸을 것이다
masterX244

5
나는 그의 펑크 눈썹을 좋아한다!
Level River St

45

BubbleSort가있는 Java

(보통 Bubblesort는 그다지 좋아하지 않지만이 도전에 대해서는 마침내 사용했습니다 :) 4096 단계 간격으로 모든 요소가있는 라인을 생성 한 다음 섞었습니다. 정렬을 통해 가고 각각의 값이 정렬되는 동안 값에 1을 더하여 결과적으로 값을 정렬하고 모든 색상을 얻었습니다.

큰 줄무늬가 제거되도록 소스 코드를 업데이트했습니다
(비트 마법이 필요합니다 : 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;
    }
}

결과:

구 버전

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

출력 미리보기


allRGB 페이지에는 이미 QuickSort 버전이 있습니다.
Mark Jeronimus 2013

1
@ Zom-B Quicksort는 Bubblesort와 다른 알고리즘입니다
masterX244

43

전혀 다른 소용돌이를 포함하는 짝수 및 홀수 프레임으로, 내가 이해하지 못하는 이유로 소용돌이를 만듭니다.

이것은 처음 50 개의 홀수 프레임의 미리보기입니다.

소용돌이 미리보기

PPM에서 데모 전체 색상 범위로 변환 된 샘플 이미지 :

샘플 이미지

나중에 모두 회색으로 블렌딩 되어도 회전하는 것을 볼 수 있습니다 : 더 긴 시퀀스 .

다음과 같이 코딩하십시오. 실행하려면 프레임 번호를 포함하십시오 (예 :

./vortex 35 > 35.ppm

나는 이것을 사용하여 애니메이션 GIF를 얻었습니다.

변환 지연 10`ls * .ppm | 정렬 -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
"알지 못하는 이유"에 대해 일이 발생하면 C라는 것을 알고 있습니다.
Nit

2
예, 일반적으로 나는 무엇을 기대해야하는지 알고 있지만 여기서는 어떤 패턴을 얻을 수 있는지 알아보기 위해 놀았습니다.

8
비교 함수가 삼각형 부등식을 따르지 않기 때문에 보텍스입니다. 예를 들어, r> b, b> g, g> r입니다. mergesort 가이 속성에 의존하기 때문에 Java로 이식 할 수도 없으므로 "비교 방법이 일반 계약을 위반합니다!"라는 예외가 발생합니다.
Mark Jeronimus

2
나는 시도 p->b * 6 - q->g;하지만 그것이 소용돌이를 난파한다면, 그것을 고치지 않을 것입니다!

4
이해할 수없는 이유로 +1합니다.
Jason C

40

자바

512x512의 색상 선택기 변형 우아한 코드가 아닙니다 예쁜 그림을 좋아합니다.

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

작성된대로 다음을 출력합니다.

셔플 없음

로 실행하면 shuffle_block = true각 64x64 블록의 색상이 섞입니다.

블록 셔플

그렇지 않으면로 실행하면 x / y shuffle_radius > 0shuffle_radius에서 임의의 픽셀로 각 픽셀을 섞습니다 . 다양한 크기로 재생 한 후 32 픽셀 반경을 좋아합니다. 너무 많이 움직이지 않고 선을 흐리게 만듭니다.

여기에 이미지 설명을 입력하십시오


3
이 사진이 가장
예뻐요

정말 대단합니다 😍
Matthew

37

가공

방금 C (다른 언어로 프로그래밍 된)를 시작했지만 Visual C의 그래픽을 따르기가 어려워 @ace에서 사용하는 처리 프로그램을 다운로드했습니다.

여기 내 코드와 알고리즘이 있습니다.

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

연산

x, y에서 32 개의 녹색과 파란색의 가능한 모든 조합의 4x4 제곱으로 시작하십시오. 형식, 128x128 정사각형 만들기 각 4x4 정사각형에는 16 픽셀이 있으므로 그 옆에 미러 이미지를 만들어 아래 이미지 당 녹색과 파란색의 가능한 각 조합의 32 픽셀을 제공하십시오.

(변태 전체 녹색 전체 시안보다 밝게 보인다. 이것은 착시해야합니다. 의견에 명확히)

왼쪽 정사각형에 빨간색 값 0-15를 추가하십시오. 오른쪽 정사각형의 경우이 값을 16으로 XOR하여 값을 16-31로 만듭니다.

여기에 이미지 설명을 입력하십시오

출력 256x128

아래 이미지 상단에 출력이 표시됩니다.

그러나 모든 픽셀은 빨강 값의 최상위 비트에서만 미러 이미지와 다릅니다. 따라서 변수를 사용하여 조건을 적용 c하여 XOR을 뒤집을 수 있습니다.이 두 픽셀을 교환하는 것과 동일한 효과가 있습니다.

이에 대한 예는 아래 맨 아래 이미지에 있습니다 (현재 주석 처리 된 코드 행의 주석을 해제 한 경우).

여기에 이미지 설명을 입력하십시오

512 x 512-Andy Warhol의 Marylin에 대한 찬사

이 질문에 대한 Quincunx의 답변에서 자유형 빨간색 원 안에 "사악한 미소"가 그려져있는이 그림은 저의 유명한 그림입니다. 원본은 실제로 25 가지의 컬러 Marylins와 25 가지의 흑백 Marylins를 가지고 있었고, 그녀의 적시의 죽음 이후에 Warhol의 Marylin에 대한 찬사였습니다. http://en.wikipedia.org/wiki/Marilyn_Diptych 참조

Processing이 256x128에서 사용한 것을 반투명으로 렌더링한다는 것을 알게 된 후 다른 기능으로 변경했습니다. 새로운 것은 불투명합니다.

그리고 이미지가 완전히 알고리즘 적이지는 않지만 오히려 좋아합니다.

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");

}

여기에 이미지 설명을 입력하십시오

512x512 거리에서 산들과 호수 위에 황혼

여기에 완전히 알고리즘적인 그림이 있습니다. 조건으로 변조하는 색상을 변경하면서 놀았지만 빨간색이 가장 잘 작동한다는 결론으로 ​​돌아 왔습니다. Marylin 사진과 마찬가지로 산을 먼저 그린 다음 사진에서 밝기를 선택하여 양의 RGB 이미지를 덮어 쓰고 음의 절반으로 복사합니다. 약간의 차이점은 많은 산의 바닥이 모두 같은 크기로 그려지기 때문에 읽기 영역 아래로 확장되므로이 영역은 읽기 프로세스 중에 잘립니다 (따라서 다른 크기의 산에 대해 원하는 인상을줍니다). )

이 예제에서는 긍정적 인 경우 32 개의 빨간색 8x4 셀을 사용하고, 부정적인 경우에는 나머지 32 개의 빨간색을 사용합니다.

내 코드 끝에 expicit 명령 frameRate (1)을 주목하십시오. 이 명령이 없으면 처리가 그리기를 마치더라도 CPU의 한 코어의 100 %를 사용한다는 것을 알았습니다. 절전 기능이 없다고 말할 수있는 한, 폴링 빈도를 줄이면됩니다.

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

여기에 이미지 설명을 입력하십시오


전체 청록색이 아니기 때문입니다. (0,217,217)입니다. 그러나 모든 32 가지 조합이 존재하지만 [0,255]는 늘어나지 않습니다. 편집 : 7 단계를 사용하고 있지만 코드에서 찾을 수 없습니다. 처리해야합니다.
Mark Jeronimus

@steveverrill 처리 save("filename.png")중 현재 프레임 버퍼를 이미지에 저장하기 위해 수행 할 수 있습니다 . 다른 이미지 형식도 지원됩니다. 스크린 샷을 찍는 문제를 해결해줍니다. 이미지가 스케치 폴더에 저장됩니다.
Jason C

@Jasonc 팁 주셔서 감사합니다, 나는 방법이 있어야한다고 확신했지만, 나는 이것을 편집 할 것이라고 생각하지 않습니다. 이미지 주위에 프레임을 부분적으로 남겨서 이미지를 분리했습니다 (이러한 작은 이미지의 경우 2 파일이 과도했습니다.) 512x512로 이미지를 만들고 싶습니다 (특히 아이디어가 있습니다). 당신은 제안합니다.
레벨 리버 세인트

1
@steveverrill Haha, Warhols는 멋진 터치입니다.
Jason C

@ Zom-B Processing은 문서에서 언급하지 않은 많은 작업을 수행하는 것처럼 보입니다. 물리적 출력에 전체 256 논리 색상 채널 값을 사용하지 않고 원하지 않는 색상을 혼합하고 그리기가 끝난 후에도 내 CPU. 여전히 들어가는 것이 간단하고 일단 문제가 있다는 것을 알게되면 이러한 문제를 해결할 수 있습니다 (첫 번째 문제를 제외하고는 아직 해결하지 못했습니다 ...)
Level River St

35

JavaScript 로 Hilbert 곡선 에 모든 16 비트 색상 (5r, 6g, 5b)을 정렬 했습니다.

힐버트 커브 색상

이전 (Hilbert 곡선 아님) 이미지 :

힐버트 커브

JSfiddle : jsfiddle.net/LCsLQ/3

자바 스크립트

// 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);

편집 : 힐버트 곡선을 계산하는 기능에 버그가 있었고 잘못되었습니다. 즉,로 r.x = (p.x & s) > 0; r.y = (p.y & s) > 0;변경r.x = (p.x & s) > 0 ? 1 : 0; r.y = (p.y & s) > 0 ? 1 : 0;

편집 2 : 또 다른 프랙탈 :

시 에르 핀 스키

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


좋은! PPCG에 오신 것을 환영합니다.
Jonathan Van Matre

컬러 큐브를 통과 할 때 3D 힐버트 곡선을 보았을 때 어떤 모습입니까? nm를 편집하십시오 . 누군가 그냥 그랬어
Mark Jeronimus

35

C # : 반복 로컬 유사성 최적화

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

생각

먼저 랜덤 셔플부터 시작합니다 :

여기에 이미지 설명을 입력하십시오

그런 다음 무작위로 두 개의 픽셀을 선택하고 교체합니다. 이것이 인접 픽셀과의 유사성을 증가시키지 않으면, 우리는 다시 교체하고 다시 시도합니다. 우리는이 과정을 반복해서 반복합니다.

단지 몇 세대 (5000) 후에 차이점이 분명하지 않습니다 ...

여기에 이미지 설명을 입력하십시오

그러나 더 오래 실행되면 (25000) ...

여기에 이미지 설명을 입력하십시오

... 더 많은 패턴이 출현하기 시작합니다 (100000).

여기에 이미지 설명을 입력하십시오

이웃 에 대해 다른 정의를 사용하면 이러한 패턴과 패턴이 안정적인지 여부에 영향을 줄 수 있습니다. 이것은 이미지 처리에서 필터에 사용되는Kernel 것과 유사한 매트릭스 입니다. RGB 델타 계산에 사용되는 각 이웃의 가중치를 지정합니다.

결과

다음은 내가 만든 결과 중 일부입니다. 비디오는 반복 프로세스를 보여 주지만 (1 프레임 == 1000 세대) 슬프게도 품질이 좋지 않습니다 (vimeo, YouTube 등은 이러한 작은 크기를 올바르게 지원하지 않습니다). 나중에 더 좋은 품질의 비디오를 만들려고 할 수 있습니다.

0 1 0
1 X 1
0 1 0

185000 세대 :

여기에 이미지 설명을 입력하십시오 비디오 (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 세대 :

여기에 이미지 설명을 입력하십시오 비디오 (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 세대 :

여기에 이미지 설명을 입력하십시오 비디오 (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

이 커널은 비대칭 성 때문에 패턴이 안정적이지 않고 세대가지나면서 전체 이미지가 오른쪽으로 이동하기 때문에 흥미 롭습니다.

2331000 세대 :

여기에 이미지 설명을 입력하십시오 비디오 (01:10)


큰 결과 (512x512)

더 큰 이미지 크기로 위의 커널을 사용하면 더 큰 총 면적에 걸쳐 동일한 로컬 패턴이 생성됩니다. 512x512 이미지는 안정화하는 데 1 ~ 2 백만 세대가 걸립니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


이제 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

이것은 세대당 계산 시간을 크게 증가시킵니다. 171 만 세대와 20 시간 후 :

여기에 이미지 설명을 입력하십시오


1
도착하는 데 시간이 걸리지 만 최종 결과는 매우 좋습니다.
primo

흥미로운 우연의 일치,이 같은 주제에 관한 기사가 있습니다 : nayuki.io/page/simulated-annealing-demo
Nayuki

30

자바

내 다른 답변에 약간의 변형이 있으면 매우 흥미로운 결과를 얻을 수 있습니다.

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

중요한 코드는 다음과 같습니다.

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

});

출력 (스크린 샷) :

여기에 이미지 설명을 입력하십시오

비교기를 다음과 같이 변경하십시오.

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

그리고 우리는 이것을 얻습니다 :

여기에 이미지 설명을 입력하십시오

다른 변형 :

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

여기에 이미지 설명을 입력하십시오

또 다른 변형 (셀룰러 오토마타를 상기시킵니다) :

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

여기에 이미지 설명을 입력하십시오

또 다른 변형 (새로운 개인 즐겨 찾기) :

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

여기에 이미지 설명을 입력하십시오

너무 프랙탈처럼 보입니다. XOR은 매우 아름답고 특히 근접 촬영입니다.

여기에 이미지 설명을 입력하십시오

또 다른 근접 촬영 :

여기에 이미지 설명을 입력하십시오

그리고 이제 Sierpinski Triangle이 기울어졌습니다.

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

여기에 이미지 설명을 입력하십시오


8
첫 번째 이미지는 CPU 또는 메모리 다이 사진처럼 보입니다
Nick T

@NickT 대비를위한 메모리 다이 (Google 이미지에 따름
Justin

4
맞습니다. 메모리는 그렇게 형식이 없습니다. 아마도 매우 멀티 코어 프로세서 일 것입니다. extremetech.com/wp-content/uploads/2012/07/Aubrey_Isle_die.jpg
Nick T

1
나는이 후자를 정말로 좋아한다. 매우 결함이 있지만 기본 구성 구조입니다. XOR 디자인처럼 짠 깔개를 원합니다!
Jonathan Van Matre

이것들은 정말 멋지다. 그들은 깨진 아케이드 게임이나 nes를 나에게 상기시킨다.
Jason C

29

자바

실제로 15 또는 18 비트 색상을 만드는 방법을 잘 모르겠으므로 각 채널 바이트의 최하위 비트를 생략하여 2 ^ 18 개의 서로 다른 24 비트 색상을 만들었습니다. 대부분의 노이즈는 분류를 통해 제거되지만 효과적인 노이즈 제거는 Comparator와 같은 방식으로 한 번에 두 개 이상의 요소를 비교해야하는 것처럼 보입니다. 더 큰 커널을 사용하여 조작을 시도하지만 그 동안 내가 할 수 있었던 최선의 방법입니다.

여기에 이미지 설명을 입력하십시오

HD 이미지 # 2를 보려면 클릭

저해상도 이미지 # 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
그 두 번째는 정말 ... 4096 X 4096 24 비트 버전이 자격
trichoplax

Imgur는 약 30 분 동안 이미지를 처리하고 있습니다. 아마 압축하려고하는 것 같습니다. 어쨌든, 나는 링크를 추가했다 : SSend.it/hj4ovh
John P

2
다운로드에 문제가 있습니다.
SuperJedi224

28

스칼라

L 시스템을 통해 3 차원 힐버트 커브 를 걸어 모든 색상을 주문합니다. . 그런 다음 출력 이미지의 픽셀을 2 차원 힐버트 커브를 따라 걸으며 모든 색상을 배치합니다.

512 x 512 출력 :

여기에 이미지 설명을 입력하십시오

코드는 다음과 같습니다. 그것의 대부분은 피치 / 롤 / 요를 통해 3 차원을 통한 이동의 논리와 수학을 다룹니다. 그 부분을 수행하는 더 좋은 방법이 있었을 것입니다.

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 #에서 이것을 찌르고 랜덤 워크 로직을 통해 모든 단색을 사용하여 약 3 분 (i7 CPU)에 4096x4096 이미지를 생성했습니다.

좋습니다. 코드도 마찬가지입니다. 몇 시간의 연구에 좌절하고 코드의 for 루프를 사용하여 모든 단일 HSL 색상을 생성하려고 시도한 후 HSL 색상을 읽을 플랫 파일을 작성하기로 결정했습니다. 내가 한 일은 모든 RGB 색상을 List로 만든 다음 Hue, Luminosity, Saturation 순으로 주문했습니다. 그런 다음 List를 텍스트 파일로 저장했습니다. ColorData는 RGB 색상을 받아들이고 HSL에 상응하는 것을 저장하는 작은 클래스입니다. 이 코드는 거대한 RAM 먹는 사람입니다. 약 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;
    }


}

그 길을 벗어났습니다. 생성 된 파일에서 다음 색상을 얻기 위해 클래스를 작성했습니다. 색조 시작 및 색조 끝을 설정할 수 있습니다. 실제로 파일을 먼저 정렬 한 차원에 따라 일반화 할 수 있으며 아마 일반화되어야합니다. 또한 여기서 성능 향상을 위해 RGB 값을 파일에 넣고 각 줄을 고정 길이로 유지할 수 있음을 알고 있습니다. 그렇게하면 내가 시작하려는 줄에 도달 할 때까지 모든 줄을 반복하는 대신 바이트 오프셋을 쉽게 지정할 수있었습니다. 그러나 그것은 나에게 많은 성능 히트가 아니 었습니다. 하지만 여기 그 수업이 있습니다

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

이제 컬러 파일이 생겼으며 파일을 읽을 수있는 방법이 생겼으므로 실제로 이미지를 만들 수 있습니다. LockBitmap이라는 비트 맵에서 픽셀 설정 성능을 높이기 위해 찾은 클래스를 사용했습니다. LockBitmap 소스

좌표 위치를 저장하기 위해 작은 Vector2 클래스를 만들었습니다.

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

또한 이웃 픽셀을 찾는 데 도움이되는 SearchArea라는 클래스를 만들었습니다. 이웃을 찾으려고하는 픽셀, 검색 할 경계 및 검색 할 "이웃 사각형"의 크기를 지정합니다. 따라서 크기가 3이면 지정된 픽셀이 가운데에 오른쪽으로 3x3 정사각형을 검색한다는 의미입니다.

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

}

다음 이웃을 실제로 선택하는 클래스가 있습니다. 기본적으로 2 가지 검색 모드가 있습니다. A) 전체 정사각형, B) 정사각형의 경계. 이것은 사각형이 가득 찼음을 깨닫고 전체 사각형을 다시 검색하지 못하도록 최적화되었습니다. DepthMap은 동일한 정사각형을 반복해서 검색하지 못하도록 더욱 최적화되었습니다. 그러나 나는 이것을 완전히 최적화하지 않았다. GetNeighbors에 대한 모든 호출은 항상 전체 제곱 검색을 먼저 수행합니다. 초기 전체 정사각형을 완료 한 후 주변 검색 만 수행하도록 이것을 최적화 할 수 있다는 것을 알고 있습니다. 나는 그 최적화에 아직 도달하지 않았으며, 그것 없이는 코드가 매우 빠릅니다. 주석 처리 된 "잠금"행은 한 지점에서 Parallel.ForEach를 사용했기 때문에 그 롤에 대해 원하는 것보다 더 많은 코드를 작성해야한다는 것을 깨달았습니다.

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

좋아, 이제 이미지를 만드는 클래스가 있습니다.

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


}

다음은 구현 예입니다.

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");

    }
}

RandomWalkSegments = 1 인 경우 기본적으로 사용자가 지시 한 위치를 따라 걷기 시작하고 파일의 첫 번째 첫 번째 색상에서 시작합니다.

내가 인정할 가장 깨끗한 코드는 아니지만 꽤 빠르게 실행됩니다!

자른 출력

3 가지 경로

128 개 경로

편집하다:

그래서 저는 OpenGL과 Shader에 대해 배웠습니다. 두 가지 간단한 셰이더 스크립트를 사용하여 GPU에서 모든 색상을 빠르게 사용하여 4096x4096을 생성했습니다. 결과물은 지루하지만 누군가가이 흥미로운 것을 발견하고 멋진 아이디어를 생각 해낼 수있을 것이라고 생각했습니다.

정점 셰이더

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

프 래그 셰이더

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
}

편집 (10/15/16) : 그냥 유전자 알고리즘의 개념 증명을 보여주고 싶었습니다. 24 시간 후에 100x100의 임의의 색상 세트 에서이 코드를 계속 실행하고 있지만 지금까지 출력이 아름답습니다!여기에 이미지 설명을 입력하십시오

편집 (10/26/16) : 나는 12 일 동안 유전자 알고리즘 코드를 실행했으며 여전히 출력을 최적화하고 있습니다. 기본적으로 로컬 최소값으로 수렴되었지만 여전히 더 많은 개선 사항을 찾고 있습니다.여기에 이미지 설명을 입력하십시오

편집 : 8/12/17-나는 새로운 임의의 도보 알고리즘을 작성-기본적으로 당신은 많은 "보행자"를 지정하지만 무작위로 걷는 대신-무작위로 다른 보행자를 선택하고 그들을 피할 것입니다 (가장 멀리있는 다음 픽셀을 선택하십시오) )-또는 그들쪽으로 걸어갑니다 (가장 가까운 다음 사용 가능한 픽셀을 선택하십시오). 그레이 스케일 출력의 예는 다음과 같습니다 (색칠을 한 후에 전체 4096x4096 컬러 렌더링을 수행합니다!) :여기에 이미지 설명을 입력하십시오


4
조금 뒤 늦었지만 PPCG에 오신 것을 환영합니다! 이것은 훌륭한 첫 번째 게시물입니다.
스파게티

1
감사합니다! 더 많은 도전을 완수하기를 기대합니다! 나는 나의 새로운 취미의, 최근 이상의 이미지 코딩 물건을 해왔
applejacks01

와우 이거 대단해요. 오늘이 게시물로 돌아와서 모든 내용을 확인했습니다.
Jason C

감사합니다! 실제로 흥미로운 그래디언트를 생성하기 위해 유전자 알고리즘 코딩을하고 있습니다. 기본적으로 10000 색을 취해 100x100 격자를 만듭니다. 각 픽셀마다 인접 픽셀을 가져옵니다. 각각에 대해 CIEDE2000 거리를 얻으십시오. 요약하자. 모든 10000 픽셀에 대해 요약하십시오. 유전자 알고리즘은 총합을 줄입니다. 느린는하지만, 20 × 20 이미지를 출력 정말 재미있다
applejacks01

나는 특히이 솔루션의 출력을 좋아합니다.
r_alex_hall

22

HTML5 캔버스 + JavaScript

randoGraph라고 부르며 여기에서 원하는만큼 만들 수 있습니다

몇 가지 예 :

예 1

예 2

예 3

예 4

예 5

예 6

예 7

예를 들어 Firefox에서는 캔버스를 마우스 오른쪽 버튼으로 클릭하고 (완료되면) 이미지로 저장할 수 있습니다. 4096x4096 이미지를 생성하는 것은 일부 브라우저 메모리 제한으로 인해 일종의 문제입니다.

아이디어는 매우 간단하지만 각 이미지는 고유합니다. 먼저 색상 팔레트를 만듭니다. 그런 다음 X 포인트로 시작하여 팔레트에서 임의의 색상을 선택하고 그 색상에 대한 위치를 지정할 때마다 (팔레트에서 색상을 삭제할 때마다) 다음 픽셀에서 동일한 위치에 놓지 않도록 위치를 기록합니다.

접하는 모든 픽셀에 대해 가능한 색상 수 (X)를 만든 다음 해당 픽셀과 가장 관련성이 높은 것을 선택합니다. 이미지가 완성 될 때까지 계속됩니다.

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>

그리고 randoGraph.js의 JavaScript

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

훌륭하지만 fejesjocoC # 답변 처럼 보입니다 . 우연입니까?
AL

1
알고리즘은 여기 있으며 누구나 실제로 다른 것을 읽고 이해할 수 있습니다. 이 답변은 fejesjoco의 C # 답변이 승자로 선언 된 후 결과가 얼마나 멋진 지에 대해 발표 한 후에 게시되었습니다. 그런 다음 이웃 색상을 처리하고 선택하는 완전히 다른 접근법을 생각했습니다. 물론 두 대답은 가시 스펙트럼을 따라 사용되는 색상의 균일 한 분포, 관련 색상의 개념 및 시작점과 같은 동일한 기준을 가지고 있습니다.
konstantinosX

네 대답을 비판한다고 생각하면 미안해 결과 출력이 비슷해지기 때문에 fejesjoco의 답변에서 영감을 얻었는지 궁금합니다.
AL

1
“프로토 타입 체인을 사용하는 대신 생성자 내부에서 클래스의 메소드를 정의하는 것은 특히 비효율적입니다. 특히 해당 클래스가 여러 번 사용되는 경우에는 매우 비효율적입니다.”Patrick Roberts는 매우 흥미로운 의견입니다. 그 유효성을 검사하는 예를 참조하십시오. , 나는이 주장이 (그 사용을 중단하기 위해) 어떤 근거가 있는지, 그리고 그것이 무엇인지 진심으로 알고 싶습니다.
konstantinosX

2
프로토 타입 사용과 관련하여 정적 메서드와 거의 같은 방식으로 작동합니다. 객체 리터럴에 함수가 정의되어 있으면 생성하는 모든 새 객체도 함수의 새 복사본을 만들어 해당 객체 인스턴스와 함께 저장해야합니다. 기억). 이에 비해 프로토 타입을 사용하면 객체가 아닌 "클래스"와 연결되도록 프로토 타입을 한 번만 만듭니다. 이것은 명백한 메모리 이점과 잠재적 인 속도 이점이 있습니다.
Mwr247

20

파이썬

그래서 여기 파이썬에서 내 솔루션이 있습니다. 파이어를 만드는 데 거의 1 시간이 걸리므로 아마도 최적화가 완료되었을 것입니다.

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")

다음은 예제 출력입니다.

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


1
미친 소리 파형처럼 보입니다
Mark Jeronimus

19

자바

나는이 도전을 시도하기로 결정했습니다. 나는 다른 코드 골프에 대한이 답변 에서 영감을 얻었습니다 . 내 프로그램은 못생긴 이미지를 생성하지만 모든 색상을 갖습니다.

또한, 내 첫 타임 코드 골프. :)

(4k 이미지가 작은 업로드 속도에 비해 너무 큽니다. 업로드를 시도했지만 1 시간 후에 업로드되지 않았습니다. 직접 이미지를 생성 할 수 있습니다.)

닫다:

내 컴퓨터에서 70 초 안에 이미지를 생성하고 생성시 약 1.5GB의 메모리를 사용합니다

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

매스 매 티카

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]

결과 (2 배) :

256x128 2 배

원본 256x128 이미지

편집하다:

Log10 [i]를 Log10 [i] / 5로 바꾸면 다음과 같은 이점이 있습니다. 여기에 이미지 설명을 입력하십시오

위의 코드는 시뮬레이션 어닐링과 관련이 있습니다. 이런 식으로 볼 때 두 번째 이미지는 첫 10 ^ 6 단계에서 더 높은 "온도"로 만들어집니다. "온도"가 높을수록 픽셀간에 더 많은 순열이 발생하지만 첫 번째 이미지에서는 정렬 된 이미지의 구조가 여전히 약간 보입니다.


17

자바 스크립트

여전히 학생이자 처음으로 게시하여 내 코드가 지저분하고 내 그림에 필요한 색상이 모두 있는지 100 % 확신하지 못하지만 결과에 만족하여 결과를 게시 할 것이라고 생각했습니다.

나는 대회가 끝났음을 알고 있지만 나는 그 결과를 정말 좋아했으며 항상 재귀 적 역 추적 생성 미로의 모습을 좋아했기 때문에 컬러 픽셀을 배치하면 어떻게 보일지 멋진 것으로 생각했습니다. 따라서 배열에서 모든 색상을 생성하는 것으로 시작한 다음 배열에서 색상을 띄우면서 재귀 역 추적을 수행합니다.

여기 내 JSFiddle이 있습니다 : http://jsfiddle.net/Kuligoawesome/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);
    }
}

256x128 사진, 색상 정렬 빨강-> 녹색-> 파랑
RGB 정렬 색상

256x128 사진, 색상이 파란색-> 녹색-> 빨간색으로 정렬 됨
BGR 정렬 색상

256x128 사진, 색상 정렬 색조-> 휘도-> 채도
HLS 분류 색상

마지막으로 생성 된 GIF
컬러 미로 GIF


가장 밝은 영역에서 색상이 잘려서 복제됩니다. 변경 r * Math.ceil(255 / (colorSteps - 1)r * Math.floor(255 / (colorSteps - 1), 또는 더 나은 : r * 255 / (colorSteps - 1)(테스트되지 않은, 당신은 jsfiddle를 제공하지 않았기 때문에)
마크 Jeronimus

으 아아아, 그래, 문제를 일으킬 느낌이 있었으면 좋겠다. 지금은 고쳐졌고 jsfiddle의 부족에 대해 유감스럽게 생각했다.
Kuligoawesome

나는 이것의 질서있는 혼란 / 소음 보이는 출력과 비슷한 출력을 생성하는 다른 솔루션을 좋아합니다.
r_alex_hall

17

씨#

그래서 나는 이것을 재미있는 운동으로 시작했고 적어도 나에게는 꽤 깔끔한 결과를 얻었습니다. (적어도) 대부분의 다른 솔루션과의 주요 차이점은 순수한 흰색에서 순수한 검정색으로 시작하여 균일하게 간격을 두는 데 필요한 색상 수를 정확하게 생성한다는 것입니다. 또한 안쪽 나선형으로 작동하는 색상을 설정하고 설정된 모든 이웃 간의 색상 차이의 평균을 기반으로 다음 색상을 선택합니다.

여기 지금까지 제작 한 작은 샘플 출력이 있습니다. 4k 렌더 작업을하고 있지만 완료하는 데 하루 이상이 걸릴 것으로 예상합니다.

다음은 256x128의 스펙 ​​출력 샘플입니다.

스펙 렌더

렌더링 시간이 여전히 적당한 일부 큰 이미지 :

360 x 240에서 렌더링

360 x 240으로 두 번째 실행하면 훨씬 부드러운 이미지가 생성됩니다

360 x 240에서 # 2 렌더링

성능을 개선 한 후 2 일이 걸리는 HD 렌더링을 실행할 수 있었지만 아직 4k를 포기하지는 않았지만 몇 주가 걸릴 수 있습니다.

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

색상 선택 알고리즘의 성능을 향상시키는 방법에 대한 의견이 있으시면 360 * 240 렌더링에 약 15 분이 걸리므로 알려주십시오. 나는 그것이 병렬화 될 수 있다고 생각하지 않지만 가장 낮은 색 차이를 얻는 더 빠른 방법이 있는지 궁금합니다.
Phaeze

360 * 240 이미지는 어떻게 '모든 색상'을 구성합니까? 구성 요소 당 cbrt (360 * 240) = 44.208377983684639269357874002958 색상을 어떻게 생산합니까?
Mark Jeronimus

이것이 무슨 언어 지? 알고리즘과 구현에 따라 편향된 결과 나 다음과 같은 예외가 발생할 수 있기 때문에 목록 정렬과 무작위를 무작위로 분류하는 것은 좋지 않은 아이디어 "Comparison method violates its general contract!"입니다 (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. 목록을 무작위 화하려면 제공된 셔플 방법을 사용하십시오. ( colors.Shuffle()?)
Mark Jeronimus

@MarkJeronimus 나는 256x128 이미지에 대한 사양을 놓쳤다는 것을 인정하고, 그 크기를 사용하여 간단한 렌더링을 다시 할 것입니다. 모든 픽셀에 중점을 두었습니다. 다른 제출이했던 것처럼 독특한 색상 측면의 도전과 큰 렌더링입니다.
Phaeze

@ MarkJeronimus 무작위 정렬이 나쁘다는 것을 알고 실제로 많은 의견이 있습니다. 이것은 내가 취하기 시작한 또 다른 접근 방식의 남은 부분이었고 큰 렌더링을 수행하는 데 오랜 시간이 걸리므로 우선 순위를 정했습니다.
Phaeze

16

가다

나에게 또 다른 것이 있는데, 더 흥미로운 결과를 가져 온다고 생각합니다.

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()
}

그것은 내 다른 대답 의 gif와 동일한 패턴으로 시작합니다 . 그런 다음 다음과 같이 섞습니다.

그냥 소음

다소 영감을 얻지 않은 이웃 비교 알고리즘을 반복할수록 무지개 패턴이 더 분명해집니다.

16384는 다음과 같습니다.

16384 회 반복시 소음이 심한 무지개

그리고 65536 :

여기에 이미지 설명을 입력하십시오


6
+1 패턴이 그것에서 나오는 것을 좋아합니다. 당신은 그것의 애니메이션을 만들어야합니다!
Jason C

16

이 이미지는 "Langton 's Rainbow"입니다. 그것들은 오히려 간단하게 그려집니다. Langton의 개미가 움직이면 픽셀을 처음 방문했을 때 각 픽셀에 색상이 그려집니다. 다음에 그릴 색은 단순히 1 씩 증가하여 각 픽셀 당 2 ^ 15 개의 색이 사용되도록합니다.

편집 : 2 ^ 24 색상을 사용하여 4096X4096 이미지를 렌더링하는 버전을 만들었습니다. 색상도 '반사'되어 멋지고 부드러운 그라디언트를 만듭니다. 크기가 크므로 링크 만 제공합니다 (> 28MB).

랭턴 레인보우 라지, 규칙 LR

랭턴 레인보우 라지, 규칙 LLRR

// 편집이 끝났습니다.

클래식 LR 규칙 세트의 경우 :

랭턴 레인보우 LR

LLRR은 다음과 같습니다.

랭턴 레인보우 LLRR

마지막으로 LRRRRRLLR 규칙 세트를 사용합니다.

랭턴 레인보우 LRRRRRLLR

그래픽에 CImg을 사용하여 C ++로 작성되었습니다. 색상이 어떻게 선택되었는지 언급해야합니다. 먼저 RGB 색상 데이터를 포함하기 위해 부호없는 short를 사용합니다. 셀을 처음 방문 할 때마다 비트를 5의 배수로 31만큼 오른쪽으로 시프트 한 다음 8을 곱합니다. 부호없는 짧은 색상은 1 씩 증가합니다. 최대 0에서 248까지의 값이 생성됩니다. 그러나이 값을 뺍니다. 빨간색과 파란색 구성 요소 255에서 때문에 R과 B는 255에서 시작하여 7까지 8의 배수입니다.

c[0]=255-((color&0x1F)*8);
c[2]=255-(((color>>5)&0x1F)*8);
c[1]=(((color>>10)&0x1F)*8);

그러나 0에서 248 사이의 8의 배수 인 녹색 구성 요소에는 적용되지 않습니다. 어떤 경우 든 각 픽셀에는 고유 한 색이 포함되어야합니다.

어쨌든 소스 코드는 다음과 같습니다.

#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
클럽에 오신 것을 환영합니다. code.google.com/p/ruletablerepository/wiki/… 에서 다른 터밋을 시도해 보는 것도 흥미로울 것입니다. (참여했습니다)
Mark Jeronimus

3
Dropbox 공용 폴더를 죽여서 이미지 링크가 끊어 졌습니다.
user2428118

15

루비

계속해서 PNG를 처음부터 새로 만들겠다고 결정했습니다. 흥미로울 것이라고 생각했기 때문입니다. 이 코드는 문자 그대로 raw를 출력합니다. 이진 데이터를 파일로 합니다.

나는 512x512 버전을했다. (알고리즘은 다소 흥미롭지 않습니다.) 내 컴퓨터에서 약 3 초 후에 완료됩니다.

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'

출력 ( all_colors.png) (이 이미지 중 하나를 클릭하여 확대) :

산출

다소 흥미로운 그라디언트 출력 (4 번째 줄에서 마지막 줄을으로 변경 }.shuffle }) :

출력 2

그리고로 변경 }.shuffle }.shuffle하면 미친 컬러 라인이 나타납니다.

출력 3


정말 멋지다. 그래도 더 예쁘게 만들 수있는 방법이 있습니까? 아마도 픽셀을 무작위로 만들 수 있습니까? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian 좋아, 편집
손잡이

훨씬 낫다!!!!!!!

1
네 번째 줄을 마지막 줄로 변경하면 }.shuffle }.shuffle }.shuffle어떻게됩니까?
John Odom

6
@John Erm, 구문 오류일까요?
Doorknob

14

파이썬

혈장

파이썬을 사용하여 휘도별로 색상을 정렬하고, 휘도 패턴을 생성하고 가장 적합한 색상을 선택합니다. 사용 가능한 색상 목록이 작아 질 때 자연스럽게 발생하는 덜 유리한 휘도 일치가 그림 전체에 골고루 퍼지도록 픽셀이 임의 순서로 반복됩니다.

#!/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

자바

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

나는 그렇게하지 않고 모든 색상을 얻는 방법을 알 수 없었기 때문에 4096 x 4096으로 갔다.

산출:

여기에 너무 큽니다. 스크린 샷입니다.

여기에 이미지 설명을 입력하십시오

약간의 변화만으로 더 아름다운 그림을 얻을 수 있습니다.

Collections.shuffle(points, new Random(0));점 생성과 색상 수행 사이에 추가하십시오 .

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

여기에 이미지 설명을 입력하십시오

닫다:

여기에 이미지 설명을 입력하십시오


29
큰 회색 얼룩을 "아름다운"이라고 부릅니까?
Doorknob

22
@Doorknob 예. 나는 그것을 매우 아름답다고 부릅니다. 모든 색상을 큰 회색 얼룩으로 정렬 할 수 있다는 것이 놀랍습니다. 확대 할 때 blob이 더 재미 있다는 것을 알 수 있습니다. 조금 더 자세히 살펴보면 임의의 Java rng가 얼마나되는지 알 수 있습니다. 두 번째 스크린 샷과 같이 더 확대하면 그 색상이 얼마나 많은지 분명해집니다. 더 확대하면 Piet 프로그램처럼 보입니다.
저스틴

더 낮은 비트를 삭제하여 더 작은 버전의 색상을 얻었습니다.
Mark Jeronimus

예, 하위 비트에 대한 r, g그리고 b별도로,하지만 나는 하나 개의 숫자로 취급되었다.
저스틴

다음 답변에서 b1t magicz를 알아 냈습니다. 주제에 Random대해서는 이상적인 난수를 생성하는 자체 하위 클래스를 실험하는 것이 흥미로울 수 있습니다 .
Mark Jeronimus

13

C ++ 11

( 업데이트 : 그 후에야 반복 횟수와 관련하여 더 많은 인내심으로 비슷한 접근법 이 이미 시도되었음을 알았습니다 .)

각 픽셀마다 인접 픽셀 세트를 정의합니다. 두 픽셀 사이의 불일치를 R / G / B 차이의 제곱의 합으로 정의합니다. 주어진 픽셀의 페널티는 픽셀과 인접 픽셀 간의 불일치의 합입니다.

이제 무작위 순열을 생성 한 다음 임의의 픽셀 쌍을 고르기 시작합니다. 두 픽셀을 교체하면 모든 픽셀의 총 페널티의 합이 줄어들면 스왑이 진행됩니다. 나는 이것을 백만 번 반복한다.

출력은 PPM 형식이며 표준 유틸리티를 사용하여 PNG로 변환했습니다.

출처:

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

이웃의 단계를 변화 시키면 다른 결과를 얻을 수 있습니다. 이것은 Image :: pixelNeighbors () 함수에서 조정할 수 있습니다. 이 코드에는 네 가지 옵션에 대한 예제가 포함되어 있습니다 (소스 참조).

예 01 예 02 예 03 예 04

편집 : 위의 네 번째 예제와 비슷하지만 더 큰 커널과 더 많은 반복이있는 또 다른 예 :

예 05

하나 더 : 사용

const int iRad = 7, jRad = 7;
auto condition = [](int i, int j) { return (i % 2==0 && j % 2==0); };

그리고 천만 번의 반복으로, 나는 이것을 얻었습니다.

예 06


11

가장 우아한 코드는 아니지만 두 가지 흥미로운 점 : 치수의 곱이 2의 거듭 제곱 인 한 치수에서 색상 수 계산 및 복잡한 색상 공간 작업 수행 :

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

OrderBy 절을 변경하여 몇 가지 흥미로운 변형을 가질 수 있습니다.

엑스:

여기에 이미지 설명을 입력하십시오

와이:

여기에 이미지 설명을 입력하십시오

지:

여기에 이미지 설명을 입력하십시오

와이:

여기에 이미지 설명을 입력하십시오

처음 세 개의 홀수 라인을 일으키는 원인을 파악할 수 있기를 바랍니다.


2
그 이상한 라인은 아마도 일종의 정렬 또는 조회 방법의 편향입니다 (이진 검색 / 퀵 정렬?)
Mark Jeronimus

나는 실제로 여기의 선을 정말로 좋아한다.
Jason C

11

자바

이것은 훨씬 좋은 아이디어였습니다. 이것은 매우 짧은 Java 코드입니다. 주요 방법은 13 줄입니다.

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

"컬러 피커"블록을 생성합니다. 기본적으로 첫 번째 블록 r=0에서, 두 번째 r=1등에서. 각 블록 에서 및 에 대해 g증분 합니다 .xby

나는 비트 연산자를 정말 좋아합니다. setRGB진술을 세분화하겠습니다 .

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

비트 연산자의 결과로 완료하는 데 7 초 밖에 걸리지 않습니다. r & 15가로 교체 되면 r % 169 초가 걸립니다.

4096 x 4096을 선택했습니다

출력 (스크린 샷, 그렇지 않으면 너무 큼) :

여기에 이미지 설명을 입력하십시오

자유형 빨간색 원으로 그려진 사악한 미소가있는 출력 :

여기에 이미지 설명을 입력하십시오


2
원본 (원본 색상)을 확인할 수 있도록 원본에 연결
Mark Jeronimus

2
롤! Java 코드를 실행할 수 없었습니다. 첫 번째 이미지는 지나가고 두 번째 이미지를 재현 할 수 없습니다 (lol) ☺
Mark Jeronimus

16
프리 핸드 서클은 모두 동일한 색상이며 실격 처리됩니다. : P
Nick T

3
@Quincunx +1 당신이 무서운 얼굴을 그릴 수 있고 여전히 색상 요구 사항을 유지할 수 있다면 !
Jason C

2
@JasonC 내 대답을 참조하십시오. 영감을 얻은 크레딧은 Quincunx입니다.
레벨 리버 세인트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.