1

首先,我指的是上一个问题Change image per pixel and save to db

我发现 html5 画布不适合,因为很难对源图像保密。这就是为什么我要尝试使用 PHP GD 库来实现我的目标。我从来没有使用过这个库,所以我遇到了一些困难。我想我需要以下功能

  • imagecreatetruecolor 用于在浏览器中创建图像
  • imagecolorallocate 用于从源图像返回 rgb
  • imagesetpixel 用于绘制随机像素

    $x = 200; //width of image
    $y = 200; //height of image
    
    $gd = imagecreatetruecolor ($x, $y);
     $color = imagecolorallocate($gd, $r, $g, $b) //not sure how to retrieve the rgb from the source image
    
    Then I need a function for drawing random pixels with imagesetpixel.
    imagesetpixel($gd, $posx,$posy, $color); // not sure how to retrieve the x and y position of each pixel.
    

我不是 PHP 的明星,这就是为什么我的搜索停留在这些 GD 函数上的原因。希望你能给我一个启动

4

3 回答 3

3

随机函数的“特征”之一是它是伪随机的,即,在给定相同种子的情况下,它总是会输出相同的序列。

因此,您可以为每个“图像”存储:

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可见像素百分比的图像。你甚至不需要使用 alpha:

// 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手册页中的详细说明(参见 tim at leethost dot com 的示例):

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()反对$pixelidusing $seed,然后按顺序从洗牌数组中提取元素:

for ($idx = 0; $idx < $pixels; $idx++)
{
    $chosen = $pixelid[$idx];
    ...

大图像

对于大图像,处理数组的成本太高,而且内存不足。因此,为了避免mt_rand()重复命中相同的像素(当图像完成 99% 时这可能会出现问题,因此随机命中仍然可行的 1% 像素之一的概率当然是 1%),这个 hack 使用另一个图像作为索引。

这将“数组”限制为 2^24 个条目,即边长为 2^12 或 4096 像素的图像。

内存节省是巨大的:每个图像像素现在需要 16 个字节,而不是大约 176 个字节(这在我的 Linux 64 位机器上)。这意味着一张 1024x1024 像素的图像只需要大约 17M 的 RAM。

在我的系统上,这个脚本每秒处理大约 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) 的平面文件,或保存整个图像。后一种方法更容易理解,并且在每一步都需要存储一个完整的图像,所以如果这是一个游戏并且你想“重播”它,你可能需要巨大的磁盘空间。第一种方法可能合理地要求每个像素六个字节,即相当于一个具有相同高度且宽度是原始图像的两倍的图像,或者每个像素只有四个字节。

于 2012-11-27T17:16:26.330 回答
0

用您的随机颜色值替换 RANDOM_COLOR

这将替换所有像素,

for($i=0;$i < $x;$i++) {
    for($e = 0;$e < $y;$e++) {
        imagesetpixel($gd, $i,$e, $RANDOM_COLOR);
    }
}

如果您只想设置几个随机像素,请确保将它们设置在 0 和宽度 -1、0 和高度 -1 的范围内

例如

$i = rand(0,$width-1);
$e = rand(0,$height - 1);
imagesetpixel($gd, $i,$e, $RANDOM_COLOR);

你可以循环使用它

于 2012-11-25T19:37:04.927 回答
0

在食人魔的帮助下,我得到了部分工作。

我所做的是操纵顶层,并在操纵后将其与函数 imagecopy 与源图像合并。输出是 jpg 图像。

顶层的操作是用这段代码完成的

for ($y = 0; $y < imagesy($img); $y++) {
for ($x = 0; $x < imagesx($img); $x++) {
$img = imagecreatefrompng("thecover.png");
    imagealphablending($img, false); // Turn off blending
    $white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);

            $rgb = imagecolorat($img, $x, $y);
            $pixel_color = imagecolorsforindex($img, $rgb);
            if ($pixel_color['red'] == 255 && $pixel_color['green'] == 255 && $pixel_color['blue'] == 255){
                for ($i = 0; $i < 200; $i++) {
                    imagesetpixel($img, rand(0,300), rand(0,300), $white_color_transparent);
                    }
            }
}
}

顶层的颜色是白色,RGB (0,0,0)。该脚本检查该颜色并在此时将 200 个随机像素设置为透明。

它将很多像素设置为透明,但不是给定的 200。

可以帮助我的人。

于 2012-11-27T12:41:19.423 回答