अभी भी जीवन को पेंट करें (या एक चलती है) - गेम ऑफ लाइफ में एक छवि बनाएं


36

आपको इनपुट एक greyscale इमेज के रूप में दिया जाता है। आपका कार्य कॉनवे के गेम ऑफ लाइफ में एक स्थिर या लूपिंग पैटर्न ढूंढना है जो इनपुट इमेज को जितना संभव हो उतना करीब से देखता है।

आपका आउटपुट या तो स्टिल इमेज हो सकता है या किसी फॉरमेट में लूपिंग एनीमेशन हो सकता है जिसे जिफ़ में बदला जा सकता है। आउटपुट छवि आयाम इनपुट के समान होना चाहिए, और इसमें केवल काले और सफेद पिक्सेल होने चाहिए।

यदि आउटपुट एक एनीमेशन है, तो प्रत्येक फ्रेम को एक सेल प्रति पिक्सेल के साथ, गेम ऑफ लाइफ के नियमों के अनुसार पिछले एक से उत्पन्न किया जाना चाहिए। एनीमेशन को लूप करना होगा, जिसमें पहले फ्रेम को समान नियमों द्वारा अंतिम फ्रेम से उत्पन्न किया जाएगा।

अगर आउटपुट स्टिल इमेज है, तो गेम-ऑफ-लाइफ रूल्स को लागू करना उसी इमेज को प्रोड्यूस करना है। इसका मतलब यह है कि किसी भी 'जिंदा' सेल में तीन या दो से कम 'जिंदा' पड़ोसी नहीं हो सकते हैं, और किसी भी 'मृत' सेल में तीन 'जिंदा' पड़ोसी नहीं हो सकते हैं। (ध्यान दें कि यह मूल रूप से ऊपर वर्णित एनीमेशन के समान है, लेकिन केवल एक फ्रेम के साथ।)

अतिरिक्त नियम और स्पष्टीकरण:

  • आप (या आपका कार्यक्रम) यह चुन सकते हैं कि क्या 'जीवित' कोशिकाओं को सफेद और 'मृत' को काले रंग के रूप में दर्शाया जाता है, या इसके विपरीत। यही है, आप इसे या तो हार्ड-कोड कर सकते हैं या आपका प्रोग्राम इसे इनपुट इमेज के आधार पर चुन सकता है। (लेकिन यह एनीमेशन के हर फ्रेम के लिए समान होना चाहिए।)

  • सीमा की स्थिति समय-समय पर होनी चाहिए, जिसका अर्थ है कि सबसे दाहिने स्तंभ पर कोशिकाओं में बाएं स्तंभ पर पड़ोसी हैं, आदि।

  • एनिमेशन के लिए, फ्रेम दर आपके (या आपके कार्यक्रम) पर निर्भर है; मुझे लगता है कि ग्रे पिक्सल्स को सन्निकट करने के लिए फास्ट फ्रेम रेट अच्छी तरह से काम करेंगे।

  • कृपया अपने उत्तर में एम्बेडेड कम से कम दो परिणाम पोस्ट करें। यदि आप नीचे दी गई सभी इनपुट छवियों से परिणाम पोस्ट कर सकते हैं, तो यह बेहतर है।

  • यदि छोटे आवश्यक फ़ाइल आकार के साथ gifs प्राप्त करने के लिए यह आवश्यक है तो परीक्षण छवियों को स्केल करना स्वीकार्य है। यदि आप बड़ी फ़ाइलों के साथ लिंक करना चाहते हैं, तो यह ठीक है। यदि आप दिखावा करना चाहते हैं, तो कुछ उच्च-रिज़ॉल्यूशन स्रोत फ़ाइलों को खोजने के लिए स्वतंत्र महसूस करें।

  • कृपया अपने कोड में बहुत सारे नियंत्रणीय मापदंडों से बचने की कोशिश करें - यह सबसे अच्छा है यदि आपके प्रोग्राम का एकमात्र इनपुट छवि है। अपवाद यह है कि यदि आप एनीमेशन फ़्रेम की संख्या को नियंत्रित करने के लिए एक पैरामीटर रखना चाहते हैं, क्योंकि यह फ़ाइल आकार को प्रभावित करेगा।

  • इनपुट और आउटपुट फ़ाइलों के प्रारूप को बदलने के लिए आप बाहरी कार्यक्रमों का उपयोग कर सकते हैं, और / या आउटपुट फ्रेम को एनीमेशन में संकलित कर सकते हैं यदि आप चाहते हैं। (यह फ़ाइल-स्वरूप-हैंडलिंग चुनौती नहीं है।)

  • यह , इसलिए सबसे अधिक वोटों के साथ जवाब जीत जाता है।

यहाँ परीक्षण छवियों का एक चयन है, ज्यादातर इस साइट पर अन्य प्रश्नों से लिया गया है। (यह संभव है कि मैं बाद में अतिरिक्त "बोनस" इनपुट छवियों को जोड़ दूंगा।)

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

बस चीजों को शुरू करने के लिए, यहां पायथन 2 में एक बहुत ही गूंगा संदर्भ प्रयास है, जो इस तथ्य का लाभ उठाता है कि चार वर्गों का एक ब्लॉक गेम ऑफ लाइफ में एक स्थिर संरचना है। यह सिर्फ 4 के कारक द्वारा इनपुट छवि को बचाता है, फिर एक ब्लॉक खींचता है यदि संबंधित पिक्सेल 0.5 से अधिक गहरा हो।

from skimage import io
from skimage import transform
import sys

img = io.imread(sys.argv[1],as_grey=True)

source = transform.resize(img, [i/4 for i in img.shape])

img[:]=1
for x in xrange(source.shape[0]):
    for y in xrange(source.shape[1]):
        if source[x,y]<0.5:
            img[x*4, y*4] = 0
            img[x*4+1, y*4] = 0
            img[x*4, y*4+1] = 0
            img[x*4+1, y*4+1] = 0

io.imsave(sys.argv[2], img)

यहाँ उदाहरण कोड से कुछ आउटपुट हैं। मुझे यकीन है कि बहुत बेहतर परिणाम संभव हैं।

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें


2
यहाँ कुछ उच्च घनत्व अभी भी जीवन खंड हैं: en.wikipedia.org/wiki/… । आप सीमा में घनत्व 1/2 से अधिक नहीं प्राप्त कर सकते हैं।
xnor

आपके उदाहरण में, तीन वर्गों के जंक्शन पर पैदा हुए नए सेल नहीं हैं?
xnor

@xnor ओह, तुम ठीक कह रहे हो। मैं बेहतर है कि उस मामले में अब के लिए उदाहरण निकाल दूंगा। (मुझे कुछ सत्यापन कोड लिखना भी शुरू करना चाहिए!)
नथानिएल

3
यकीन नहीं है कि यह कैसे मदद करेगा, क्योंकि ईडन पैटर्न के बगीचे में से कोई भी अभी भी जीवन नहीं है। (जो उन्हें अपने पूर्ववर्ती बना देगा) इसी कारण से कि वे दोलक भी नहीं हैं।
टैली

1
प्रतियोगियों के लिए कुछ और प्रेरणा: tlrobinson.net/blog/2009/02/game-of-life-generator
Abulafia

जवाबों:


13

अजगर

import sys, random, itertools
from PIL import Image

filename, cutoff = sys.argv[1], int(sys.argv[2]) if len(sys.argv) > 2 else 128

# load command-line arg as image
src = Image.open(sys.argv[1]).convert("L") # grayscale
(w, h), src = src.size, src.load()
# flatten
src = bytearray(src[x, y] for y in range(h) for x in range(w))
size = len(src)
neighbour_offsets = (-w-1,-w,-w+1,-1,1,w-1,w,w+1)    

shapes = set()
max_shape_x, max_shape_y = 0, 0
for shape in (((1, 1), (1, 1), "b"), # block
    ((0,1,1,0),(1,0,0,1),(0,1,1,0), "h"), # hive
    ((0,0,1,0),(0,1,0,1),(1,0,0,1),(0,1,1,0), "l"), # loaf
    ((0,1,0),(1,0,1),(0,1,0), "t"), # tub
    ((1,1,0),(1,0,1),(0,1,0), "B"), # boat
    ((1,1,0),(1,0,1),(0,1,1), "s"), # ship
    ((1,1,0,1,1),(0,1,0,1,0),(0,1,0,1,0),(1,1,0,1,1), "I"), # II
    ((0,0,0,1,1),(0,0,0,0,1),(0,0,0,1,0),(1,0,1,0,0),(1,1,0,0,0), "c"), # canoe sinking
    ((1,1,0,0),(1,0,0,1),(0,0,1,1), "a"), # aircraft carrier
    ((0,1,1,0,0),(1,0,0,1,0),(0,1,0,0,1),(0,0,1,1,0), "m"), # mango
    ((0,1,1,0),(1,0,0,1),(1,0,0,1),(0,1,1,0), "p"), # pond
    ((0,0,0,1,1),(0,0,1,0,1),(0,0,1,0,0),(1,0,1,0,0),(1,1,0,0,0), "i"), # integral
    ((1,1,0,1),(1,0,1,1), "S"), # snake
    ((1,1,0,0),(1,0,1,0),(0,0,1,0),(0,0,1,1), "f"), # fish hook
    ):
    X, Y = len(shape[0]), len(shape)-1
    max_shape_x, max_shape_y = max(X, max_shape_x), max(Y, max_shape_y)
    shapes.add(((X, Y), tuple(y*w+x for y in range(Y) for x in range(X) if shape[y][x]), shape[:-1], shape[-1]))
    shapes.add(((X, Y), tuple(y*w+x for y in range(Y) for x in range(X-1,-1,-1) if shape[y][x]), shape[:-1], shape[-1]))
    shapes.add(((X, Y), tuple(y*w+x for y in range(Y-1,-1,-1) for x in range(X) if shape[y][x]), shape[:-1], shape[-1]))
    shapes.add(((X, Y), tuple(y*w+x for y in range(Y-1,-1,-1) for x in range(X-1,-1,-1) if shape[y][x]), shape[:-1], shape[-1]))

def torus(i, *indices):
    if len(indices) == 1:
        return (i + indices[0]) % size
    return [(i + n) % size for n in indices]

def iter_neighbours(i):
    return torus(i, *neighbour_offsets)

def conway(src, dest):
    for i in range(size):
        alive = count_alive(src, i)
        dest[i] = (alive == 2 or alive == 3) if src[i] else (alive == 3)

def calc_score(i, set):
    return 255-src[i] if not set else src[i]

def count_alive(board, i, *also):
    alive = 0
    for j in iter_neighbours(i):
        if board[j] or (j in also):
            alive += 1
    return alive

def count_dead(board, i, *also):
    dead = 0
    for j in iter_neighbours(i):
        if (not board[j]) and (j not in also):
            dead += 1
    return dead

def iter_alive(board, i, *also):
    for j in iter_neighbours(i):
        if board[j] or (j in also):
            yield j

def iter_dead(board, i, *also):
    for j in iter_neighbours(i):
        if (not board[j]) and (j not in also):
            yield j

def check(board):
    for i in range(size):
        alive = count_alive(board, i)
        if board[i]:
            assert alive == 2 or alive == 3, "alive %d has %d neighbours %s" % (i, alive, list(iter_alive(board, i)))
        else:
            assert alive != 3, "dead %d has 3 neighbours %s" % (i, list(iter_alive(board, i)))

dest = bytearray(size)

if False:
    # turn into contrast
    for i in range(size):
        mx = max(src[i], max(src[j] for j in iter_neighbours(i)))
        mn = min(src[i], min(src[j] for j in iter_neighbours(i)))
        dest[i] = int((0.5 * src[i]) + (128 * (1 - float(src[i] - mn) / max(1, mx - mn))))
    src, dest = dest, bytearray(size)

try:
    checked, bad, score_cache = set(), set(), {}
    next = sorted((calc_score(i, True), i) for i in range(size))
    while next:
        best, best_score = None, sys.maxint
        current, next = next, []
        for at, (score, i) in enumerate(current):
            if score > cutoff:
                break
            if best and best_score < score:
                break
            if not dest[i] and not count_alive(dest, i):
                do_nothing_score = calc_score(i, False)
                clean = True
                for y in range(-max_shape_y-1, max_shape_y+2):
                    for x in range(-max_shape_x-1, max_shape_x+2):
                        if dest[torus(i, y*w+x)]:
                            clean = False
                            break
                    if not clean:
                        break
                any_ok = False
                for (X, Y), shape, mask, label in shapes:
                    for y in range(Y):
                        for x in range(X):
                            if mask[y][x]:
                                pos, ok = torus(i, -y*w-x), True
                                if (pos, label) in bad:
                                    continue
                                if clean and (pos, label) in score_cache:
                                    score = score_cache[pos, label]
                                else:
                                    paint = torus(pos, *shape)
                                    for j in paint:
                                        for k in iter_alive(dest, j, *paint):
                                            if count_alive(dest, k, *paint) not in (2, 3):
                                                ok = False
                                                break
                                        if not ok:
                                            break
                                        for k in iter_dead(dest, j, *paint):
                                            if count_alive(dest, k, *paint) == 3:
                                                ok = False
                                                break
                                        if not ok:
                                            break
                                    if ok:
                                        score = 0
                                        any_ok = True
                                        for x in range(X):
                                            for y in range(Y):
                                                score += calc_score(torus(pos, y*w+x), mask[y][x])
                                            score /= Y*X
                                        if clean:
                                            score_cache[pos, label] = score
                                    else:
                                        bad.add((pos, label))
                                if ok and best_score > score and do_nothing_score > score:
                                    best, best_score = (pos, shape, label), score
                if any_ok:
                    next.append((score, i))
        if best:
            pos, shape, label = best
            shape = torus(pos, *shape)
            sys.stdout.write(label)
            sys.stdout.flush()
            for j in shape:
                dest[j] = True
            check(dest)
            next += current[at+1:]
        else:
            break
except KeyboardInterrupt:
    pass
print

if True:
    check(dest)
    anim = False
    while dest != src:
        if anim:
            raise Exception("animation!")
        else:
            anim = True
        sys.stdout.write("x"); sys.stdout.flush()
        conway(dest, src)
        dest, src = src, dest
        check(dest)

# canvas
out = Image.new("1", (w, h))
out.putdata([not i for i in dest])

# tk UI
Tkinter = None
try:
    import Tkinter
    from PIL import ImageTk
    root = Tkinter.Tk()
    root.bind("<Button>", lambda event: event.widget.quit())
    root.geometry("%dx%d" % (w, h))
    show = ImageTk.PhotoImage(out)
    label = Tkinter.Label(root, image=show)
    label.pack()
    root.loop()
except Exception as e:
    print "(no Tkinter)", e
    Tkinter = False

if len(sys.argv) > 3:
    out.save(sys.argv[3])

if not Tkinter:
    out.show()

कृपया ध्यान दें:

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

कोड सबसे अच्छा फिटिंग मानक अभी भी जीवन के साथ सफेद पिक्सल पर टिकटें । एक कट-ऑफ तर्क है जिससे आप तय करते हैं कि ब्लैक-व्हाइट थ्रेशोल्ड के लिए गोलाई जाती है। मैंने जीवित-श्वेत के साथ प्रयोग किया है और परिणाम बहुत समान है।


9
अपने कोड को अपनी पोस्ट में शामिल करना, और अपनी पोस्ट को भाषा के नाम के साथ शीर्षक देना सबसे अच्छा है। जैसे#Python
केल्विन के शौक में

मेरा सत्यापनकर्ता स्क्रिप्ट कहता है कि आपके पास बाएं और दाएं किनारों पर कुछ खराब पिक्सेल हैं। ऐसा लगता है कि जब पिक्सेल दाईं से बाईं ओर लपेटते हैं, तो वे एक पिक्सेल को भी नीचे की ओर ले जाते हैं।
नथानिएल

+1 हालांकि - बहुत जल्दी जवाब के लिए धन्यवाद!
नथानिएल

GoL को लागू करने के लिए मुझे शुरू करने के लिए @Nathaniel thx। एकदम सीधी गलतफहमी। आउटपुट बहुत समान होगा और मुझे उन सभी को फिर से प्रस्तुत करने के लिए प्रतीक्षा करने की कोई लालसा नहीं है :( इस चुनौती का मेरे स्वयं के एनीमेशन के समान दोष है - लोग वास्तव में कल्पना करते हैं कि वे परिणाम देखना चाहते हैं, लेकिन यह अभी तक दूर है वास्तव में प्रवेश करने के लिए बहुत अधिक निवेश। इस विशेष समस्या की जटिलता अंतरिक्ष azspcs.net प्रतियोगिता को वश में दिखती है। इसकी एक शर्म कि GoL मोनोक्रोम है और, ईमानदार होने के लिए, अभी भी छवियों के लिए एक गरीब फिट। स्मूथलाइफ मजेदार लगता है; लेकिन ड्राइंग के लिए नहीं। साथ तस्वीरें।
विल

@ बग को ठीक करने के बारे में चिंता करने की कोई आवश्यकता नहीं है, मैं इसे उल्लेख करने के लिए मजबूर महसूस करता हूं क्योंकि मैं इसे जांचने के लिए एक कार्यक्रम लिखने की परेशानी में चला गया हूं!
नथानिएल

8

जावा

एक किनारे का पता लगाने-आधारित दृष्टिकोण। चल रहे निर्देशिका में इस पाठ फ़ाइल की आवश्यकता है ।

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

import javax.imageio.ImageIO;


public class StillLifer{
    private static List<boolean[][]>patterns=new ArrayList<>();
    private static boolean[][] copy(boolean[][]b,int x,int y){
        boolean[][]r=new boolean[6][6];
        for(int i=0;i<6;i++){
            for(int j=0;j<6;j++){
                r[i][j]=b[y+i][x+j];
            }
        }
        return r;
    }
    private static void paste(boolean[][]from,boolean[][]to,int x,int y){
        for(int i=0;i<from.length;i++)for(int j=0;j<from[0].length;j++){
            to[y+i][x+j]=from[i][j];
        }
    }
    private static boolean[][]findClosest(boolean[][]b){
        boolean[][]c=null;
        int d=999999;
        for(boolean[][]k:patterns){
            int d2=editDistance(b,k);
            if(d2<d){
                c=k;
                d=d2;
            }
        }
        return c;
    }
    private static boolean[][]decode(String s){
        char[]a=s.toCharArray();
        boolean[][]r=new boolean[6][6];
        int k=0;
        for(int i=0;i<6;i++){
            for(int j=0;j<6;j++){
                r[i][j]=a[k++]=='1';
            }
        }
        return r;
    }
    private static class EdgeDetectEntry{
        int l;
        int x;
        int y;
        public EdgeDetectEntry(int m,int x,int y){
            this.l=m;
            this.x=x;
            this.y=y;
        }
    }
    private static Random rand;
    private static int w,h;
    private static BufferedImage img;
    private static boolean[][]grid;
    private static File file;
    private static int editDistance(boolean[][]from,boolean[][]to){
        int w=from.length;
        int h=from[0].length;
        int k=0;
        for(int x=0;x<w;x++){
            for(int y=0;y<h;y++){
                k+=from[y][x]^to[y][x]?1:0;
            }
        }
        return k;
    }
    private static int colorDistance(Color from,Color to){
        return from.getRed()-to.getRed();
    }
    private static int edgeDetectWeight(int x,int y){
        int k=0;
        Color c=new Color(img.getRGB(x, y));
        for(int x2=Math.max(0,x-1);x2<Math.min(w,x+2);x2++){
            for(int y2=Math.max(0,y-1);y2<Math.min(h,y+2);y2++){
                int l=colorDistance(c,new Color(img.getRGB(x2, y2)));
                k+=l*l;
            }
        }
        return k;
    }
    private static void save() throws Exception{
        int bk=Color.BLACK.getRGB();
        int wt=Color.WHITE.getRGB();
        for(int x=0;x<w;x++){
            for(int y=0;y<h;y++){
                img.setRGB(x,y,grid[y][x]?wt:bk);
            }
        }
        String k=file.getName().split("\\.")[0];
        ImageIO.write(img,"png",new File(k="out_"+k+".png"));
    }
    private static String rle(boolean[][]grid){
        StringBuilder st=new StringBuilder();
        for(boolean[]row:grid){
            for(int j=0;j<row.length;j++){
                int k=1;
                for(;j<row.length-1&&row[j]==row[j+1];j++)k++;
                if(k!=1)st.append(Integer.toString(k,36));
                st.append(row[j]?'@':'-');
            }
        }
        return st.toString();
    }
    private static int getVal(boolean[][]grid,int x,int y){
        if(x<0)x+=w;
        if(y<0)y+=h;
        if(x==w)x=0;
        if(y==h)y=0;
        return grid[y][x]?1:0;
    }
    private static boolean newState(boolean[][]grid,int x,int y,String rule){
        String[]r=rule.split("/");
        int k=0;
        for(int a=-1;a<=1;a++)for(int b=-1;a<=1;a++)k+=(a|b)==0?0:getVal(grid,x+a,y+b);
        String s=Integer.toString(k);
        return grid[y][x]?r[1].contains(s):r[0].contains(s);
    }
    private static boolean[][] next(boolean[][]grid,String rule){
        boolean[][]r=new boolean[h][w];
        for(int x=0;x<w;x++){
            for(int y=0;y<h;y++){
                r[y][x]=newState(grid,x,y,rule);
            }
        }
        return r;
    }
    private static void loadPatterns() throws Exception{
        Scanner reader=new Scanner(new File("lib.txt"));
        while(reader.hasNext()){
            String line=reader.nextLine();
            if(line.startsWith("--"))continue;
            patterns.add(decode(line));
        }
        reader.close();
    }
    public static void main(String[]a) throws Exception{
        loadPatterns();
        Scanner in=new Scanner(System.in);
        img=ImageIO.read(file=new File(in.nextLine()));
        in.close();
        w=img.getWidth();
        h=img.getHeight();
        grid=new boolean[h][w];
        final int npix=w*h;
        rand=new Random(npix*(long)img.hashCode());
        List<EdgeDetectEntry> list=new ArrayList<>();
        for(int x=0;x<w;x++){
            for(int y=0;y<h;y++){
                list.add(new EdgeDetectEntry(edgeDetectWeight(x,y),x,y));
            }
        }
        list.sort((one,two)->{int k=two.l-one.l;if(k>0)return 1;if(k<0)return -1;return 0;});
        for(int i=Math.max(Math.min(3,npix),npix/5);i>0;i--){
            EdgeDetectEntry e=list.get(i);
            grid[e.y][e.x]=rand.nextDouble()<0.9;
        }
        grid=next(grid,"/2345678");
        boolean[][]d=new boolean[h][w];
        for(int i=0;i<w/6;i++){
            for(int j=0;j<h/6;j++){
                paste(findClosest(copy(grid,i*6,j*6)),d,i*6,j*6);
            }
        }
        grid=d;
        assert(rle(next(grid,"3/23")).equals(rle(grid)));
        save();
    }
}

कुछ परिणाम:

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें


5

सी ++

8x8 आउटपुट ग्रिड (एक "रंग बनावट") का चयन करने के लिए प्रत्येक 8x8 ग्रिड के औसत का उपयोग करते हुए सीधा पिक्सेलशन दृष्टिकोण। प्रत्येक 8x8 आउटपुट ग्रिड में शीर्ष और दाईं ओर 2 सेल विभाजक हैं। ग्रिड 4 सेल से डिजाइन किए गए थे, अभी भी 18 सेल के लिए शेष 6x6 पिक्सल के भीतर अभी भी लिफ्ट करते हैं।

कार्यक्रम द्विआधारी पीजीएम से बाइनरी पीबीएम तक एक फिल्टर के रूप में कार्य करता है। डिफ़ॉल्ट रूप से, छवियां "अंधेरे" हैं; काली मृत्यु है और सफेद जीवन है; -iइसका विरोध करता है। -g [value]एक गामा जोड़ता है, जिसका उपयोग रंग बनावट का चयन करने से पहले औसत वजन करने के लिए किया जाता है।

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>

// colors 4 through 18 have 4 through 18 live cells
// colors 1-3 are repetitions of 0 or 4 used
// as artificially weighted bins
unsigned char gol_colors[]={
      0,  0,  0,  0,  0,  0,  0,  0 // Color  0
   ,  0,  0,  0,  0,  0,  0,  0,  0 // Color  1
   ,  0,  0,  0, 16, 40, 16,  0,  0 // Color  2
   ,  0,  0,  0, 16, 40, 16,  0,  0 // Color  3
   ,  0,  0,  0, 16, 40, 16,  0,  0 // Color  4
   ,  0,  0,  0, 24, 40, 16,  0,  0 // Color  5
   ,  0,  0,  0, 16, 40, 80, 32,  0 // Color  6
   ,  0,  0,  0, 48, 72, 40, 16,  0 // Color  7
   ,  0,  0,  8, 20,  8, 64,160, 64 // Color  8
   ,  0,  0,  8, 20,  8, 64,160, 96 // Color  9
   ,  0,  0, 12, 20,  8, 64,160, 96 // Color 10
   ,  0,  0, 12, 20,  8,192,160, 96 // Color 11
   ,  0,  0,204,204,  0,  0, 48, 48 // Color 12
   ,  0,  0,204,204,  0,192,160, 64 // Color 13
   ,  0,  0,  0,108,168,168,108,  0 // Color 14
   ,  0,  0, 96,144,104, 40,172,192 // Color 15
   ,  0,  0,204,204,  0,  0,204,204 // Color 16
   ,  0,  0,216,216,  0,216,212,  8 // Color 17
   ,  0,  0,204,164, 40, 80,148,204 // Color 18
};

enum { gol_bins = sizeof(gol_colors)/(sizeof(*gol_colors))/8 };

bool inverted=false;

bool applygamma=false;
double gammasetting=0.0;

unsigned int corrected(unsigned int i, unsigned int r) {
   return static_cast<unsigned int>(r*std::pow(i/static_cast<double>(r), gammasetting));
}

int main(int argc, char** argv) {
   std::vector<unsigned short> pgm_data;
   unsigned pgm_width;
   unsigned pgm_height;
   unsigned pgm_vpp;

   std::vector<unsigned char> pbm_data;
   unsigned pbm_width;
   unsigned pbm_height;

   unsigned int doublings=0;

   std::vector<std::string> args(argv+1, argv+argc);
   for (unsigned int i=0, e=args.size(); i<e; ++i) {
      if (args[i]=="-i") { inverted=true; continue; }
      if (args[i]=="-g") {
         if (i+1==e) continue;
         std::stringstream ss;
         ss << args[++i];
         if (ss >> gammasetting) applygamma = true;
         continue;
      }
   }

   std::string line;
   std::getline(std::cin, line);
   if (line!="P5") return 1;
   enum { nothing, have_w, have_h, have_bpp } readstate = nothing;
   while (std::cin) {
      std::getline(std::cin, line);
      if (line.empty()) continue;
      if (line[0]=='#') continue;
      std::stringstream ss; ss << line;
      for(;;) {
         switch (readstate) {
         case nothing: if (ss >> pgm_width) readstate = have_w; break;
         case have_w:  if (ss >> pgm_height) readstate = have_h; break;
         case have_h:  if (ss >> pgm_vpp) readstate = have_bpp; break;
         }
         if (readstate==have_bpp) break;
         if (ss) continue;
         break;
      }
      if (readstate==have_bpp) break;
   }
   if (readstate!=have_bpp) return 1;
   // Fill pgm data
   pgm_data.resize(pgm_width*pgm_height);
   for (unsigned i=0, e=pgm_width*pgm_height; i<e; ++i) {
      int v = std::cin.get();
      if (v==std::char_traits<char>::eof()) return 1;
      pgm_data[i] = static_cast<unsigned int>(std::char_traits<char>::to_char_type(v))&0xFFU;
   }
   pbm_width  = pgm_width/8*8;
   pbm_height = pgm_height/8*8;
   pbm_data.resize(pbm_width*pbm_height/8);
   for (unsigned x=0, xe=pbm_width/8; x<xe; ++x) {
      for (unsigned y=0, ye=pbm_height/8; y<ye; ++y) {
         // Calculate the average of this 8x8 area
         unsigned int total=0;
         for (unsigned int xd=0; xd<8; ++xd) {
            for (unsigned int yd=0; yd<8; ++yd) {
               unsigned int c = x+xd+(y+yd)*pgm_width;
               unsigned int pv = pgm_data[x*8+xd+(y*8+yd)*pgm_width];
               // Apply gamma prior to averaging
               if (applygamma) pv=corrected(pv, pgm_vpp);
               total += pv;
            }
         }
         total /= 64;
         // Invert average if inverting colors (white on black)
         if (inverted) total=pgm_vpp-total;
         total *= gol_bins;
         total /= (pgm_vpp+1);
         // Fill 8x8 areas with gol color texture
         for (unsigned int yd=0; yd<8; ++yd) {
            pbm_data[x+(y*8+yd)*pbm_width/8] = gol_colors[total*8+yd];
         }
      }
   }
   // Now, write a pbm
   std::cout
      << "P4\n"
      << "# generated by pgm2gol\n"
      << pbm_width << " " << pbm_height << "\n";
   for (unsigned i=0, e=pbm_data.size(); i<e; ++i) {
      unsigned char data=pbm_data[i];
      if (!inverted) { data=pbm_data[i]^0xFF; }
      std::cout.put(data);
   }
}

चयनित परिणाम (ध्यान दें: सभी pbm को अपलोड करने के लिए तीसरे पक्ष के कार्यक्रम का उपयोग करके पीएनजी में परिवर्तित किया गया था):

एस्चर, गामा 2.2
Escher

मोना लिसा, गामा २.२
मोना

महासागर, गामा 2.2 उलटा
सागर

पिल्ला, गामा २.२
कुत्ते का बच्चा

छवियों के विश्वासघात, गामा 2.2 उलटा
द्रऋह

तुलना के लिए, मोना लिसा गामा 2.2, उल्टा
मोना

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.