Screenshot smoothening

Page 2/3
1 | | 3

By [D-Tail]

Ascended (8232)

[D-Tail]'s picture

22-06-2004, 23:04

Okay. So far so good. I know now what I use the files for. The ImageMagick 28.1MB package is for the IM system itself, it contains all functions, etc. The php_imagick.dll is a PHP4/PHP5 wrapper for this system, thus enabling PHP to use it.
Well, that *nux script which I spoke of before, was on the generic PHP site. That site provided as well a link to the compiled win32 binaries, so I suppose I don't have to worry. But now:

  • What's the difference between "PHP4 binaries for Win32" and "PECL binaries for Win32 (PHP4)"?
  • Which should I use? You can find both of them (and more!) :) HERE :).

Logically, I'd say I'd go for the first one, since I have PHP 4.3.7, but the PECL heading also mentions PHP4...

By Sousuke

Master (177)

Sousuke's picture

22-06-2004, 23:09

IMO those images look too blurry after resizing w/ antialiasing... whatever Smile
1. How do you scale your image? Which algo did you use?
2. How is the code for scaling built up? Would you mind posting the main part here? Perhaps there are some optimizations left. Because scaling a whole image taking > 1 min sounds a bit odd... Tongue Wink Wink

By Sousuke

Master (177)

Sousuke's picture

22-06-2004, 23:16

What's the difference between "PHP4 binaries for Win32" and "PECL binaries for Win32 (PHP4)"?PECL stands for PHP Extension Community Library, those are (precompiled) extensions for the PHP-engine.

By turbor

Champion (426)

turbor's picture

22-06-2004, 23:17


maybe you can read a few lines at once and store the pixeldata in arrays?
pixels are probably more often too, so you can reuse them instead of getting them from the image all the time Smile

What do you mean by reusing them, Arjan? By the way, I use .PNG pictures as input, so there isn't any pallette involved. Hence, any pixel can have any color, and the blending color has to be defined with imagecolorallocate().

About PNG: If I'm not mistaken PNG can be used for color indexed images as well, and can store a colorpallet, but you seem to use it in the TrueColor mode

In SDL you can get the colormasks and colorshifts for each color, so that you can extract each color separately. Isn't this possible in PHP and for blending this can also be used
by adding all color masked pixels and then dividing(or shifting) them back in place and mask them again.
Ok that sentence is unclear but here is an example that will hopefully make it clear:

if a pixel value was something like a screen 8 pixel (your 24 bit number)
then each bit would code for a part of a color for instance red colorcompeonent was stored in bits 7-5,Green in bits 4-2 and blue and the final two bits. The byte value is thus coded as
RRRGGGBB
the BlueColorMask (=BCM) woud be 00000011 ,
the RedColorMask (=RCM) would be 11100000 and
the GreenColorMask (=GCM) would be 00011100

if you want to make pixel 1 (=P1) average the value of its four neighbours (P2,P3,P4 and P5) then the new value would be something like
P1=( ( (P2 & BCM) +(P3 & BCM) +(P4 & BCM) +(P5 & BCM) ) /4) &BCM +
( ( (P2 & RCM) +(P3 & RCM) +(P4 & RCM) +(P5 & RCM) ) /4) &RCM +
( ( (P2 & GCM) +(P3 & GCM) +(P4 & GCM) +(P5 & GCM) ) /4) &GCM


And you would really like to have arrays with a length of about 512*3 bytes for one line?! (.PNG is truecolor, 24bpp...)

I would assume that looking up each pixel in such array would be way faster then calling the other pixel functions... I would store each line in one array of 512 elements of 24(or 32) bits numbers. And I would store 3 lines at a time ass well to speed things up :-)

It is a trade of, do you want speed or a small memory footprint?

rgds,

David

Edit: This forum has troubles with nested quote tags!

By [D-Tail]

Ascended (8232)

[D-Tail]'s picture

22-06-2004, 23:29

PECL stands for PHP Extension Community Library, those are (precompiled) extensions for the PHP-engine.
Ah, so I'd better use the PECL version instead?

IMO those images look too blurry after resizing w/ antialiasing... whatever
1. How do you scale your image? Which algo did you use?
2. How is the code for scaling built up? Would you mind posting the main part here? Perhaps there are some optimizations left. Because scaling a whole image taking > 1 min sounds a bit odd...

Yes, the algorythm is too basic to discuss, really Wink

1. I don't scale the image. I just took them right from NLMSX (pressing F9 for the screenshot-procedure, ey? Tongue)
2. Erm... one moment please Wink (variable names are mostly in Dutch Wink. The only thing you need to know is that everything beginning letter (r, g, b, for example $rood, $groen and $blauw) stand for red, green and blue intensities, respectively Wink. Now, If you'd allow me...

> 16 & 0xFF;
          $g[$c]=$sc[$c] >> 8 & 0xFF;
          $b[$c]=$sc[$c] & 0xFF;
        }

        $rood=$groen=$blauw=0;

        for ($c=0; $c<8; $c++) {
          $rood+=$r[$c];
          $groen+=$g[$c];
          $blauw+=$b[$c];
        }

        for ($c=0; $c<4; $c++) {
          $rood+=$r[$c];
          $groen+=$g[$c];
          $blauw+=$b[$c];
        }

        $rood/=12;
        $groen/=12;
        $blauw/=12;

        /**
         * The following lines are for brightness control.
         * Uncommment them if you think the output's too dark.
         */
         
        /*$rood+=12;
        $groen+=12;
        $blauw+=12;

        if ($rood>255)
          $rood=255;
        if ($groen>255)
          $groen=255;
        if ($blauw>255)
          $blauw=255;*/

        $blend_color=imagecolorallocate($image, $rood, $groen, $blauw);

        imagesetpixel($im, $x, $y, $blend_color);
      }
    }
    return $im;
  }

As you can see, it takes a resource type variable for input, and produces a new one (beginning from imagecreatetruecolor()), which is returned in the very end. The upper scripts will take care for proper display on HTML pages.
Of course, except for stripping the white lines (of which I don't think it will matter), there's a lot of optimization to do, for I really *hacked* it... Tongue

Plus, I do some illegal actions, e.g. getting pixels outside of the image's borders. But I doubt that will give the tremendous speed issue. As you can see now, the script is really hacked into another Wink

By [D-Tail]

Ascended (8232)

[D-Tail]'s picture

22-06-2004, 23:38

Now, the "algorythm" described in words:

FOR ALL PIXELS(x,y) {
1 Let's estimate the color of the pixel in the new image at position x,y. Therefore:
2 we need the colors of the surrounding pixels in the original picture. Those pixels are located at:

  • x, y-1
  • x+1, y-1
  • x+1, y
  • x+1, y+1
  • x, y+1
  • x-1, y+1
  • x-1, y
  • x-1, y-1

3 We obtain the color of each of those pixels
4 The pixels directly (e.g. not diagonal) adjacent to the to-be-estimated-pixel weigh heavier than the diagonal-adjacent pixels (say, 2 times).
5 This makes up for a total of 2+2+2+2+1+1+1+1 = 12 R, G and B intensities.
6 Finally, we calculate the average, and assign a blend color.
7 A pixel with color==blend color is set at x,y in the new picture.
}

Fin.

[edit]The algorythm is too simple for putting it into words, but the results are quite nice, and the execution time should be amazingly fast... Sad[/edit]

By Sousuke

Master (177)

Sousuke's picture

23-06-2004, 00:25

variable names are mostly in Dutch Don't worry about that, I can speak/read dutch, so it's no problem for me Wink Besides, it should be clear when reading the code Smile

Bout the algo: I don't know how GD works, but is there a way to get/set a whole image line? Perhaps the bottleneck is the imagecolorat-call.
I suppose that function calculates the location of the specified pixel and then reads it, and that would take *much* time. Additionally (nearly) every pixel is read 8 times. Tongue
So try reading lines, process them, then put back the new line. Should be much much faster that way Smile
(Think someone mentioned this before Smile)

Oh and just one small speed up:
You are creating a 24bit-image, so this function $blend_color=imagecolorallocate($image, $rood, $groen, $blauw); is not necessary. Simply write $blend_color=$rood & 0xFF << 0x10 | $groen & 0xFF << 0x08 | $blauw & 0xFF;
Hope it works Wink

[edit]Ah, so I'd better use the PECL version instead?Well those are addons or plug-ins for your PHP4. Dunno what extra modules you need Smile
Have a look here in their database pecl.php.net[/edit]

By mth

Champion (484)

mth's picture

23-06-2004, 01:51

I'm not familiar with PHP or its libs, but it seems strange that you call imagecolorallocate on the source image ($image) instead of the target image ($im).

Also, I'm surprised you take the weighted average of all neighbouring pixels, but ignore the colour of the centre pixel. You could try to add it with a weight of 4 and then divide by 16 instead of 12. That won't make things faster, but the output will become a bit sharper.

Vampier once made a SCREEN8 to PNG/BMP (I forgot) converter in PHP, it was fast enough to use for realtime conversions. So PHP as a language should be fast enough for image conversion algorithms. Probably one of the operations you're using is much slower than expected.

Try to disable parts of your code and see what the impact on performance is. For example, ignore the input completely and write RGB=(x mod 256, y mod 256, 0) to the output image. Try to fetch only 4 neighbours instead of 8. Try to duplicate the for-loops that add to rood/groen/blauw and divide by 24, see if it becomes twice as slow or doesn't change a lot in performance. Generally, just play a bit with the different parts of the code to get a feeling for where most CPU time is spent.

A completely different solution would be to use a drawing program to apply the algorithm. Most of them have pre-cooked blur filters, as well as the ability to create your own filter. For example, in the GIMP you can use the option Filters -> Generic -> Convolution Matrix. Paint Shop Pro also has an option to create your own filter. The matrix for your filter looks like this:
0 0 0 0 0
0 1 2 1 0
0 2 0 2 0
0 1 2 1 0
0 0 0 0 0
with divisor=12 and offset=0.
(Doing the conversion in PHP certainly has a cool factor, but since you'll have to cut out the interesting bits of the screenshot in an image editor anyway, one extra step shouldn't be a real annoyance.)

By Sousuke

Master (177)

Sousuke's picture

23-06-2004, 03:00

Try to disable parts of your code and see what the impact on performance is.I've played a bit w/ the GD-library. And as I already supposed, the (multiple) imagecolorat-calls are the main slowdown of the smoothener.

I my new algo reads line for line and processes them, instead of reading pixel for pixel. Of course the code has become a bit larger and more complex, but it doesn't take so long anymore (and my PHP-engine doesn't complain about too long execution times Smile)

Hopy that there are no fatal mistakes in the code, and you can read/understand/use the code Smile

  function anti_alias2($image)
  {
    // avoid function calls :)
    $iw=imagesx($image);
    $ih=imagesy($image);

    // create new image
    $im=imagecreatetruecolor($iw, $ih);

    // initialize line buffers
    $l1r=array_fill(0,$iw-1,0); $l2r=array_fill(0,$iw-1,0); $l3r=array_fill(0,$iw-1,0);
    $l1g=array_fill(0,$iw-1,0); $l2g=array_fill(0,$iw-1,0); $l3g=array_fill(0,$iw-1,0);
    $l1b=array_fill(0,$iw-1,0); $l2b=array_fill(0,$iw-1,0); $l3b=array_fill(0,$iw-1,0);

    // read first line
    for ($x=0; $x<$iw; $x++)  {
      $pixel = imagecolorat($image, $x, 0);
      $l3r[$x] = ($pixel >> 0x10) & 0xff;
      $l3g[$x] = ($pixel >> 0x08) & 0xff;
      $l3b[$x] = ($pixel)         & 0xff;
    };


    // vertical loop (through lines 1 to h - skip first line)
    for ($y=0; $y<$ih; $y++) {
      // update buffers   
      $l1r=$l2r; $l1g=$l2g; $l1b=$l2b;
      $l2r=$l3r; $l2g=$l3g; $l2b=$l3b;
      if ($y < ($ih+1)) {
        for ($x=0; $x<$iw; $x++) {
          $pixel = imagecolorat($image, $x, $y);
          $l3r[$x] = ($pixel >> 0x10) & 0xff;
          $l3g[$x] = ($pixel >> 0x08) & 0xff;
          $l3b[$x] = ($pixel)         & 0xff;
        };
      }
      else {
        $l3r=array_fill(0,$iw-1,0);
        $l3g=array_fill(0,$iw-1,0);
        $l3b=array_fill(0,$iw-1,0);
      };
      // horizontal loop (through pixels) - here's the main algo
      for ($x=0; $x<$iw; $x++) {
        $red   = ((($l2r[$x-1] + $l2r[$x+1] + $l1r[$x  ] + $l3r[$x  ]) << 1)+
                   ($l1r[$x-1] + $l3r[$x-1] + $l1r[$x+1] + $l3r[$x+1]));
        $green = ((($l2g[$x-1] + $l2g[$x+1] + $l1g[$x  ] + $l3g[$x  ]) << 1) +
                   ($l1g[$x-1] + $l3g[$x-1] + $l1g[$x+1] + $l3g[$x+1]));
        $blue  = ((($l2b[$x-1] + $l2b[$x+1] + $l1b[$x  ] + $l3b[$x  ]) << 1) +
                   ($l1b[$x-1] + $l3b[$x-1] + $l1b[$x+1] + $l3b[$x+1]));
        $red   = (($red   / 12)+12); if ($red   > 0xff) $red   = 0xff;
        $green = (($green / 12)+12); if ($green > 0xff) $green = 0xff;
        $blue  = (($blue  / 12)+12); if ($blue  > 0xff) $blue  = 0xff;
        $l2r[$x] = $red;
        $l2g[$x] = $green;
        $l2b[$x] = $blue;
      };
      // update image
      for ($x=0; $x<$iw; $x++) {
        $pixel = (($l2r[$x] << 0x10) | ($l2g[$x] << 0x08) | ($l2b[$x]));
        imagesetpixel($im, $x, $y, $pixel);
      };
    };

    // return new image 
    return $im;
  };

Here's the test-page: www.hl-soft.net/test.php
Have phun ;)

By Sonic_aka_T

Enlighted (4130)

Sonic_aka_T's picture

23-06-2004, 17:09

Two suggestions... One, try averaging only with the direct neighbours. This should give a pretty nice result too, and you would only have to check 4 pixels instead of 8. Also I would go a bit easier on the blur effect. Try a weight of perhaps 1 for each neighbour and a weight of 2 or 4 for the original color. You would probably cut your time in half (at least) and still get a reasonable result. I for one would give it a go...

Page 2/3
1 | | 3