ภาพที่มีทุกสี


433

คล้ายกับภาพในallrgb.comสร้างภาพที่แต่ละพิกเซลมีสีที่ไม่ซ้ำกัน (ไม่มีการใช้สีสองครั้งและไม่มีสีหายไป)

มอบโปรแกรมที่สร้างภาพดังกล่าวพร้อมกับภาพหน้าจอหรือไฟล์ของเอาต์พุต (อัปโหลดเป็น PNG)

  • สร้างภาพอัลกอริทึมอย่างหมดจด
  • รูปภาพต้องเป็น 256 × 128 (หรือกริดที่สามารถจับภาพหน้าจอและบันทึกที่ 256 × 128)
  • ใช้สี 15 บิตทั้งหมด *
  • ไม่อนุญาตให้ใช้อินพุตภายนอก (เช่นไม่มีข้อความค้นหาเว็บ URL หรือฐานข้อมูล)
  • ไม่อนุญาตให้ใช้รูปภาพที่ฝังไว้ (ซอร์สโค้ดซึ่งเป็นภาพก็ดีเช่น Piet )
  • อนุญาตให้ทำ Dithering ได้
  • นี่ไม่ใช่การประกวดรหัสสั้น ๆ ถึงแม้ว่ามันจะชนะใจคุณ
  • หากคุณพร้อมท้าทายให้ทำ 512 × 512, 2048 × 1024 หรือ 4096 × 4096 (เพิ่มทีละ 3 บิต)

การให้คะแนนคือการลงคะแนน โหวตภาพที่สวยที่สุดโดยใช้โค้ดที่หรูหราที่สุดและ / หรืออัลกอริทึมที่น่าสนใจ

อัลกอริทึมแบบสองขั้นตอนที่คุณสร้างภาพที่ดีครั้งแรกและจากนั้นปรับพิกเซลทั้งหมดให้พอดีกับสีใดสีหนึ่งที่ได้รับอนุญาตแน่นอน แต่จะไม่ชนะคะแนนความสง่างามของคุณ

* สี 15 บิตเป็นสี 32768 สีที่สามารถทำได้โดยการผสม 32 สีแดง, 32 สีเขียวและ 32 สีฟ้าทั้งหมดในขั้นตอนระยะเท่ากันและช่วงที่เท่ากัน ตัวอย่าง: ในภาพ 24 บิต (8 บิตต่อช่อง) ช่วงต่อช่องคือ 0..255 (หรือ 0..224) ดังนั้นแบ่งออกเป็น 32 เฉดสีที่เว้นระยะเท่ากัน

เพื่อความชัดเจนอาร์เรย์ของพิกเซลภาพควรมีการเปลี่ยนแปลงเนื่องจากภาพที่เป็นไปได้ทั้งหมดมีสีเดียวกัน ฉันจะให้การเรียงสับเปลี่ยนเล็กน้อยที่นี่ซึ่งไม่สวยงามเลย:

Java 7

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

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

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

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

ป้อนคำอธิบายรูปภาพที่นี่

ผู้ชนะ

เพราะ 7 วันผ่านไปฉันประกาศผู้ชนะแล้ว

อย่างไรก็ตามไม่คิดว่ามันจะจบลง ฉันและผู้อ่านทุกคนยินดีต้อนรับการออกแบบที่ยอดเยี่ยมเสมอ อย่าหยุดที่จะสร้าง

ผู้ชนะ: fejesjocoด้วยคะแนนโหวต 231


8
เมื่อคุณพูดว่า "อนุญาตให้ใช้ระบบ Dithering" คุณหมายถึงอะไร นี่เป็นข้อยกเว้นสำหรับกฎ "แต่ละพิกเซลเป็นสีที่ไม่ซ้ำกัน" หรือไม่? ถ้าไม่คุณอนุญาตอะไรที่ถูกห้ามอย่างอื่น
Peter Taylor

1
หมายความว่าคุณสามารถวางสีในรูปแบบได้ดังนั้นเมื่อมองด้วยตาพวกมันจะผสมผสานเป็นสีอื่น ตัวอย่างเช่นดูภาพ "ชัดเจน RGB ทั้งหมด" ในหน้า allRGB และอื่น ๆ อีกมากมายที่นั่น
Mark Jeronimus

8
ฉันพบตัวอย่างการเปลี่ยนแปลงที่ไม่สำคัญของคุณที่จะทำให้ตาของคุณพอใจ
Jason C

2
@ Zom-B Man ฉัน freakin 'รักโพสต์นี้ ขอบคุณ!
Jason C

7
ผลลัพธ์ / คำตอบที่สวยงาม!
EthanB

คำตอบ:


534

C #

ฉันวางพิกเซลแบบสุ่มไว้ตรงกลางแล้วเริ่มวางพิกเซลแบบสุ่มในละแวกที่คล้ายกับพวกเขามากที่สุด รองรับสองโหมด: เมื่อเลือกอย่างน้อยที่สุดจะมีการพิจารณาพิกเซลละหนึ่งพิกเซลเท่านั้น ด้วยการเลือกเฉลี่ยทั้งหมด (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 สีเท่านั้น):

โหมดการเลือกขนาดเล็กสุดจะเติบโตขึ้นด้วยคลื่นขนาดเล็กเช่นหยดเติมพิกเซลทั้งหมดตามที่ไป อย่างไรก็ตามในโหมดเฉลี่ยเมื่อกิ่งที่มีสีต่างกันสองกิ่งเริ่มเติบโตติดกันจะมีช่องว่างสีดำเล็ก ๆ เพราะไม่มีอะไรจะใกล้เคียงกับสีที่ต่างกันสองสี เนื่องจากช่องว่างเหล่านั้นคลื่นจะเป็นลำดับที่มีขนาดใหญ่กว่าดังนั้นอัลกอริทึมจะช้ากว่ามาก แต่ก็ดีเพราะมันดูเหมือนปะการังที่กำลังเติบโต ถ้าฉันจะวางโหมดเฉลี่ยอาจทำได้เร็วขึ้นเล็กน้อยเพราะแต่ละสีใหม่จะถูกนำมาเปรียบเทียบกับแต่ละพิกเซลที่มีอยู่ประมาณ 2-3 ครั้ง ฉันไม่เห็นวิธีอื่นในการปรับให้เหมาะสมฉันคิดว่ามันดีพอเหมือนเดิม

และสิ่งที่ดึงดูดใจใหญ่ ๆ นี่คือการแสดงผล 512x512 พิกเซลจุดเริ่มต้นตรงกลางการเลือกขั้นต่ำ:

ฉันแค่หยุดเล่นไม่ได้! ในรหัสข้างต้นสีจะถูกจัดเรียงแบบสุ่ม หากเราไม่เรียงลำดับทั้งหมดหรือเรียงลำดับตามสี ( (c1, c2) => c1.GetHue().CompareTo(c2.GetHue())) เราจะได้รับสิ่งเหล่านี้ตามลำดับ (ทั้งการเริ่มต้นกลางและการเลือกขั้นต่ำ):

ชุดค่าผสมอื่นที่เก็บรูปแบบปะการังจนถึงจุดสิ้นสุด: ฮิวสั่งด้วยการเลือกโดยเฉลี่ยด้วยแอนิเมชั่น 30 เฟรม:

UPDATE: มันพร้อมแล้ว !!!

คุณต้องการความละเอียดสูงฉันต้องการความละเอียดสูงคุณใจร้อนฉันเพิ่งจะนอนไม่หลับ ตอนนี้ฉันตื่นเต้นที่จะประกาศว่าในที่สุดมันก็พร้อมคุณภาพการผลิต และฉันก็ปล่อยมันออกมาด้วยบิ๊กแบงวิดีโอ YouTube 1080p สุดยอดเยี่ยม! คลิกที่นี่สำหรับวิดีโอลองทำให้เป็นไวรัสเพื่อส่งเสริมสไตล์ที่เกินบรรยาย ฉันยังโพสต์เนื้อหาบนบล็อกของฉันที่http://joco.name/จะมีการโพสต์ทางเทคนิคเกี่ยวกับรายละเอียดที่น่าสนใจทั้งหมดการเพิ่มประสิทธิภาพวิธีการทำวิดีโอ ฯลฯ และในที่สุดฉันก็แบ่งปันแหล่งข้อมูล รหัสภายใต้ GPL มันกลายเป็นเรื่องใหญ่ดังนั้นโฮสติ้งที่เหมาะสมเป็นสถานที่ที่ดีที่สุดสำหรับสิ่งนี้ อย่าลืมคอมไพล์ในโหมด release! โปรแกรมปรับขนาดแกนคอร์ของ CPU ได้ดี เรนเดอร์ 4Kx4K ต้องการ RAM ประมาณ 2-3 GB

ตอนนี้ฉันสามารถแสดงภาพขนาดใหญ่ได้ใน 5-10 ชั่วโมง ฉันมีเรนเดอร์ 4Kx4K แล้วฉันจะโพสต์ในภายหลัง โปรแกรมมีความก้าวหน้าเป็นอย่างมากมีการเพิ่มประสิทธิภาพนับไม่ถ้วน ฉันยังทำให้ผู้ใช้เป็นมิตรเพื่อให้ทุกคนสามารถใช้งานได้อย่างง่ายดายมีบรรทัดคำสั่งที่ดี โปรแกรมนี้ยังสุ่มกำหนดขึ้นซึ่งหมายความว่าคุณสามารถใช้เมล็ดพันธุ์แบบสุ่มและมันจะสร้างภาพเดียวกันทุกครั้ง

ที่นี่มีวาทกรรมขนาดใหญ่

ฉันชื่นชอบ 512:


(ที่มา: joco.name )

ปี 2048 ที่ปรากฏในวิดีโอของฉัน:


(ที่มา: joco.name )


(ที่มา: joco.name )


(ที่มา: joco.name )


(ที่มา: joco.name )

4096 การแสดงครั้งแรก (สิ่งที่ต้องทำ: พวกเขากำลังอัปโหลดและเว็บไซต์ของฉันไม่สามารถรองรับปริมาณการใช้งานจำนวนมากดังนั้นพวกเขาจึงถูกย้ายไปชั่วคราว):


(ที่มา: joco.name )


(ที่มา: joco.name )


(ที่มา: joco.name )


(ที่มา: joco.name )


25
ตอนนี้มันเจ๋ง!
Jaa-c

5
ดีมาก :-D ตอนนี้ทำให้บางอย่างที่ใหญ่กว่า!
ossifrage คลื่นไส้

20
คุณเป็นศิลปินที่แท้จริง! :)
อัล

10
เท่าไหร่สำหรับการพิมพ์?
โม่

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

ป้อนคำอธิบายรูปภาพที่นี่

อื่น ๆ (สั่งให้เหลือสองคำสั่ง, สุ่มสองตัว, จำกัดความยาวเส้นทางสองอันดับแรก, สองอันดับไม่ จำกัด ):

ordlimit randlimit ordnolimit randnolimit

อันนี้สามารถปูกระเบื้อง:

randtile

โหมด 2:

เพชร ดอกไม้ boxfade diagover bigdiamonds boxes2 เศษ

สิ่งเหล่านี้สามารถปูกระเบื้อง:

bigtile diamondtile กระดาษห่อของขวัญ

ตัวเลือก 512x512:

เพชร Tileable, รายการโปรดของฉันจากโหมด 2; คุณสามารถเห็นได้ในวิธีนี้ว่าเส้นทางเดินไปรอบ ๆ วัตถุที่มีอยู่:

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นตอนพา ธ ที่ใหญ่ขึ้นและความยาวพา ธ สูงสุดสามารถกำหนดได้:

ป้อนคำอธิบายรูปภาพที่นี่

โหมดสุ่ม 1, tileable:

ป้อนคำอธิบายรูปภาพที่นี่

ตัวเลือกเพิ่มเติม:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

การเรนเดอร์ 512x512 ทั้งหมดสามารถพบได้ในโฟลเดอร์ดรอปบ็อกซ์ (* _64.png)

2048x1024 และ 4096x4096:

สิ่งเหล่านี้ใหญ่เกินกว่าจะฝังและโฮสต์รูปภาพทั้งหมดที่ฉันพบวางไว้ที่ 1600x1200 ขณะนี้ฉันกำลังแสดงผลชุดภาพขนาด 4096x4096 ภาพจะมีอีกมากในไม่ช้า แทนที่จะรวมลิงค์ทั้งหมดไว้ที่นี่เพียงลองดูที่ลิงค์เหล่านั้นในโฟลเดอร์ดรอปบ็อกซ์ (* _128.png และ * _256.png หมายเหตุ: 4096x4096 นั้นใหญ่เกินไปสำหรับผู้ดูตัวอย่างดรอปบ็อกซ์เพียงคลิก "ดาวน์โหลด") นี่คือบางส่วนของรายการโปรดของฉันคือ:

2048x1024 เพชรเรียงต่อกันขนาดใหญ่ (อันเดียวกับที่ฉันลิงค์ไว้ตอนเริ่มโพสต์นี้)

เพชร 2048x1024 (ฉันชอบอันนี้!) ลดขนาด:

ป้อนคำอธิบายรูปภาพที่นี่

4096x4096 เพชรเรียงต่อกันขนาดใหญ่ (ในที่สุด! คลิก 'ดาวน์โหลด' ในลิงก์ Dropbox มันใหญ่เกินไปสำหรับผู้ดูตัวอย่าง) ปรับขนาดลง:

เพชรขนาดใหญ่เรียงกัน 4096x4096

โหมดสุ่ม 4096x4096 1 : ป้อนคำอธิบายรูปภาพที่นี่

4096x4096 อีกหนึ่งที่ยอดเยี่ยม

อัปเดต: ชุดภาพที่ตั้งไว้ล่วงหน้า 2048x1024 เสร็จสิ้นและอยู่ในดรอปบ็อกซ์ ควรตั้งค่า 4096x4096 ภายในหนึ่งชั่วโมง

มีของดี ๆ มากมายฉันมีช่วงเวลาที่ยากมากที่จะเลือกคนที่จะโพสต์ดังนั้นโปรดตรวจสอบลิงก์ของโฟลเดอร์!


6
มันทำให้ฉันนึกถึงมุมมองอย่างใกล้ชิดของแร่ธาตุบางอย่าง
Morwenn

3
ไม่ใช่ส่วนหนึ่งของการประกวดแต่ฉันคิดว่านี้คือ kinda เย็น ; ฉันใช้เบลอเกาส์ขนาดใหญ่และปรับคอนทราสต์อัตโนมัติให้เป็นหนึ่งในโหมดสุ่ม 1 รูปใน photoshop และมันทำให้พื้นหลังของเดสก์ท็อปดีทีเดียว
Jason C

2
โอ้โหนี่เป็นภาพเจ๋ง ๆ !
sevenseacat

2
เตือนฉันถึงพื้นผิว Gustav Klimt
คิม

2
คุณรู้หรือไม่ว่าคุณสามารถเชื่อมโยงรูปภาพใน Dropbox ได้ เพียงแค่คัดลอก URL ดาวน์โหลดเอาdl=1และส่วนหนึ่งและทำให้การเชื่อมโยงไปยังภาพของคุณเช่นนี้:token_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

Python w / PIL

แห่งนี้ตั้งอยู่บนพื้นฐานของนิวตันเศษส่วนเฉพาะสำหรับZ → Z 5 - 1 เนื่องจากมีห้ารากและจุดบรรจบกันห้าจุดทำให้พื้นที่สีที่มีอยู่ถูกแบ่งออกเป็นห้าภูมิภาคตามเว้ แต่ละจุดจะเรียงลำดับแรกตามจำนวนการวนซ้ำที่ต้องการเพื่อไปยังจุดบรรจบกันแล้วตามระยะทางไปยังจุดนั้นโดยค่าก่อนหน้าจะถูกกำหนดให้มีสีเรืองแสงมากขึ้น

ปรับปรุง: 4096x4096 ขนาดใหญ่แสดงผลโฮสต์บนallrgb.com

ต้นฉบับ (33.7 MB)

ภาพระยะใกล้ของศูนย์กลางภาพมาก (ขนาดจริง):

จุดได้เปรียบอื่นโดยใช้ค่าเหล่านี้:

xstart = 0
ystart = 0

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

ดั้งเดิม (32.2 MB)

และอีกสิ่งหนึ่งที่ใช้สิ่งเหล่านี้:

xstart = 0.5
ystart = 0.5

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

ดั้งเดิม (27.2 MB)


นิเมชั่น

ตามคำขอฉันได้รวบรวมภาพเคลื่อนไหวการซูม

จุดโฟกัส: ( 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 โดยใช้เคอร์เนล Gaussian] เชื่อมต่อ 8 สี (นี่คือเหตุผลที่ทำให้ดูเรียบเนียน)หากไม่พบให้ใช้สีดำ
  5. ในลูกบาศก์ 3x3x3 รอบสีนี้ค้นหาสีที่ไม่ได้ใช้
    • เมื่อพบหลายสีให้นำสีที่มืดที่สุด
    • เมื่อพบว่ามีสีเข้มหลายสีเท่ากันให้นำสีที่สุ่มออกมา
    • เมื่อไม่พบสิ่งใดเลยให้อัปเดตช่วงการค้นหาเป็น 5x5x5, 7x7x7 และทำซ้ำตั้งแต่ 5
  6. พล็อตพิกเซล, รายการอัปเดตและทำซ้ำตั้งแต่ 3

ฉันยังทดลองกับความน่าจะเป็นที่แตกต่างกันในการเลือกคะแนนผู้สมัครจากการนับจำนวนเพื่อนบ้านที่เยี่ยมชมที่มีพิกเซลที่เลือกไว้ แต่มันทำให้อัลกอริทึมช้าลงโดยไม่ทำให้มันสวยกว่า อัลกอริทึมปัจจุบันไม่ได้ใช้ความน่าจะเป็นและเลือกจุดสุ่มจากรายการ สิ่งนี้ทำให้เกิดจุดที่มีเพื่อนบ้านจำนวนมากเติมเต็มอย่างรวดเร็วทำให้มันเป็นเพียงลูกแข็งที่กำลังเติบโตพร้อมกับขอบที่คลุมเครือ สิ่งนี้ยังช่วยป้องกันไม่ให้มีสีใกล้เคียงหากเติมรอยแยกในภายหลังในกระบวนการ

รูปภาพเป็นรูปวงแหวน

ชวา

ดาวน์โหลด: com.digitalmodularlibrary

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 วินาที
  • ลบใน photoshop

ป้อนคำอธิบายรูปภาพที่นี่

  • 2048 × 1024
  • 8 เมล็ด
  • 27 วินาที

ป้อนคำอธิบายรูปภาพที่นี่

  • 512 × 512
  • 40 สุ่มเมล็ด
  • 6 วินาที

ป้อนคำอธิบายรูปภาพที่นี่

  • 4096 × 4096
  • 1 เมล็ด
  • ลายเส้นมีความคมชัดกว่า (อย่างที่ดูเหมือนว่าพวกเขาสามารถสับปลาเป็นซาชิมิ)
  • ดูเหมือนว่ามันจะเสร็จใน 20 นาที แต่ ... ล้มเหลวที่จะเสร็จสิ้นด้วยเหตุผลบางอย่างดังนั้นตอนนี้ฉันกำลังรัน 7 อินสแตนซ์ในแบบคู่ขนานในชั่วข้ามคืน

[ดูด้านล่าง]

[แก้ไข]
** ฉันค้นพบว่าวิธีการเลือกพิกเซลของฉันไม่ได้สุ่มทั้งหมด ฉันคิดว่าการสุ่มเปลี่ยนพื้นที่การค้นหาจะเป็นการสุ่มและเร็วกว่าการสุ่มจริง (เนื่องจากจุดจะไม่ได้รับการเลือกสองครั้งโดยบังเอิญอย่างไรก็ตามการแทนที่ด้วยการสุ่มแบบสุ่มจริง ๆ

[ลบรหัสเวอร์ชั่น 2 ออกเนื่องจากฉันมีอักขระเกินขีด จำกัด สูงสุด 30,000 ตัว]

ป้อนคำอธิบายรูปภาพที่นี่

  • เพิ่มคิวบ์ค้นหาเริ่มต้นเป็น 5x5x5

ป้อนคำอธิบายรูปภาพที่นี่

  • ยิ่งใหญ่กว่าขนาด 9x9x9

ป้อนคำอธิบายรูปภาพที่นี่

  • อุบัติเหตุ 1. ปิดการใช้งานการเปลี่ยนแปลงเพื่อให้พื้นที่การค้นหาเป็นแบบเชิงเส้นเสมอ

ป้อนคำอธิบายรูปภาพที่นี่

  • อุบัติเหตุ 2. พยายามใช้เทคนิคการค้นหาใหม่โดยใช้คิวการติดต่อ ยังต้องวิเคราะห์สิ่งนี้ แต่ฉันคิดว่ามันคุ้มค่าที่จะแบ่งปัน

ป้อนคำอธิบายรูปภาพที่นี่

  • เลือกเสมอภายใน X พิกเซลที่ไม่ได้ใช้จากศูนย์กลาง
  • X อยู่ในช่วงตั้งแต่ 0 ถึง 8192 ในขั้นตอน 256

ไม่สามารถอัปโหลดรูปภาพ: "อ๊ะ! มีบางอย่างผิดปกติเกิดขึ้น! ไม่ใช่คุณเป็นเรานี่คือความผิดของเรา" ภาพมีขนาดใหญ่เกินไปสำหรับ imgur ลองที่อื่น ...

ป้อนคำอธิบายรูปภาพที่นี่

การทดลองกับแพ็คเกจตัวกำหนดตารางเวลาที่ฉันพบใน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 อยู่ในช่วง 0.5 ถึง 20 ในขั้นตอนของ X = X × 1.2

ป้อนคำอธิบายรูปภาพที่นี่

  • พอควร

ป้อนคำอธิบายรูปภาพที่นี่

  • พีทาโกรัส

ป้อนคำอธิบายรูปภาพที่นี่

  • เป็นแฉก

ป้อนคำอธิบายรูปภาพที่นี่

  • สุ่ม

ป้อนคำอธิบายรูปภาพที่นี่

  • scanline

ป้อนคำอธิบายรูปภาพที่นี่

  • Spiral (X) โดยที่ X อยู่ในช่วง 0.1 ถึง 200 ในขั้นตอนของ X = X × 1.2
  • คุณสามารถเห็นมันอยู่ในช่วงระหว่างรัศมีกับเชิงมุม (5)

ป้อนคำอธิบายรูปภาพที่นี่

  • แยก

ป้อนคำอธิบายรูปภาพที่นี่

  • SquareSpiral

ป้อนคำอธิบายรูปภาพที่นี่

  • แฮคเกอร์

ป้อนคำอธิบายรูปภาพที่นี่

ใหม่อาหารตา

  • ผลกระทบของการเลือกสีโดย 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*bล้นในจำนวนเต็ม 32 บิต

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

  • ชุดที่ 3 มีค่าสีเทาและเครื่องมือกำหนดตารางเวลาแบบรัศมี

ป้อนคำอธิบายรูปภาพที่นี่

  • ฉันลืมว่าฉันสร้างสิ่งนี้ได้อย่างไร

ป้อนคำอธิบายรูปภาพที่นี่

  • ตัวกำหนดตารางเวลา CRT ยังมีข้อบกพร่องล้นจำนวนเต็มที่มีความสุข (อัปเดต ZIP) ทำให้การเริ่มครึ่งทางด้วยภาพ 512 × 512 แทนที่จะอยู่ตรงกลาง นี่คือสิ่งที่ควรจะเป็น:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

  • InverseSpiralScheduler(64) (ใหม่)

ป้อนคำอธิบายรูปภาพที่นี่

  • อีกแฮคเกอร์

ป้อนคำอธิบายรูปภาพที่นี่

  • การเรนเดอร์ 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

72

C ++ w / Qt

ฉันเห็นคุณรุ่น:

ป้อนคำอธิบายรูปภาพที่นี่

ใช้การกระจายปกติสำหรับสี:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

หรือเรียงลำดับแรกด้วยสีแดง / สี (มีค่าเบี่ยงเบนเล็ก):

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

หรือการแจกแจงอื่น ๆ :

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

การแจกจ่าย Cauchy (hsl / red):

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

จัดเรียง cols ตามความสว่าง (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

@ Zom-B: ขอบคุณฉันอาจเพิ่มอีกไม่กี่ฉันชอบมัน: P
Jaa-c

คนที่มีวงกลมสองวงและวงที่อยู่ข้างล่างมันรวมกันดูเหมือนจะเป็นใบหน้าของลิง
Jason C

64

ใน Java:

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

import javax.imageio.ImageIO;

public class ImgColor {

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

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

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

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

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

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

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

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

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

}

และภาพอินพุต:

สัตว์จำพวกลิง

ฉันสร้างบางสิ่งเช่นนี้

acidLemur

เวอร์ชันที่ไม่บีบอัดที่นี่: https://www.mediafire.com/?7g3fetvaqhoqgh8

คอมพิวเตอร์ใช้เวลาประมาณ 30 นาทีในการทำภาพ 4096 ^ 2 ซึ่งเป็นการปรับปรุงครั้งใหญ่ในช่วง 32 วันที่การติดตั้งครั้งแรกของฉันจะต้องดำเนินการ


1
อุ๊ยตาย; 32 วันฟังดูไม่ตลก ..... อัลกอริธึมเฉลี่ยใน fejesjocos ตอบ 4k ก่อนการเพิ่มประสิทธิภาพอาจใช้เวลาหลายเดือน
masterX244

5
ฉันรักคิ้วพังก์ของเขา!
เลเวลริเวอร์เซนต์

45

Java พร้อม BubbleSort

(โดยปกติแล้ว 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;
    }
}

แสดงตัวอย่างผลลัพธ์


มีเวอร์ชัน QuickSort อยู่แล้วในหน้า allRGB
Mark Jeronimus

1
@ Zom-B Quicksort เป็นอัลกอริธึมที่แตกต่างจาก Bubblesort
masterX244

43

C

สร้างกระแสน้ำวนด้วยเหตุผลที่ฉันไม่เข้าใจเฟรมคู่และคี่ที่มีกระแสน้ำวนต่างกันโดยสิ้นเชิง

นี่เป็นหน้าตัวอย่างของ 50 เฟรมแรก:

ตัวอย่างกระแสน้ำวน

ภาพตัวอย่างที่แปลงจาก PPM เพื่อสาธิตการครอบคลุมสีสมบูรณ์:

ภาพตัวอย่าง

ต่อมาเมื่อมันคือทั้งหมดที่ผสมลงในสีเทาคุณยังสามารถเห็นมันปั่น: ลำดับอีกต่อไป

รหัสดังนี้ หากต้องการเรียกใช้ให้ใส่หมายเลขเฟรมเช่น:

./vortex 35 > 35.ppm

ฉันใช้สิ่งนี้เพื่อรับภาพเคลื่อนไหว GIF:

แปลง -delay 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 เมื่อสิ่งที่เกิดขึ้นสำหรับ "เหตุผลที่ฉันไม่เข้าใจ"
นิด

2
ใช่ฉันมักจะรู้ว่าจะคาดหวังอะไร แต่ที่นี่ฉันแค่เล่นไปรอบ ๆ เพื่อดูรูปแบบที่ฉันจะได้รับ

8
มันเป็นน้ำวนเพราะฟังก์ชั่นการเปรียบเทียบของคุณไม่เป็นไปตามความไม่เท่าเทียมกันของสามเหลี่ยม ตัวอย่างเช่น r> b, b> g, g> r ฉันไม่สามารถย้ายไปยัง 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:

บล็อกสับเปลี่ยน

มิฉะนั้นถ้าคุณเรียกใช้ด้วยshuffle_radius > 0มันจะสับพิกเซลแต่ละพิกเซลด้วยพิกเซลแบบสุ่มภายในshuffle_radiusใน x / y หลังจากเล่นด้วยขนาดต่าง ๆ ฉันชอบรัศมี 32 พิกเซลเพราะมันพร่ามัวเส้นโดยไม่ย้ายสิ่งต่าง ๆ มากเกินไป:

ป้อนคำอธิบายรูปภาพที่นี่


3
ooh รูปเหล่านี้สวยที่สุด
sevenseacat

สิ่งเหล่านี้ยอดเยี่ยมจริงๆ😍
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);  
  } 
}

ขั้นตอนวิธี

เริ่มต้นด้วยสี่เหลี่ยม 4x4 ของชุดค่าผสมที่เป็นไปได้ทั้งหมด 32 ค่าของสีเขียวและสีน้ำเงินในหน่วย x, y รูปแบบสร้างสี่เหลี่ยมจัตุรัสขนาด 128x128 แต่ละตาราง 4x4 มี 16 พิกเซลดังนั้นให้ทำภาพสะท้อนด้านข้างเพื่อให้ได้ 32 พิกเซลในการรวมกันที่เป็นไปได้ทั้งสีเขียวและสีน้ำเงินต่อภาพด้านล่าง

(พิกลสีเขียวเต็มรูปแบบมีลักษณะที่สว่างกว่าสีฟ้าเต็ม. นี้จะต้องเป็นภาพลวงตา.ชี้แจงในความคิดเห็น)

ในช่องสี่เหลี่ยมเล็ก ๆ ให้เพิ่มค่าสีแดง 0-15 สำหรับจตุรัสทางขวา XOR ค่าเหล่านี้มี 16 เพื่อให้ค่า 16-31

ป้อนคำอธิบายรูปภาพที่นี่

เอาต์พุต 256x128

ซึ่งจะให้ผลลัพธ์ในภาพด้านบนด้านล่าง

อย่างไรก็ตามทุกพิกเซลแตกต่างจากภาพสะท้อนในค่าบิตที่สำคัญที่สุดของค่าสีแดงเท่านั้น ดังนั้นฉันสามารถใช้เงื่อนไขกับตัวแปรcเพื่อย้อนกลับ XOR ซึ่งมีผลเช่นเดียวกับการแลกเปลี่ยนพิกเซลทั้งสองนี้

ตัวอย่างนี้มีให้ในภาพด้านล่างด้านล่าง (หากเราไม่ใส่เครื่องหมายในบรรทัดของโค้ดที่ถูกคอมเม้นท์ไว้ในขณะนี้)

ป้อนคำอธิบายรูปภาพที่นี่

512 x 512 - บรรณาการให้ Marylin ของ Andy Warhol

แรงบันดาลใจจากคำตอบของ Quincunx สำหรับคำถามนี้ด้วย "รอยยิ้มชั่วร้าย" ในวงกลมสีแดงด้วยมือเปล่านี่คือภาพที่มีชื่อเสียงของฉัน ต้นฉบับมีจริง Marylins 25 สีและ Marylins ขาวดำ 25 ตัวและเป็นบรรณาการของ Warhol ต่อ Marylin หลังจากเธอเสียชีวิตก่อนวัยอันควร ดูhttp://en.wikipedia.org/wiki/Marilyn_Diptych

ฉันเปลี่ยนไปใช้ฟังก์ชั่นที่แตกต่างกันหลังจากพบว่าการประมวลผลแสดงผลที่ฉันใช้ใน 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 เชิงบวกขณะที่คัดลอกไปครึ่งเชิงลบ ความแตกต่างเล็กน้อยคือด้านล่างของภูเขาหลายแห่ง (เพราะถูกวาดด้วยขนาดเท่ากัน) ทอดตัวลงมาด้านล่างของพื้นที่อ่านดังนั้นบริเวณนี้จะถูกครอบตัดในระหว่างขั้นตอนการอ่าน )

ในอันนี้ฉันใช้เซลล์ 8x4 32 สีแดงสำหรับค่าบวกและ 32 สีแดงที่เหลือสำหรับค่าลบ

สังเกตคำสั่ง expicit frameRate (1) ที่ท้ายรหัสของฉัน ฉันค้นพบว่าไม่มีคำสั่งนี้การประมวลผลจะใช้ 100% ของหนึ่งคอร์ของ CPU ของฉันแม้ว่ามันจะวาดเสร็จแล้วก็ตาม เท่าที่ฉันสามารถบอกได้ว่าไม่มีฟังก์ชั่นสลีปสิ่งที่คุณทำได้คือลดความถี่ของการลงคะแนนเลือกตั้ง

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 ฮ่าฮ่า Warhols เป็นสิ่งที่ดี
Jason C

@ การประมวลผล Zom-B ดูเหมือนจะทำสิ่งต่าง ๆ ที่ไม่ได้กล่าวถึงในเอกสารประกอบ: (ไม่น่ารำคาญ): ไม่ใช้ 256 ช่องสัญญาณสีแบบลอจิคัลเต็มรูปแบบในผลลัพธ์ทางกายภาพผสมสีเมื่อคุณไม่ต้องการโดยใช้แกนเต็ม CPU ของฉันแม้หลังจากวาดเสร็จแล้ว ยังคงเป็นเรื่องง่ายที่จะเข้ามาและคุณสามารถแก้ไขปัญหาเหล่านี้ได้เมื่อคุณรู้ว่าพวกเขาอยู่ที่นั่น (ยกเว้นข้อแรกฉันยังไม่ได้แก้ปัญหานั้น ... )
Level River St

35

ฉันเพิ่งจัดเรียงสี 16 บิตทั้งหมด (5r, 6g, 5b) บนเส้นโค้ง Hilbertใน JavaScript

สีโค้งของฮิลแบร์ต

ก่อนหน้ารูปภาพ (ไม่ใช่เส้นโค้งของฮิลแบร์ต):

โค้งฮิลเบิร์ต

JSfiddle: jsfiddle.net/LCsLQ/3

JavaScript

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

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

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

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

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

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

แก้ไข : ปรากฎว่ามีข้อผิดพลาดในฟังก์ชันของฉันในการคำนวณเส้นโค้งของฮิลแบร์ตและมันไม่ถูกต้อง กล่าวคือ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:เศษส่วนอื่น:

sierpinsky

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


ดี! ยินดีต้อนรับสู่ PPCG
Jonathan Van Matre

มันดูเหมือนอะไรเมื่อเดินผ่านลูกบาศก์สีเป็นเส้นโค้ง 3D Hilbert แก้ไข 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));
                    }
                }
            }
        }
    }
}

ความคิด

ก่อนอื่นเราเริ่มด้วยการสุ่มแบบสุ่ม:

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นเราสุ่มเลือกสองพิกเซลและสลับ หากสิ่งนี้ไม่ได้เพิ่มความคล้ายคลึงของพิกเซลให้กับเพื่อนบ้านเราจะสลับกลับและลองอีกครั้ง เราทำซ้ำขั้นตอนนี้ซ้ำแล้วซ้ำอีก

หลังจากเพียงไม่กี่ชั่วอายุคน (5,000 ปี) ความแตกต่างนั้นไม่ชัดเจน ...

ป้อนคำอธิบายรูปภาพที่นี่

แต่อีกต่อไปมันทำงาน (25000), ...

ป้อนคำอธิบายรูปภาพที่นี่

... ยิ่งรูปแบบที่แน่นอนเริ่มปรากฏ (100000)

ป้อนคำอธิบายรูปภาพที่นี่

การใช้คำจำกัดความที่แตกต่างกันสำหรับละแวกใกล้เคียงเราสามารถมีอิทธิพลต่อรูปแบบเหล่านี้และไม่ว่าพวกเขาจะมีเสถียรภาพหรือไม่ Kernelเป็นเมทริกซ์คล้ายกับคนที่ใช้สำหรับการกรองในการประมวลผลภาพ มันระบุน้ำหนักของแต่ละเพื่อนบ้านที่ใช้สำหรับการคำนวณเดลต้า RGB

ผล

นี่คือผลลัพธ์บางส่วนที่ฉันสร้าง วิดีโอแสดงกระบวนการวนซ้ำ (1 เฟรม == 1000 รุ่น) แต่น่าเสียดายที่คุณภาพไม่ดีที่สุด (vimeo, YouTube และอื่น ๆ ไม่สนับสนุนขนาดเล็กอย่างเหมาะสม) ฉันอาจลองสร้างวิดีโอที่มีคุณภาพดีกว่าในภายหลัง

0 1 0
1 X 1
0 1 0

185,000 ชั่วอายุคน:

ป้อนคำอธิบายรูปภาพที่นี่ วิดีโอ (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

สิ่งนี้จะเพิ่มเวลาในการคำนวณอย่างมากต่อการสร้าง 1.71 ล้านชั่วอายุคนและ 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;
}

ป้อนคำอธิบายรูปภาพที่นี่

มันดูเป็นเศษส่วนเลย แฮคเกอร์มีความสวยงามมากโดยเฉพาะโคลสอัพ:

ป้อนคำอธิบายรูปภาพที่นี่

โคลสอัพอื่น:

ป้อนคำอธิบายรูปภาพที่นี่

และตอนนี้สามเหลี่ยม Sierpinski เอียง:

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 รูปภาพ) สำหรับความคมชัด: files.macbidouille.com/mbv2/news/news_05_10/25-nm-die.jpg
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 บิตได้อย่างไรดังนั้นฉันจึงเหลือบิตที่มีนัยสำคัญน้อยที่สุดในแต่ละช่องของไบต์เพื่อทำให้สี 24 24 บิต 2 ^ 18 แตกต่างกัน เสียงส่วนใหญ่จะถูกลบออกโดยการเรียงลำดับ แต่การกำจัดสัญญาณรบกวนที่มีประสิทธิภาพดูเหมือนจะต้องมีการเปรียบเทียบมากกว่าสององค์ประกอบในเวลาเดียวกับที่ 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
ที่หนึ่งที่สองจริงๆสมควรที่จะมีรุ่น 24bit 4096 x 4096 ...
Trichoplax

Imgur ประมวลผลภาพประมาณครึ่งชั่วโมง ฉันคิดว่ามันคงพยายามบีบอัดมัน อย่างไรก็ตามฉันได้เพิ่มลิงก์: SSend.it/hj4ovh
John P

2
มีปัญหากับการดาวน์โหลด
SuperJedi224

28

สกาล่า

ผมสั่งซื้อทั้งหมดของสีโดยการเดิน 3 มิติHilbert Curveผ่านทางL-ระบบ จากนั้นฉันก็เดินพิกเซลในภาพที่ส่งออกไปตามแนวโค้งของฮิลแบร์ต 2 มิติและวางสีทั้งหมด

เอาต์พุต 512 x 512:

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือรหัส ส่วนใหญ่ครอบคลุมเพียงตรรกะและคณิตศาสตร์ในการเคลื่อนที่ผ่านสามมิติผ่าน pitch / roll / yaw ฉันแน่ใจว่ามีวิธีที่ดีกว่าในการทำส่วนนั้น แต่ทำได้ดี

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 #

ว้าวสิ่งที่เจ๋งจริงๆในความท้าทายนี้ ฉันแทงที่นี่ใน C # และสร้างภาพ 4096x4096 ในเวลาประมาณ 3 นาที (i7 CPU) โดยใช้ทุกสีผ่านตรรกะ Random Walk

ตกลงดังนั้นสำหรับรหัส หลังจากผิดหวังกับการวิจัยหลายชั่วโมงและพยายามสร้างสี HSL ทุกสีที่ใช้สำหรับลูปในรหัสฉันตัดสินเพื่อสร้างไฟล์แบบแฟลตเพื่ออ่านสี HSL จาก สิ่งที่ฉันทำคือสร้างทุกสี RGB เป็นรายการจากนั้นฉันก็สั่งโดย Hue, Luminosity และ Saturation จากนั้นฉันบันทึกรายการลงในไฟล์ข้อความ ColorData เป็นเพียงคลาสเล็ก ๆ ที่ฉันเขียนซึ่งยอมรับสี RGB และเก็บ HSL ที่เทียบเท่ากัน รหัสนี้เป็นตัวกินแรมขนาดใหญ่ ใช้เกี่ยวกับ 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 เป็นการเพิ่มประสิทธิภาพเพิ่มเติมเพื่อป้องกันการค้นหาสแควร์เดียวกันซ้ำแล้วซ้ำอีก อย่างไรก็ตามฉันไม่ได้ปรับให้เหมาะสมอย่างเต็มที่ การเรียกใช้ GetNeighbor ทุกครั้งจะทำการค้นหาแบบเต็มตารางก่อนเสมอ ฉันรู้ว่าฉันสามารถปรับให้เหมาะสมเพื่อทำการค้นหาปริมณฑลเท่านั้นหลังจากทำตารางเต็มเริ่มต้น ฉันยังไม่ได้รับการเพิ่มประสิทธิภาพนั้นและแม้จะไม่มีรหัสก็ค่อนข้างเร็ว ความคิดเห็นที่ออก "ล็อค" บรรทัดเป็นเพราะฉันใช้ 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 และ Shaders ฉันสร้าง 4096x4096 โดยใช้ทุกสีสว่างอย่างรวดเร็วบน GPU ด้วยสคริปต์ shader 2 ตัว ผลลัพธ์น่าเบื่อ แต่ใคร ๆ ก็คิดว่าน่าสนใจและคิดไอเดียดีๆ:

Vertex Shader

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

Frag Shader

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 สรุปว่าขึ้น สรุปว่าทั้งหมด 10,000 พิกเซล ขั้นตอนวิธีเชิงพันธุกรรมพยายามลดผลรวมทั้งหมด มันช้า แต่สำหรับภาพขนาด 20x20 เอาต์พุตมันน่าสนใจจริงๆ
applejacks01

ฉันรักผลลัพธ์ของการแก้ปัญหานี้เป็นพิเศษ
r_alex_hall

22

HTML5 canvas + 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>

และ JavaScript สำหรับ randoGraph.js

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

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

นั่นเป็นเรื่องดี แต่ดูเหมือนว่าคำตอบ C # จาก fejesjoco เป็นเพราะบังเอิญหรือไม่
อัล

1
อัลกอริทึมอยู่ที่นี่และทุกคนสามารถอ่านและเข้าใจที่แตกต่างกันจริง คำตอบนี้เผยแพร่หลังจากคำตอบ C # จาก fejesjoco ประกาศว่าเป็นผู้ชนะที่ได้รับแรงบันดาลใจจากผลลัพธ์ที่ดี จากนั้นฉันคิดว่าวิธีการประมวลผลและเลือกสีเพื่อนบ้านที่แตกต่างกันทั้งหมดและนี่คือมัน แน่นอนว่าคำตอบทั้งคู่มีพื้นฐานเดียวกันเช่นการกระจายสีที่ใช้ตามสเปกตรัมที่มองเห็นได้อย่างสม่ำเสมอแนวคิดของสีที่เกี่ยวข้องและจุดเริ่มต้นบางทีอาจทำให้ความคิดพื้นฐานเหล่านั้นบางคนคิดว่าภาพที่ผลิตมีความคล้ายคลึงกันในบางกรณี
konstantinosX

โอเคฉันขอโทษถ้าคุณคิดว่าฉันกำลังวิจารณ์คำตอบของคุณ ฉันแค่สงสัยว่าคุณได้รับแรงบันดาลใจจากคำตอบของ fejesjoco หรือไม่เพราะผลลัพธ์ที่ออกมานั้นดูคล้ายกัน
อัล

1
“ การกำหนดวิธีการของชั้นเรียนภายในตัวสร้างแทนการใช้โซ่ต้นแบบนั้นไม่มีประสิทธิภาพจริงๆโดยเฉพาะอย่างยิ่งหากว่ามีการใช้คลาสดังกล่าวหลายครั้ง” เป็นคำวิจารณ์ที่น่าสนใจมากแพทริคโรเบิร์ตส์ คุณมีการอ้างอิงพร้อมตัวอย่างที่ตรวจสอบได้หรือไม่? ฉันขอแสดงความนับถือหากการเรียกร้องนี้มีฐานใด ๆ (เพื่อหยุดใช้สิ่งนั้น) และมันคืออะไร
konstantinosX

2
เกี่ยวกับการใช้งานต้นแบบ: มันทำงานในลักษณะเดียวกับที่วิธีการแบบคงที่จะ เมื่อคุณมีฟังก์ชั่นที่กำหนดไว้ในวัตถุตามตัวอักษรวัตถุใหม่ทุกชิ้นที่คุณสร้างจะต้องสร้างสำเนาใหม่ของฟังก์ชั่นเช่นกันและเก็บไว้ในอินสแตนซ์ของวัตถุนั้น (ดังนั้นวัตถุ 16 ล้านสีหมายถึงฟังก์ชันเดียวกัน หน่วยความจำ) โดยการเปรียบเทียบการใช้ต้นแบบจะสร้างเพียงครั้งเดียวเพื่อเชื่อมโยงกับ "คลาส" แทนที่จะเป็นวัตถุ สิ่งนี้มีประโยชน์หน่วยความจำที่ชัดเจนเช่นเดียวกับผลประโยชน์ความเร็วที่อาจเกิดขึ้น
Mwr247

20

หลาม

ดังนั้นนี่คือวิธีแก้ปัญหาของฉันใน python ใช้เวลาเกือบหนึ่งชั่วโมงในการสร้างมัน

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 นั้นใหญ่เกินไปสำหรับความเร็วในการอัพโหลดขนาดเล็กของฉันฉันลองอัปโหลดหนึ่งรูป แต่หลังจากนั้นหนึ่งชั่วโมงมันไม่ได้อัปโหลดคุณสามารถสร้างของคุณเองได้)

ใกล้ชิด:

สร้างภาพใน 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]

ผลลัพธ์ (2x):

256x128 2x

รูปภาพ 256x128 ดั้งเดิม

แก้ไข:

โดยแทนที่ Log10 [i] ด้วย Log10 [i] / 5 คุณจะได้รับ: ป้อนคำอธิบายรูปภาพที่นี่

รหัสข้างต้นเกี่ยวข้องกับการหลอมจำลอง ด้วยวิธีนี้ภาพที่สองจะถูกสร้างขึ้นด้วย "อุณหภูมิ" ที่สูงขึ้นใน 10 ^ 6 ขั้นตอนแรก "อุณหภูมิ" ที่สูงขึ้นทำให้เกิดการเรียงสับเปลี่ยนระหว่างพิกเซลในขณะที่ในภาพแรกโครงสร้างของภาพที่สั่งยังคงปรากฏให้เห็นเล็กน้อย


17

JavaScript

ยังเป็นนักเรียนและการโพสต์ครั้งแรกของฉันดังนั้นรหัสของฉันอาจยุ่งและฉันไม่แน่ใจ 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)
Mark Jeronimus

อ๊ะใช่ฉันมีความรู้สึกว่ากำลังจะก่อให้เกิดปัญหาหวังว่าจะได้รับการแก้ไขแล้วและขออภัยที่ขาด jsfiddle (ฉันไม่รู้ว่ามันมีอยู่จริง!) ขอบคุณ!
Kuligoawesome

ฉันชอบผลลัพธ์ที่ออกมาวุ่นวายและมีคำสั่งของโซลูชันนี้และอีกวิธีหนึ่งที่ให้ผลลัพธ์ที่คล้ายกัน
r_alex_hall

17

C #

ดังนั้นฉันจึงเริ่มต้นทำสิ่งนี้เช่นเดียวกับการออกกำลังกายที่สนุกสนานและจบลงด้วยผลลัพธ์ที่อย่างน้อยสำหรับฉันก็ดูเรียบร้อยดี ความแตกต่างที่สำคัญในการแก้ปัญหาของฉันต่อ (อย่างน้อยที่สุด) คนอื่น ๆ ส่วนใหญ่คือฉันสร้างจำนวนสีที่ต้องเริ่มต้นด้วยและเว้นระยะระหว่างการสร้างจากสีขาวบริสุทธิ์ไปจนถึงสีดำบริสุทธิ์ ฉันยังตั้งค่าสีที่ทำงานเป็นเกลียวเข้าด้านในและเลือกสีถัดไปตามค่าเฉลี่ยของสีที่ต่างกันระหว่างเพื่อนบ้านทั้งหมดที่ตั้งค่าไว้

นี่คือเอาต์พุตตัวอย่างขนาดเล็กที่ฉันสร้างจนถึงตอนนี้ฉันกำลังสร้างเรนเดอร์ขนาด 4k แต่ฉันคาดว่าจะใช้เวลาเกินหนึ่งวันเพื่อเสร็จสิ้น

นี่คือตัวอย่างของการส่งออกข้อมูลจำเพาะที่ 256x128:

Spec Render

รูปภาพขนาดใหญ่กว่าที่มีเวลาการเรนเดอร์ที่สมเหตุสมผล:

เรนเดอร์ที่ 360 x 240

การวิ่งครั้งที่สองที่ 360 x 240 ให้ภาพที่นุ่มนวลขึ้นมาก

แสดงอันดับ 2 ที่ 360 x 240

หลังจากปรับปรุงประสิทธิภาพแล้วฉันสามารถแสดงภาพ HD ซึ่งใช้เวลา 2 วันฉันยังไม่ยอมแพ้ใน 4k แต่อาจใช้เวลาหลายสัปดาห์

HD Render

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:

สายรุ้งที่มีเสียงดังมากที่ 1,6384 การวนซ้ำ

และ 65536:

ป้อนคำอธิบายรูปภาพที่นี่


6
+1 ฉันชอบที่รูปแบบจะโผล่ออกมาจากสิ่งนั้น คุณควรทำแอนิเมชั่นของมัน!
Jason C

16

ภาพเหล่านี้คือ "สายรุ้งของแลงตัน" พวกมันวาดได้ค่อนข้างง่าย: เมื่อมดเคลื่อนไหวไปแล้วของ Langton สีจะถูกวาดในแต่ละพิกเซลในครั้งแรกที่มีการมองเห็นพิกเซล สีที่จะวาดถัดไปนั้นเพิ่มขึ้นเพียง 1 ทำให้มั่นใจได้ว่าจะใช้สี 2 ^ 15 สีสำหรับแต่ละพิกเซล

แก้ไข: ฉันทำรุ่นที่แสดงภาพ 4096X4096 โดยใช้ 2 ^ 24 สี สีเหล่านี้ก็ 'สะท้อน' เพื่อให้การไล่ระดับสีที่ดีและราบรื่น ฉันจะให้ลิงก์ไปยังพวกเขาเท่านั้นเนื่องจากมีขนาดใหญ่ (> 28 MB)

สายรุ้งขนาดใหญ่ของ Langton ปกครอง LR

สายรุ้งขนาดใหญ่ของ Langton กฎ LLRR

// สิ้นสุดการแก้ไข

สำหรับชุดกฎ LR คลาสสิก:

Rainbow LR ของ Langton

นี่คือ LLRR:

Rainbow LLRR ของ Langton

ในที่สุดอันนี้ใช้ LRetRRRLLR ruleset:

LRRRRRLLR ของ Langton

เขียนใน C ++ โดยใช้ CImg สำหรับกราฟิก ฉันควรพูดถึงวิธีเลือกสีด้วย: อันดับแรกฉันใช้คำย่อที่ไม่ได้ลงชื่อเพื่อเก็บข้อมูลสี RGB ทุกครั้งที่มีการมองเซลล์ครั้งแรกฉันจะเลื่อนบิตโดยผลคูณของ 5 และ 31 จากนั้นคูณด้วย 8 จากนั้นสีสั้นที่ไม่ได้ลงนามจะเพิ่มขึ้น 1 ซึ่งจะสร้างค่าจาก 0 ถึง 248 มากที่สุด อย่างไรก็ตามฉันได้ลบค่านี้จาก 255 ในองค์ประกอบสีแดงและสีน้ำเงินดังนั้น R และ B อยู่ในทวีคูณของ 8 โดยเริ่มจาก 255 ลงไป 7:

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

อย่างไรก็ตามสิ่งนี้ใช้ไม่ได้กับองค์ประกอบสีเขียวซึ่งมีค่าเป็นทวีคูณของ 8 จาก 0 ถึง 248 ในกรณีใด ๆ แต่ละพิกเซลควรมีสีที่ไม่ซ้ำกัน

อย่างไรก็ตามรหัสแหล่งที่มาอยู่ด้านล่าง:

#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
ยินดีต้อนรับและเข้าร่วมคลับ อาจจะน่าสนใจที่จะลอง turmites อื่น ๆ จากcode.google.com/p/ruletablerepository/wiki/… (ฉันเข้าร่วมในนั้น)
Mark Jeronimus

3
ลิงก์รูปภาพตายเพราะ Dropbox ฆ่าโฟลเดอร์สาธารณะ
user2428118

15

ทับทิม

ฉันตัดสินใจว่าจะเดินหน้าต่อและทำ PNG ตั้งแต่เริ่มต้นเพราะฉันคิดว่ามันน่าสนใจ รหัสนี้กำลังแสดงผลข้อมูลไบนารีแบบดิบลงในไฟล์อย่างแท้จริง

ฉันทำรุ่น 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) (คลิกที่ภาพใด ๆ เหล่านี้เพื่อขยายภาพ):

เอาท์พุต

ค่อนข้างเอาท์พุทการไล่ระดับสี - ish ที่น่าสนใจ (โดยการเปลี่ยนบรรทัดที่ 4 เป็นบรรทัดสุดท้าย}.shuffle }):

เอาท์พุท 2

และโดยการเปลี่ยนเป็น}.shuffle }.shuffleคุณจะได้รับเส้นสีบ้า:

เอาท์พุท 3


นั่นเจ๋งจริงๆ มีวิธีที่คุณสามารถทำให้สวยขึ้นกว่าเดิมไหม? อาจจะสุ่มพิกเซล? Scoring is by vote. Vote for the most beautiful images made by the most elegant code.

1
@LowerClassOverflowian Ok แก้ไขแล้ว
Doorknob

ดีกว่ามาก !!!!!!!

1
จะเกิดอะไรขึ้นถ้าคุณเปลี่ยนบรรทัดที่ 4 เป็นบรรทัดสุดท้ายเป็น}.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 โดย 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 ใช่ ฉันเรียกมันว่าสวยงามมาก ฉันพบว่ามันน่าทึ่งที่สีทั้งหมดสามารถจัดเรียงเป็นหยดสีเทาขนาดใหญ่ ฉันพบว่าหยดนั้นน่าสนใจยิ่งขึ้นเมื่อฉันซูมเข้าไปด้วยรายละเอียดที่มากขึ้นคุณจะเห็นว่า rng ของ Java แบบสุ่ม เมื่อเราซูมเข้าไปมากขึ้นเช่นเดียวกับหน้าจอช็อตสองมันจะเห็นชัดเจนว่ามีกี่สีในนั้น เมื่อฉันซูมยิ่งขึ้นดูเหมือนว่าโปรแกรม Piet
Justin

ฉันได้สีในรุ่นที่เล็กลงโดยปล่อยบิตที่ต่ำกว่า
Mark Jeronimus

ใช่บิตที่ต่ำกว่าสำหรับ r , gและbแยกกัน แต่ผมจัดการกับพวกเขาเป็นจำนวนหนึ่ง
Justin

ฉันเห็นคุณคิดว่า te1 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 :: pixelNeighbor () รหัสประกอบด้วยตัวอย่างสำหรับสี่ตัวเลือก: (ดูแหล่งที่มา)

ตัวอย่างที่ 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

ไม่ใช่รหัสที่หรูหราที่สุด แต่น่าสนใจสำหรับสองตัวนับ: การคำนวณจำนวนสีจากมิติ (ตราบใดที่ผลคูณของขนาดเป็นพลังของสอง) และทำพื้นที่สี trippy:

void Main()
{
    var width = 256;
    var height = 128;
    var colorCount = Math.Log(width*height,2);
    var bitsPerChannel = colorCount / 3;
    var channelValues = Math.Pow(2,bitsPerChannel);
    var channelStep = (int)(256/channelValues);

    var colors = new List<Color>();

    var m1 = new double[,] {{0.6068909,0.1735011,0.2003480},{0.2989164,0.5865990,0.1144845},{0.00,0.0660957,1.1162243}};
    for(var r=0;r<255;r+=channelStep)
    for(var g=0;g<255;g+=channelStep)
    for(var b=0;b<255;b+=channelStep)   
    {
        colors.Add(Color.FromArgb(0,r,g,b));
    }
    var sortedColors = colors.Select((c,i)=>
                            ToLookupTuple(MatrixProduct(m1,new[]{c.R/255d,c.G/255d,c.B/255d}),i))
                            .Select(t=>new
                                            {
                                                x = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 : t.Item1/(t.Item1+t.Item2+t.Item3),
                                                y = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item2/(t.Item1+t.Item2+t.Item3),
                                                z = (t.Item1==0 && t.Item2==0 && t.Item3==0) ? 0 :t.Item3/(t.Item1+t.Item2+t.Item3),
                                                Y = t.Item2,
                                                i = t.Item4
                                            })
                            .OrderBy(t=>t.x).Select(t=>t.i).ToList();
    if(sortedColors.Count != (width*height))
    {
        throw new Exception(string.Format("Some colors fell on the floor: {0}/{1}",sortedColors.Count,(width*height)));
    }
    using(var bmp = new Bitmap(width,height,PixelFormat.Format24bppRgb))
    {
        for(var i=0;i<colors.Count;i++)
        {
            var y = i % height;
            var x = i / height;

            bmp.SetPixel(x,y,colors[sortedColors[i]]);
        }
        //bmp.Dump(); //For LINQPad use
        bmp.Save("output.png");
    }
}
static Tuple<double,double,double,int>ToLookupTuple(double[] t, int index)
{
    return new Tuple<double,double,double,int>(t[0],t[1],t[2],index);
}

public static double[] MatrixProduct(double[,] matrixA,
    double[] vectorB)
{
    double[] result=new double[3];
    for (int i=0; i<3; ++i) // each row of A
        for (int k=0; k<3; ++k)
            result[i]+=matrixA[i,k]*vectorB[k];
    return result;
}

รูปแบบที่น่าสนใจบางอย่างสามารถทำได้โดยการเปลี่ยนคำสั่ง OrderBy:

x:

ป้อนคำอธิบายรูปภาพที่นี่

Y:

ป้อนคำอธิบายรูปภาพที่นี่

Z:

ป้อนคำอธิบายรูปภาพที่นี่

Y:

ป้อนคำอธิบายรูปภาพที่นี่

ฉันหวังว่าฉันจะได้รู้ว่าอะไรเป็นสาเหตุของเส้นแปลก ๆ ในสามช่วงแรก


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มีการเพิ่มขึ้นด้วยความเคารพxและbด้วยความเคารพyด้วยความเคารพ

ฉันชอบตัวดำเนินการระดับบิต ให้ฉันทำลาย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 % 16จะใช้เวลา 9 วินาที

ฉันเลือก 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.