ランダム関数の「機能」の1つは、疑似ランダムであるということです。つまり、同じシードが与えられると、常に同じシーケンスが出力されます。
したがって、「画像」ごとに保存できます。
sourcefile - the name of the source image
seed - integer, maybe the start time of this sequence
position - number of pixels that need to be shown, or maybe % completion
したがって$sourcefile
、シード$seed
と$position
ピクセルのパーセンテージが表示された状態で画像を出力するとします。アルファを使用する必要はありません。
// Load image
$src = imageCreateFromPNG($sourcefile); // Assume image is PNG
// Create work image
$new = imageCreateTrueColor(ImageSX($src), ImageSY($src)); // new image of same size
mt_srand($seed); // Seed the Mersenne Twister generator
// Number of pixels to set: $position = 0: NONE, $position = 100: ALL
$pixels = round($position*imageSX($src)*imageSY($src)/100);
// Now we have a problem: if we do $pixels attempts, mt_rand might sometimes
// return the same pixel again. So we end up setting less than $pixels pixels.
// So we do this the expensive way, saving an array of yet-to-be-used pixels.
$max = ImageSX($src)*ImageSY($src);
$pixelid = array();
for ($i = 0; $i < $max; $i++)
$pixelid[] = $i;
$W = ImageSX($src);
while($pixels--)
{
// Extract one pixel
$chosen = $pixelid[$idx = mt_rand(0, $pixels)];
array_splice ($pixelid, $idx, 1); // Remove extracted pixel from array
$x = $chosen % $W;
$y = ($chosen - $x)/ $W;
$rgb = imagecolorat($src, $x, $y);
$pix = imagecolorsforindex($src, $rgb);
$rgb = imageColorAllocate($new, $pix['red'], $pix['green'], $pix['blue']);
imageSetPixel($new, $x, $y, $rgb);
}
ImageDestroy($src);
// $new has now exactly $pixels set to the same pixels of $src,
// the rest are undefined (you can fill $new with white beforehand...)
Header("Content-Type: image/png");
ImagePNG($new);
バリエーション
アレイをスプライスする代わりに、「使用済み」ピクセルを消し去ることができます。これにより、均一な分布が得られない場合でも、次のようになります。
$cnt = count($pixelid);
while($pixels--)
{
// Extract one pixel
$idx = mt_rand(0, $cnt);
// If the extracted pixel is null, find next pixel that is unextracted
// and if there are none, restart from the beginning of the array.
while (-1 == ($chosen = $pixelid[$idx]))
if ($cnt == ++$idx)
$idx = 0;
$chosen = $pixelid[$idx];
$pixelid[$idx] = -1;
または、...再試行することもできます。ただし、画像がほぼ完成している場合、これはコストがかかる可能性があります。
$cnt = count($pixelid);
while($pixels--)
{
// Extract one pixel
for ($idx = mt_rand(0, $cnt); $pixelid[$idx] != -1; $idx = mt_rand(0, $cnt))
;
$chosen = $pixelid[$idx];
$pixelid[$idx] = -1;
画像が常に異なる方法で再構築されることを気にしない場合は、のarray_shuffle()
代わりにを使用できます。mt_rand()
各反復で、からi番目のピクセルを抽出し$pixelid
ます。
最後のオプションは、マニュアルページで詳しく説明されているようにarray_shuffle()
使用を再実装するmt_rand
ことです(leethost dot comのtimによる例を参照)。
function array_new_shuffle(&$items, $seed)
{
mt_srand($seed);
for ($i = count($items) - 1; $i > 0; $i--)
{
$j = @mt_rand(0, $i);
list($items[$i], $items[$j]) = array($items[$j], $items[$i]);
}
}
したがって、を使用しないように呼び出しarray_new_shuffle()
て$pixelid
から$seed
、シャッフルされた配列から要素を順番に抽出します。
for ($idx = 0; $idx < $pixels; $idx++)
{
$chosen = $pixelid[$idx];
...
大きな画像
大きな画像の場合、配列の処理にはコストがかかりすぎ、メモリが不足します。したがって、同じピクセルを繰り返しヒットすることを避けるためにmt_rand()
(画像が99%完成すると非常に問題になる可能性があるため、まだ実行可能な1%ピクセルの1つをランダムにヒットする確率はもちろん1%です)、このハックはインデックスとしての別の画像。
これにより、「配列」が2 ^ 24エントリ、つまり1辺が2 ^ 12、つまり4096ピクセルの画像に制限されます。
メモリの節約は非常に大きいです。各画像のピクセルのコストは、約176バイトではなく16バイトになりました(これは私のLinux 64ビットマシンでは)。これは、1024x1024ピクセルの画像に必要なRAMは約17Mのみであることを意味します。
私のシステムでは、このスクリプトは1秒あたり約180kピクセルを処理します(1024x1024の画像は7.4秒で100%処理され、そのうち約2つが画像の読み込みとセットアップに必要でした)。
$seed = 0;
$position = 2;
$sourcefile = '/home/lserni/Lena19721024-filtered.png';
mt_srand($seed); // Seed the Mersenne Twister generator
// Load image
$src = ImageCreateTrueColor(512,512);
// $src = imageCreateFromPNG($sourcefile); // Assume image is PNG
$W = ImageSX($src);
$H = ImageSY($src);
// Total number of pixels
$size = $W*$H;
if (($W > 4095) || ($H > 4095))
die("Image too big");
// Create work image
$new = imageCreateTrueColor($W, $H); // new image of same size
/*
if ($position > 50)
{
$position = 100-$position;
$tmp = $src;
$src = $new;
$new = $tmp;
}
*/
// Number of pixels to set: $position = 0: NONE, $position = 100: ALL
$pixels = round($position*$size/100.0);
// Create a temporary buffer image of the same size
$fix = imageCreateTrueColor($W, $H);
for ($i = 0; $i < $size; $i++)
{
$b = $i & 0xFF;
$g = ($i >> 8) & 0xFF;
$r = ($i >> 16) & 0xFF;
imageSetPixel($fix, $i % $W, floor($i / $W), imageColorAllocate($fix, $r, $g, $b));
}
while($pixels--)
{
// Recover one of the available pixel indexes
$idx = mt_rand(0, $size--);
// Recover index from image
$y = floor($idx / $W);
$x = $idx % $W;
$idx = imageColorAt($fix, $x, $y);
$lst = imageColorAt($fix, $size % $W, floor($size / $W));
$b = $lst & 0xff; $lst >>= 8;
$g = $lst & 0xff; $lst >>= 8;
$r = $lst & 0xff;
imageSetPixel($fix, $x, $y, imageColorAllocate($fix, $r, $g, $b));
// Whew. Now recover true x and y from new $idx
$y = floor($idx / $W);
$x = $idx % $W;
$rgb = imagecolorat($src, $x, $y);
$pix = imagecolorsforindex($src, $rgb);
$rgb = imageColorAllocate($new, $pix['red'], $pix['green'], $pix['blue']);
imageSetPixel($new, $x, $y, $rgb);
}
ImageDestroy($src);
// $new has now exactly $pixels set to the same pixels of $src,
// the rest are undefined (you can fill $new with white beforehand...)
// die("Memory: " . memory_get_peak_usage());
Header("Content-Type: image/png");
ImagePNG($new);
最適化
上記のコードにコメント付きのセクションがあります。$position
50%を超える場合、たとえば70%の場合、空のイメージを作成して、ピクセルの70%を適切なイメージから空のイメージにコピーすることは意味がありません。空のピクセルの30%を空の画像から適切な画像にコピーして、「黒く塗りつぶす」方が理にかなっています。$new
これは、を交換し$src
て調整するだけで実現できます$position
。私はこのコードを完全にテストしていません。だからコメントを残しました。しかし、ぜひお試しください。
実装
PRNGを使用する利点は、画像を保存する必要がないことですがseed
、position
通常は全部で8バイトしか保存できません。
人物Aが位置1を受け取り、最大5の位置(つまり、画像の5%が表示される)を受け取るように要求し、シードとこの値5を保存して、人物Bに使用すると、人物Bは同じ5を見ることになります。 %その人Aが得た。
元の画像を除いて、画像が保存または読み込まれることなくすべて。
$ _GETパラメータでシードと位置を渡すことができる場合は、ブラウザにさまざまな段階で画像を表示できます(たとえばimage.php?seed=12345678&position=5
、5%のピクセルが設定された画像を表示します。パーセンテージの代わりにピクセル数を指定することもできます。もちろん)。
これは、ピクセルがランダムに選択されている限り機能します。人物Aが希望する正確なピクセルを選択できる場合、このアプローチは無効であり、個々のピクセル位置を保存する必要があります。これは、いくつかの方法で実行できます。 (x、y)のカップルをバイナリ形式で保持するフラットファイルを使用するか、画像全体を保存します。後者のアプローチは理解しやすく、各ステップで1つのイメージ全体を保存する必要があるため、これがゲームであり、「再生」する場合は、膨大なディスク容量が必要になる可能性があります。最初のアプローチでは、ピクセルあたり6バイトが合理的に必要になる場合があります。つまり、同じ高さで元の画像の2倍の幅、またはピクセルあたりわずか4バイトの画像に相当します。