用php GD进行单像素操作 [英] single pixel manipulation with php GD

查看:111
本文介绍了用php GD进行单像素操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我指的是上一个问题更改每像素的图像并保存到数据库

First I'm referring to a previous question Change image per pixel and save to db

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

I found that the html5 canvas isn't suitable because it's hard to keep the source-image secret. That's why I'm trying to achieve my goal with the PHP GD library. I never worked with this library so I have some difficulties. I guess I need the following functions


  • 用于在浏览器中创建图像的imagecreatetruecolor

  • imagecolorallocate for从源图像返回rgb

  • 用于绘制随机像素的imagesetpixel

  • imagecreatetruecolor for creating the image in the browser
  • imagecolorallocate for returning rgb from source-image
  • imagesetpixel for drawing the random pixels

$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功能。希望你能给我一个启动

I'm not a star with PHP, that's why my search is stuck with these GD functions. Hope you can give me a start-up

推荐答案

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

One of the "features" of the random function is that it's pseudorandom, i.e., it will always output the same sequence given the same seed.

所以你可以为每个图像存储:

So you could store, for each "image":

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:

So say that you want to output the image $sourcefile with seed $seed and $position percentage of pixels visible. You don't even need to use 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);



变化



而不是拼接数组,你可以抹掉用过的像素,即使这不能给出统一分布:

Variations

Instead of splicing the array, you could blot out the "used" pixels, even if this does not give a uniform distribution:

$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;

或者你可以......重试。但是当图像几乎完成时,这可能会很昂贵。

Or you could just... retry. But this can be expensive when the image is almost complete.

$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

If you do not care that the image is rebuilt always in different manners, you can use array_shuffle() instead of mt_rand(), and at each iteration i extract the i-th pixel from $pixelid.

最后一个选项是使用<重新实现 array_shuffle() code> mt_rand 详见手册页(参见leethost dot com的示例):

A last option is to reimplement the array_shuffle() using mt_rand as detailed in the manual page (see example by 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 ()使用 $ seed $ pixelid ,然后从shuffled中提取元素顺序数组:

So you would call array_new_shuffle() against $pixelid using $seed, and then extract the elements from the shuffled array in sequence:

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



大图像



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

Large Images

For large images, handling the array is too expensive and you get out of memory. So, to avoid mt_rand() repeatedly hitting the same pixels (which can get really problematic when image is 99% complete, and therefore the probability of hitting at random one of the still viable 1% pixels is, of course, 1%), this hack uses another image as the index.

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

This limits the "array" to 2^24 entries, that is, an image with a side of 2^12, or 4096 pixels.

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

The memory savings are huge: each image pixel costs now 16 bytes, instead of around 176 (this on my Linux 64bit machine). This means that a 1024x1024 pixel image requires only about 17M of RAM.

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

On my system, this script processes around 180k pixels per second (a 1024x1024 image was 100% processed in 7.4 seconds, of which around 2 were needed for image loading and setup).

$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 。我还没有真正彻底测试过这段代码;这就是我离开它评论的原因。但是欢迎你试一试。

Optimization

You'll notice a commented section in the above code. If it so happens that $position is more than 50%, say, 70%, it doesn't makes sense to create an empty image and copy 70% of pixels from the good image to the empty image. It makes more sense to copy 30% of empty pixels from the empty image to the good image, "blacking" it out. This can be accomplished by simply swapping $new and $src and adjusting $position. I haven't really thoroughly tested this code; that's why I left it commented. But you're welcome to give it a try.

使用PRNG的优势在于您不需要保存任何图片,但只有种子位置 - 通常总共八个字节。

The advantage in using the PRNG is that you don't need to save any image, but only seed and position - typically eight bytes in all.

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

If person A receives position 1, and asks to receive positions up to 5 (i.e. 5% of image visible), and you save the seed and this value of 5, and use it for person B, then person B will see the same 5% that person A got.

所有没有任何图像被保存或加载除外原来的。

All without any images being saved or loaded except the original one.

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

If you can pass seed and position in the $_GET parameters, you can show in the browser the image at different stages (e.g. image.php?seed=12345678&position=5 would show the image with 5% of pixels set. You can also specify the number of pixels instead of their percentage, of course).

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

This works as long as the pixels are chosen at random: if person A gets to choose the exact pixels he or she wants, then this approach is not valid and you need to save the individual pixel positions, which can be done in several ways: using a flatfile holding couples of (x,y) in binary format, or saving the whole image. The latter approach is easier to understand and needs storage for one whole image at each step, so if this is a game and you want to "replay" it, you might need huge disk space. The first approach might reasonably require six bytes per pixel, i.e. equivalent to an image which has the same height and is twice as wide as the original, or as few as four bytes per pixel.

这篇关于用php GD进行单像素操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆