Archive for November, 2007

Creating buttons with Imagick

A fellow called kakapo asked me to create a button with Imagick. He had an image of the button and a Photoshop tutorial but unfortunately the tutorial was in Chinese. My Chinese is a bit rusty so it will take a little longer to create that specific button ;)

The button in this example is created after this tutorial http://xeonfx.com/tutorials/easy-button-tutorial/ (yes, I googled "easy button tutorial"). The code and the button it creates are both very simple but the effect looks really nice.

Here we go with the code:

PHP:
  1. <?php
  2.  
  3. /* Create a new Imagick object */
  4. $im = new Imagick();
  5.  
  6. /* Create empty canvas */
  7. $im->newImage( 200, 200, "white", "png" );
  8.  
  9. /* Create the object used to draw */
  10. $draw = new ImagickDraw();
  11.  
  12. /* Set the button color.
  13.    Changing this value changes the color of the button */
  14. $draw->setFillColor( "#4096EE" );
  15.  
  16. /* Create the outer circle */
  17. $draw->circle( 50, 50, 70, 70 );
  18.  
  19. /* Create the smaller circle on the button */
  20. $draw->setFillColor( "white" );
  21.  
  22. /* Semi-opaque fill */
  23. $draw->setFillAlpha( 0.2 );
  24.  
  25. /* Draw the circle */
  26. $draw->circle( 50, 50, 68, 68 );
  27.  
  28. /* Set the font */
  29. $draw->setFont( "./test1.ttf" );
  30.  
  31. /* This is the alpha value used to annotate */
  32. $draw->setFillAlpha( 0.17 );
  33.  
  34. /* Draw a curve on the button with 17% opaque fill */
  35. $draw->bezier( array(
  36.                     array( "x" => 10 , "y" => 25 ),
  37.                     array( "x" => 39, "y" => 49 ),
  38.                     array( "x" => 60, "y" => 55 ),
  39.                     array( "x" => 75, "y" => 70 ),
  40.                     array( "x" => 100, "y" => 70 ),
  41.                     array( "x" => 100, "y" => 10 ),
  42.                  ) );
  43.  
  44. /* Render all pending operations on the image */            
  45. $im->drawImage( $draw );
  46.  
  47. /* Set fill to fully opaque */
  48. $draw->setFillAlpha( 1 );
  49.  
  50. /* Set the font size to 30 */
  51. $draw->setFontSize( 30 );
  52.  
  53. /* The text on the */
  54. $draw->setFillColor( "white" );
  55.  
  56. /* Annotate the text */
  57. $im->annotateImage( $draw, 38, 55, 0, "go" );
  58.  
  59. /* Trim extra area out of the image */
  60. $im->trimImage( 0 );
  61.  
  62. /* Output the image */
  63. header( "Content-Type: image/png" );
  64. echo $im;
  65.  
  66. ?>

And here is a few buttons I created by changing the fill color value:

red

green

blue

Creating a reflection

Here is a simple example of creating a reflection of an image. The reflection is created by flipping the image and overlaying a gradient on it. Then both, the original image and the reflection is overlayed on a canvas.

This example is created for Imagick 2.1.x but with a little tuning it should work with earlier versions.

PHP:
  1. <?php
  2.  
  3. /* Read the image */
  4. $im = new Imagick( "strawberry.png" );
  5.  
  6. /* Thumbnail the image */
  7. $im->thumbnailImage( 200, null );
  8.  
  9. /* Create a border for the image */
  10. $im->borderImage( "white", 5, 5 );
  11.  
  12. /* Clone the image and flip it */
  13. $reflection = $im->clone();
  14. $reflection->flipImage();
  15.  
  16. /* Create gradient. It will be overlayd on the reflection */
  17. $gradient = new Imagick();
  18.  
  19. /* Gradient needs to be large enough for the image
  20. and the borders */
  21. $gradient->newPseudoImage( $reflection->getImageWidth() + 10,
  22.                            $reflection->getImageHeight() + 10,
  23.                            "gradient:transparent-black"
  24.                         );
  25.  
  26. /* Composite the gradient on the reflection */
  27. $reflection->compositeImage( $gradient, imagick::COMPOSITE_OVER, 0, 0 );
  28.  
  29. /* Add some opacity */
  30. $reflection->setImageOpacity( 0.3 );
  31.  
  32. /* Create empty canvas */
  33. $canvas = new Imagick();
  34.  
  35. /* Canvas needs to be large enough to hold the both images */
  36. $width = $im->getImageWidth() + 40;
  37. $height = ( $im->getImageHeight() * 2 ) + 30;
  38. $canvas->newImage( $width, $height, "black", "png" );
  39.  
  40. /* Composite the original image and the reflection on the canvas */
  41. $canvas->compositeImage( $im, imagick::COMPOSITE_OVER, 20, 10 );
  42. $canvas->compositeImage( $reflection, imagick::COMPOSITE_OVER,
  43.                         20, $im->getImageHeight() + 10 );
  44.  
  45. /* Output the image*/
  46. header( "Content-Type: image/png" );
  47. echo $canvas;
  48.  
  49. ?>

The source image:

source

And the result:

result

P.S. Please send me some new images which I can use in these examples ;)

ImagickPixelIterator is not read-only after all..

A few days ago I got a help request from a user: "How do you change pixel color during the iteration with ImagickPixelIterator". My initial response was that ImagickPixelIterator is read-only.

Well, I have to admit I was wrong. After searching trough ImageMagick docs I stumbled across an example and noticed that PixelIterator (and therefor ImagickPixelIterator) is not read-only after all. I have tried the code like in this example before; without the syncIterator call after each row. After adding the Imagick::syncIterator call everything worked as expected.

This example will work with Imagick 2.1.0 (the RC1 was released yesterday) but with a little tweaking it should work with 2.0.x too.

PHP:
  1. <?php
  2.  
  3. /* Create new object with the image */
  4. $im = new Imagick( "strawberry.png" );
  5.  
  6. /* Get iterator */
  7. $it = $im->getPixelIterator();
  8.  
  9. /* Loop trough pixel rows */
  10. foreach( $it as $row => $pixels )
  11. {
  12.     /* For every second row */
  13.     if ( $row % 2 )
  14.     {
  15.         /* Loop trough the pixels in the row (columns) */
  16.         foreach ( $pixels as $column => $pixel )
  17.         {
  18.                 /* Paint every second pixel black*/
  19.                 if ( $column % 2 )
  20.                 {
  21.                         $pixel->setColor( "black" );
  22.                 }
  23.         }
  24.     }
  25.    
  26.     /* Sync the iterator, this is important
  27.     to do on each iteration */
  28.     $it->syncIterator();
  29. }
  30.  
  31. /* Display the image */
  32. header( "Content-Type: image/png" );
  33. echo $im;
  34.  
  35. ?>

The source image:

strawberry

The result image:

strawberry_result

Color analysis

The idea for today's example comes from Jonathan. Jonathan was asking: "I’d like to request a tutorial. One thing I’d really like to know how to do is analyze an image for it’s color “palette”, or in other words, to find what the primary colors are that are used in an image."

I don't know if this is exactly what he meant but I hope this example can be used as a kickstart for color analysis. The code in the example reduces the image colors to 10, then discards all but one pixel of every color and then creates the palettes out of those colors. This might not be the most accurate way to do this, but at least it's fast :)

The code creates three different "palettes" from image; average palette, dark palette and bright palette. I got the idea for three different palettes from MarcosBL at freenode.

Here is the code:

PHP:
  1. <?php
  2.  
  3. /* The original image is the average colors */
  4. $average = new Imagick( "test.png" );
  5.  
  6. /* Reduce the amount of colors to 10 */
  7. $average->quantizeImage( 10, Imagick::COLORSPACE_RGB, 0, false, false );
  8.  
  9. /* Only save one pixel of each color */
  10. $average->uniqueImageColors();
  11.  
  12. /* Clone the average and modulate to brighter */
  13. $bright = $average->clone();
  14. $bright->modulateImage ( 125, 200, 100 );
  15.  
  16. /* Clone the average and modulate to darker */
  17. $dark = $average->clone();
  18. $dark->modulateImage ( 80, 100, 100 );
  19.  
  20. /* Helper function to create the mini-images */
  21. function createImages( Imagick $composite, Imagick $im )
  22. {
  23.     /* Get ImagickPixelIterator */
  24.     $it = $im->getPixelIterator();
  25.    
  26.     /* Reset the iterator to begin */
  27.     $it->resetIterator();
  28.  
  29.     /* Loop trough rows */
  30.     while( $row = $it->getNextIteratorRow() )
  31.     {
  32.         /* Loop trough columns */
  33.         foreach ( $row as $pixel )
  34.         {      
  35.             /* Create a new image which contains the color */
  36.             $composite->newImage( 20, 20, $pixel );
  37.             $composite->borderImage( new ImagickPixel( "black" ), 1, 1 );
  38.         }
  39.     }
  40. }
  41. /* This object holds the color images */
  42. $composite = new Imagick();
  43.  
  44. /* Create "icons" for each palette */
  45. createImages( $composite, $dark );
  46. createImages( $composite, $average );
  47. createImages( $composite, $bright );
  48.  
  49. /* Montage the color images into single image
  50.    Ten images per row, three rows */
  51. $montage = $composite->montageImage( new imagickdraw(), "10x3+0+0",
  52.                                       "20x20+4+3>", imagick::MONTAGEMODE_UNFRAME,
  53.                                       "0x0+3+3" );
  54.  
  55. /* Free some resources */
  56. $composite->destroy();
  57.  
  58. /* Create an empty canvas */
  59. $canvas = new Imagick();
  60. $canvas->newImage( $montage->getImageWidth() + 55,
  61.                    $montage->getImageHeight(),
  62.                    new ImagickPixel( "white" ) );
  63.  
  64. /* Display the canvas as png */
  65. $canvas->setImageFormat( "png" );
  66.  
  67. /* Set font size to 12 points */
  68. $draw = new ImagickDraw();
  69. $draw->setFontSize( 12 );
  70.  
  71. /* Create legends for each palette */
  72. $canvas->annotateImage( $draw, 5, 20, 0, "Dark: " );
  73. $canvas->annotateImage( $draw, 5, 45, 0, "Average: " );
  74. $canvas->annotateImage( $draw, 5, 70, 0, "Bright: " );
  75.  
  76. /* Composite the montaged images next to texts */
  77. $canvas->compositeImage( $montage, Imagick::COMPOSITE_OVER, 55, 0 );
  78.  
  79. /* Output the image */
  80. header( "Content-Type: image/png" );
  81. echo $canvas;
  82.  
  83. ?>

Edited the example to work with 2.0.x

The first source image:

strawberry

The created palette:

strawberry_palette

The second source image:

strawberry

And the palette:

strawberry

And one more:

chev

Palette:

chev

Trimming an image

Especially product images usually "suffer" from this issue; the product itself is composited on a white background and there are large areas of white around the object.

This is a simple example to demonstrate how to easily trim the areas off the image and only display the parts where the object lies.

Imagick::trimImage takes one parameter which is "fuzz". Quoting ImageMagick manual: "By default target must match a particular pixel color exactly. However, in many cases two colors may differ by a small amount. The fuzz member of image defines how much tolerance is acceptable to consider two colors as the same. For example, set fuzz to 10 and the color red at intensities of 100 and 102 respectively are now interpreted as the same color for the purposes of the floodfill."

I use fuzz 0 in this example because the background color pixels are all same color.

PHP:
  1. <?php
  2.  
  3. /* Create the object and read the image in */
  4. $im = new Imagick( "test.png" );
  5.  
  6. /* The background color. This is what we trim. */
  7. $im->setImageBackgroundColor( new ImagickPixel( "rgb(213,213,213)" ) );
  8.  
  9. /* Trim the image. */
  10. $im->trimImage( 0 );
  11.  
  12. /* Ouput the image */
  13. header( "Content-Type: image/" . $im->getImageFormat() );
  14. echo $im;
  15.  
  16. ?>

The source image is a simple png image with black circle on gray background:
test image

The trimmed image:
result image