Malen Sie ein Stillleben (oder ein bewegtes) - zeichnen Sie ein Bild im Spiel des Lebens


36

Sie erhalten als Eingabe ein Graustufenbild. Ihre Aufgabe ist es, in Conways Game of Life ein statisches oder sich wiederholendes Muster zu finden , das dem eingegebenen Bild so nahe wie möglich kommt.

Ihre Ausgabe kann entweder ein Standbild oder eine sich wiederholende Animation in einem Format sein, das in GIF konvertiert werden kann. Die Abmessungen des Ausgabebilds sollten mit denen der Eingabe übereinstimmen und dürfen nur schwarze und weiße Pixel enthalten.

Wenn es sich bei der Ausgabe um eine Animation handelt, muss jeder Frame gemäß den Game of Life-Regeln mit einer Zelle pro Pixel aus dem vorherigen generiert werden. Die Animation muss eine Schleife bilden, wobei das erste Bild nach denselben Regeln aus dem letzten Bild generiert wird.

Wenn es sich bei der Ausgabe um ein Standbild handelt, muss bei Anwendung der Spielregeln dasselbe Bild erstellt werden. Dies bedeutet, dass keine "lebende" Zelle mehr als drei oder weniger als zwei "lebende" Nachbarn haben darf und keine "tote" Zelle genau drei "lebende" Nachbarn haben darf. (Beachten Sie, dass dies im Prinzip mit einer Animation wie oben beschrieben identisch ist, jedoch nur mit einem Frame.)

Zusätzliche Regeln und Erläuterungen:

  • Sie (oder Ihr Programm) können wählen, ob "lebende" Zellen als weiß und "tote" als schwarz dargestellt werden oder umgekehrt. Das heißt, Sie können dies entweder fest codieren oder Ihr Programm kann es basierend auf dem eingegebenen Bild auswählen. (Es muss jedoch für jedes Bild der Animation gleich sein.)

  • Die Randbedingungen sollten periodisch sein, dh die Zellen in der rechten Spalte haben Nachbarn in der linken Spalte usw.

  • Bei Animationen liegt die Framerate bei Ihnen (oder Ihrem Programm). Ich stelle mir vor, dass schnelle Bildraten gut für die Approximation grauer Pixel geeignet sind.

  • Bitte posten Sie mindestens zwei Ergebnisse in Ihrer Antwort. Wenn Sie die Ergebnisse aller unten aufgeführten Eingabebilder veröffentlichen können, ist dies vorzuziehen.

  • Es ist akzeptabel, die Testbilder zu verkleinern, wenn dies erforderlich ist, um GIFs mit ausreichend kleinen Dateigrößen zu erzielen. Wenn Sie auch auf größere Dateien verlinken möchten, ist das in Ordnung. Wenn Sie angeben möchten, können Sie einige Quelldateien mit höherer Auflösung suchen.

  • Bitte versuchen Sie, zu viele steuerbare Parameter in Ihrem Code zu vermeiden. Es ist am besten, wenn die einzige Eingabe Ihres Programms das Bild ist. Die Ausnahme ist, wenn Sie einen Parameter zum Steuern der Anzahl der Animationsrahmen haben möchten, da dies die Dateigröße beeinflusst.

  • Sie können externe Programme verwenden, um das Format der Eingabe- und Ausgabedateien zu ändern und / oder Ausgabefelder in eine Animation zu kompilieren, wenn Sie dies möchten. (Dies ist keine Herausforderung beim Umgang mit Dateiformaten.)

  • Dies ist ein , daher gewinnt die Antwort mit den meisten Stimmen.

Hier finden Sie eine Auswahl von Testbildern, die größtenteils aus anderen Fragen auf dieser Website stammen. (Es ist möglich, dass ich später zusätzliche "Bonus" -Eingabebilder hinzufüge.)

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Um den Anfang zu machen, hier ist ein sehr dummer Referenzversuch in Python 2, der die Tatsache ausnutzt, dass ein Block mit vier Quadraten eine stabile Struktur im Spiel des Lebens ist. Es skaliert das Eingabebild nur um den Faktor 4 neu und zeichnet dann einen Block, wenn das entsprechende Pixel dunkler als 0,5 ist.

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)

Hier sind einige Ausgaben aus dem Beispielcode. Ich bin sicher, dass viel bessere Ergebnisse möglich sind.

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben


2
Hier sind einige Stillebensegmente mit hoher Dichte: en.wikipedia.org/wiki/… . Sie können nicht mehr als die Hälfte der Dichte im Limit erreichen.
Xnor

Werden in Ihrem Beispiel keine neuen Zellen an der Kreuzung von drei Quadraten geboren?
Xnor

@xnor oh ja, du hast recht. In diesem Fall sollte das Beispiel vorerst entfernt werden. (Ich sollte auch anfangen, einen Bestätigungscode zu schreiben!)
Nathaniel

3
Nicht sicher, wie das helfen würde, da keiner der Garten Eden-Muster noch lebt. (was sie zu ihren eigenen Vorgängern machen würde) Ähnliche Gründe, warum sie auch keine Oszillatoren sind.
Tally

1
Einige weitere Inspirationen für Teilnehmer: tlrobinson.net/blog/2009/02/game-of-life-generator
Abulafia

Antworten:


13

Python

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

Bitte schielen:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Der Code stempelt die weißesten Pixel mit dem am besten passenden Standardstillleben . Es gibt ein Cut-Off-Argument, mit dem Sie entscheiden können, ob die Rundung auf Schwarz-Weiß-Schwelle erfolgt. Ich habe mit Living-Is-White experimentiert und das Ergebnis ist fast dasselbe.


9
Am besten fügen Sie Ihren Code in Ihren Beitrag ein und betiteln Ihren Beitrag mit dem Namen der Sprache. zB#Python
Calvins Hobbys

Mein Validierungsskript besagt, dass Sie am linken und rechten Rand einige fehlerhafte Pixel haben. Es sieht so aus, als ob Pixel, die von rechts nach links gewickelt werden, sich auch um ein Pixel nach unten bewegen.
Nathaniel

+1 obwohl - danke für die sehr schnelle Antwort!
Nathaniel

@ Nathaniel thx für die Einführung in die Implementierung von GoL. Ein ziemlich einfaches Missverständnis. Die Ausgabe wäre ungefähr gleich und ich möchte nicht warten, bis sie wieder gerendert werden :( Diese Herausforderung hat den gleichen Fehler wie meine eigene Animation - die Leute stellen sich wirklich vor, dass sie die Ergebnisse sehen wollen, aber es dauert lange, bis sie wieder gerendert werden Die Komplexität dieses Problems lässt die azspcs.net- Wettbewerbe zahm aussehen. Es ist eine Schande, dass GoL einfarbig ist und, um ehrlich zu sein, nicht für Standbilder geeignet. SmoothLife scheint Spaß zu machen, aber nicht zum Zeichnen Bilder mit.
Will

@ Kein Grund zur Sorge, den Fehler zu beheben, ich fühlte mich nur gezwungen, ihn zu erwähnen, da ich mir die Mühe gemacht hatte, ein Programm zu schreiben, um ihn zu überprüfen!
Nathaniel

8

Java

Ein auf Kantenerkennung basierender Ansatz. Benötigt diese Textdatei im laufenden Verzeichnis.

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

Einige Ergebnisse:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben


5

C ++

Einfacher Pixelansatz unter Verwendung des Durchschnitts jedes 8x8-Gitters zur Auswahl eines 8x8-Ausgabegitters (eine "Farbtextur"). Jedes 8x8-Ausgangsraster hat oben und rechts einen 2-Zellen-Separator. In den verbleibenden 6 x 6 Pixeln wurden Raster von 4-Zellen-Stillleben bis zu 18-Zellen-Stillleben entworfen.

Das Programm fungiert als Filter von binärem PGM zu binärem PBM. Standardmäßig sind Bilder "dunkel". Schwarz ist der Tod und Weiß ist das Leben; -iinvertiert dies. -g [value]Fügt ein Gamma hinzu, mit dem Durchschnittswerte vor der Auswahl der Farbtexturen vorgewichtet werden.

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

Ausgewählte Ergebnisse (Hinweis: Alle PBMs wurden mithilfe eines Drittanbieterprogramms zum Hochladen in PNGs konvertiert.):

Escher, Gamma 2.2
Escher

Mona Lisa, Gamma 2.2
Mona

Ozean, Gamma 2,2 invertiert
Ozean

Welpe, Gamma 2.2
Hündchen

Verrat an Bildern, Gamma 2.2 invertiert
Verrat

Mona Lisa Gamma 2.2, invertiert, zum Vergleich
Mona

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.