用php GD进行单像素操作

时间:2012-11-25 19:31:15

标签: php image-processing php-gd

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

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

  • 用于在浏览器中创建图像的imagecreatetruecolor
  • imagecolorallocate用于从source-image返回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功能的原因。希望你能给我一个初创公司

3 个答案:

答案 0 :(得分: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(),并且在每次迭代时我从{{1}中提取第i个像素}。

最后一个选项是使用$pixelid重新实现array_shuffle(),详见手册页(详见le letiost dot com的示例):

mt_rand

因此,您可以使用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]; ... 重复击中相同的像素(当图像完成99%时可能会出现问题,因此随机击中的可能性之一仍是可行的1%像素当然是1% ),这个hack使用另一个图像作为索引。

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

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

在我的系统上,此脚本每秒处理大约180k像素(1024x1024图像在7.4秒内100%处理,其中大约需要2张图像加载和设置)。

mt_rand()

优化

您会注意到上面代码中的注释部分。如果恰好$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); 超过50%,比如说70%,那么创建一个空图像并将70%的像素从好图像复制到空图像是没有意义的。将30%的空像素从空图像复制到好图像更有意义,将其“黑化”。只需交换$position$new并调整$src即可实现此目的。我还没有真正彻底测试过这段代码;这就是我离开它评论的原因。但是欢迎你试一试。

实施

使用PRNG的优势在于您不需要保存任何图像,但只有$positionseed - 通常是8个字节。

如果A人收到位置1,并要求接收最多5个位置(即可见图像的5%),并保存种子和该值5,并将其用于B,那么B人将会看到与A相同的5%。

除原始图像外,没有任何图像被保存或加载。

如果你可以在$ _GET参数中传递种子和位置,你可以在浏览器中显示不同阶段的图像(例如position会显示设置了5%像素的图像。你也可以指定当然,像素数而不是它们的百分比。

只要像素是随机选择的就可以:如果人A获得选择他或她想要的确切像素,那么这种方法是无效,您需要保存单个像素位置,这可以通过多种方式完成:使用以二进制格式保存(x,y)对的flatfile,或保存整个图像。后一种方法更容易理解,并且需要在每一步都存储一个完整的图像,所以如果这是一个游戏并且你想“重放”它,你可能需要巨大的磁盘空间。第一种方法可能合理地要求每个像素六个字节,即相当于具有相同高度并且是原始宽度的两倍的图像,或者相当于每个像素四个字节的图像。

答案 1 :(得分: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);

你可以在循环中使用它

答案 2 :(得分:0)

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

我所做的是操纵一个顶层玩家,在操作之后,将它与功能图像复制一起与源图像合并。输出是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。

可以帮助我的人。