2013-02-14 16 views
9

Sto cercando di capire uno script che genererà una trama (che può quindi essere moltiplicata per un'immagine in scala di grigi per "applicarla"). Finora il mio metodo implica la semina del RNG, quindi generare casualmente una matrice 8x8 di numeri interi nell'intervallo [0,3], quindi ridimensionare quella matrice in un'immagine 256x256 utilizzando un certo livello di interpolazione.Creazione di una trama in modo procedurale

Ecco un esempio di output (valore seed 24):

Sample http://adamhaskell.net/misc/texture/sample.png

Sulla sinistra è la matrice scalata con primi vicini interpolazione. A destra c'è il mio tentativo di interpolazione bilineare. Per la maggior parte sembra ok, ma poi ottieni strutture simili a quelle del centro-sinistra dove ci sono due quadrati arancioni adiacenti diagonalmente con due quadrati rossi diagonalmente contigui, e il risultato non è un'interpolazione per quell'area. Inoltre, viene trattato più come una heatmap (come mostrato dall'abbondanza di arancione nell'angolo in alto a sinistra) e questo sta causando più problemi.

Ecco il codice che ho per il mio "interpolazione bilineare":

<?php 
$matrix = Array(); 
srand(24); 
$dim = 256; 
$scale = 32; 
for($y=0;$y<=$dim/$scale;$y++) for($x=0;$x<=$dim/$scale;$x++) $matrix[$y][$x] = rand(0,3); 
$img = imagecreate($dim,$dim); 
imagecolorallocate($img,255,255,255); 
$cols = Array(
    imagecolorallocate($img,128,0,0), 
    imagecolorallocate($img,128,64,32), 
    imagecolorallocate($img,128,128,0), 
    imagecolorallocate($img,64,64,64) 
); 
for($y=0;$y<$dim;$y++) { 
    for($x=0;$x<$dim;$x++) { 
     $xx = floor($x/$scale); $yy = floor($y/$scale); 
     $x2 = $x%$scale; $y2 = $y%$scale; 
     $col = $cols[round((
      $matrix[$yy][$xx]*($scale-$x2)*($scale-$y2) 
      + $matrix[$yy][$xx+1]*$x2*($scale-$y2) 
      + $matrix[$yy+1][$xx]*($scale-$x2)*$y2 
      + $matrix[$yy+1][$xx+1]*$x2*$y2 
     )/($scale*$scale))]; 
     imagesetpixel($img,$x,$y,$col); 
    } 
} 
header("Content-Type: image/png"); 
imagepng($img); 
exit; 

In realtà, questo può essere un po 'un problema XY. Quello che sto specificatamente cercando di fare è generare "modelli di pelliccia" per le creature in un gioco che sto pianificando. In particolare, voglio essere in grado di averlo in modo tale che l'allevamento mescoli elementi dei due genitori (sia che si tratti di colore o elementi del modello), così solo avere un seme casuale non lo taglierà davvero. Idealmente ho bisogno di un qualche tipo di approccio basato sul vettore, ma sono molto lontano dalla mia profondità, quindi qualsiasi aiuto sarebbe molto apprezzato.

+3

Penso che il motivo per cui sembra che una mappa di calore sia perché in realtà non stai facendo una vera interpolazione lineare dei colori dei vicini più vicini, in realtà stai scegliendo il colore esistente più vicino per questa interpolazione. Questo è anche il motivo per cui non vi è interpolazione nel vicino centro-sinistra. Perché non riesci a mescolare i colori? E se sono trame, non puoi fondere le trame con qualche componente alfa? – zakinster

+0

Non proprio una risposta, ma aneddoto forse utile correlato. Penso che Alan Turing abbia lavorato su alcuni algoritmi matematici per produrre modelli di pelle animale di questo tipo. Forse valgono la pena dare un'occhiata? – AntonChanning

risposta

1

Un paio di cose mi vengono in mente:

  1. Lei non è interpolando i valori di colore. Per espandere il commento di zakinster, stai interpolando gli indici di colore e quindi arrotondando al più vicino. Un effetto di questo è che si finisce con una striscia di giallo (indice 2) tra le aree arancione (indice 1) e grigio (indice 3). Se invece interponassi i valori dei colori, ti ritroverai con, forse, un'arancia grigiastra?

  2. Hai più giallo e arancione e meno rosso e grigio nell'immagine finale. Ciò è dovuto all'utilizzo di round() per eseguire lo snap ad un indice di colore. Il tuo calcolo (prima di round()) può produrre float equamente distribuiti tra 0 e 3, ma l'arrotondamento non lo conserva.

Quindi, ecco alcuni suggerimenti:

  1. Se non si è limitato a 4 colori, un uso più. Interpolare i valori del colore (cioè (128,0,0) mescolati con (64,64,64) produce (91,32,32)) piuttosto che gli indici.

  2. Se sei limitato a quei 4 colori, prova un tipo di dithering. Un approccio semplice, con modifiche minime al codice, sarebbe aggiungere un po 'di casualità all'indice del colore scelto. Quindi, invece di round (...), fai qualcosa di simile: ad esempio il tuo calcolo produce il valore 1.7. Quindi, arrotondare fino a 2 con una probabilità del 70% e fino a 1 l'altro 30%. Questo mescolerà i colori, ma potrebbe produrre un'immagine molto rumorosa. Se sei pronto a cambiare il tuo codice in modo più sostanziale, controlla Floyd-Steinberg dithering.

1

So che è vecchia questione, e la risposta da @ Markku-k è corretto, in ogni caso ho problema simile, e qui è il mio codice modificato per la questione

diversi avvisi:

  1. produce 2 immagini in una, per mostrare "matrice originale" e risultato
  2. utilizza matrice 8x8 per produrre risultati, ma la matrice effettiva è 10x10 per coprire i bordi
  3. utilizza il colore al colore di base algoritmo indice sul semplice delta, funziona bene per me

ecco il codice:

<?php 
$matrix = array(); 
$dim = 256; 
$scale = 32; 

for($y=0; $y<=9; $y++) 
{ 
    $matrix[$y] = array(); 
    for($x=0; $x<=9; $x++) 
    { 
     $same = false; 
     do 
     { 
      $matrix[$y][$x] = mt_rand(0, 3); // do not use rand function, mt_rand provide better results 
      if (($x>0) && ($y>0)) // check for checkers siatuion, where no colors are preferable and produce 90 degree angles 
      { 
       $c1 = $matrix[$y-1][$x-1]; 
       $c2 = $matrix[$y][$x]; 
       $c3 = $matrix[$y-1][$x]; 
       $c4 = $matrix[$y][$x-1]; 
       $same = (($c1==$c2) && ($c3==$c4)); 
      } 
     } while ($same); 
    } 
} 

$img = imagecreate($dim*2 + 32*4, $dim + 32*2); 
$colorsRGB = array(0x800000, 0x804020, 0x808000, 0x404040); 
$cols = Array(
     imagecolorallocate($img,128,0,0), // red 
     imagecolorallocate($img,128,64,32), // orange 
     imagecolorallocate($img,128,128,0), // yellow 
     imagecolorallocate($img,64,64,64), // gray 
     imagecolorallocate($img,0,0,0), // black, just to fill background 
); 

imagefilledrectangle($img, 0, 0, $dim*2 + 32*4 - 1, $dim + 32*2 - 1, $cols[4]); 

function mulclr($color, $multiplicator) 
{ 
    return array(($color>>16) * $multiplicator, (($color>>8)&0xff) * $multiplicator, ($color&0xff) * $multiplicator); 
} 

function addclr($colorArray1, $colorArray2) 
{ 
    return array($colorArray1[0]+$colorArray2[0], $colorArray1[1]+$colorArray2[1], $colorArray1[2]+$colorArray2[2]); 
} 

function divclr($colorArray, $div) 
{ 
    return array($colorArray[0]/$div, $colorArray[1]/$div, $colorArray[2]/$div); 
} 

function findclridx($colorArray, $usedColors) 
{ 
    global $colorsRGB; 

    $minidx = $usedColors[0]; 
    $mindelta = 255*3; 
    foreach ($colorsRGB as $idx => $rgb) 
    { 
     if (in_array($idx, $usedColors)) 
     { 
      $delta = abs($colorArray[0] - ($rgb>>16)) + abs($colorArray[1] - (($rgb>>8)&0xff)) + abs($colorArray[2] - ($rgb&0xff)); 
      if ($delta < $mindelta) 
      { 
       $minidx = $idx; 
       $mindelta = $delta; 
      } 
     } 
    } 
    return $minidx; 
} 

for($y=0; $y<($dim+64); $y++) 
{ 
    for($x=0; $x<($dim+64); $x++) 
    { 
     $xx = $x>>5; 
     $yy = $y>>5; 
     $x2 = ($x - ($xx<<5)); 
     $y2 = ($y - ($yy<<5)); 

     imagesetpixel($img, $x, $y, $cols[$matrix[$yy][$xx]]); 

     if (($xx>0) && ($yy>0) && ($xx<=8) && ($yy<=8)) 
     { 
      $color1 = $colorsRGB[$matrix[$yy][$xx]]; 
      $color2 = $colorsRGB[$matrix[$yy][ ($xx+1) ]]; 
      $color3 = $colorsRGB[$matrix[ ($yy+1) ][$xx]]; 
      $color4 = $colorsRGB[$matrix[ ($yy+1) ][ ($xx+1) ]]; 

      $usedColors = array_unique(array($matrix[$yy][$xx], $matrix[$yy][ ($xx+1) ], $matrix[ ($yy+1) ][$xx], $matrix[ ($yy+1) ][ ($xx+1) ])); 

      $a1 = mulclr($color1, ($scale-$x2)*($scale-$y2)); 
      $a1 = addclr($a1, mulclr($color2, $x2*($scale-$y2))); 
      $a1 = addclr($a1, mulclr($color3, ($scale-$x2)*$y2)); 
      $a1 = addclr($a1, mulclr($color4, $x2*$y2)); 
      $a1 = divclr($a1, $scale*$scale); 
      $clrIdx = findclridx($a1, $usedColors); 

      $col = $cols[$clrIdx]; 
      imagesetpixel($img, $dim+$x+32*2, $y, $col); 
     } 
    } 
} 
header("Content-Type: image/png"); 
imagepng($img); 
exit; 

qui è il risultato del campione:

enter image description here

+0

Vale la pena notare che un modo possibile per evitare di avere una matrice 10x10 in questo caso potrebbe essere di assumere una matrice 8x8 con se stessa, dall'alto in basso e da sinistra a destra. Tale che in alto a sinistra nell'angolo in alto a sinistra è l'angolo in basso a destra. – AntonChanning