Usually images are watermarked using a predefined color (let’s say for example “white”). How well does this actually work when you’re doing the watermark on a light or even a white image? The answer is simple: not very well.
This example illustrates how to use ImagickPixelIterator to get the average luminosity of the background and chosing text color according to it. I tested this algorithm very briefly and the results seemed positive. There’s four example images posted at the end of this post to show why background matters when doing a watermark.
Here is the code:
- <?php
- /* Allowed images */
- $allowed = array( "strawberry.png", "Image32.png" );
- $img = ( isset( $_GET['img'] ) && in_array( $_GET['img'], $allowed ) )
- ? $_GET['img'] : 'strawberry.png';
- /* Read the image in. This image will be watermarked. */
- $image = new Imagick( $img );
- $image->setImageFormat( "png" );
- /* The text to write on the mark */
- $text = "www.valokuva.org";
- /* This object will hold the font properties */
- $draw = new ImagickDraw();
- /* Setting gravity to the center changes the origo
- where annotation coordinates are relative to */
- $draw->setGravity( Imagick::GRAVITY_CENTER );
- /* Use a custom truetype font */
- $draw->setFont( "./WCManoNegraBta.ttf" );
- /* Set the font size */
- $draw->setFontSize( 26 );
- /* Create a new imagick object */
- $im = new imagick();
- /* Get the text properties */
- $properties = $im->queryFontMetrics( $draw, $text );
- /* Region size for the watermark.
- Add some extra space on the sides */
- $watermark['w'] = intval( $properties["textWidth"] + 5 );
- $watermark['h'] = intval( $properties["textHeight"] + 5 );
- /* Create a canvas using the font properties.
- Add some extra space on width and height */
- $im->newImage( $watermark['w'], $watermark['h'],
- new ImagickPixel( "transparent" ) );
- /* Get a region pixel iterator to get the pixels in the watermark area */
- $it = $image->getPixelRegionIterator( 0, 0, $watermark['w'], $watermark['h'] );
- $luminosity = 0;
- $i = 0;
- /* Loop trough rows */
- while( $row = $it->getNextIteratorRow() )
- {
- /* Loop trough each column on the row */
- foreach ( $row as $pixel )
- {
- /* Get HSL values */
- $hsl = $pixel->getHSL();
- $luminosity += $hsl['luminosity'];
- $i++;
- }
- }
- /* If we are closer to white, then use black font and
- the other way around */
- $textColor = ( ( $luminosity / $i ) > 0.5 ) ?
- new ImagickPixel( "black" ) :
- new ImagickPixel( "white" );
- /* Use the same color for the shadow */
- $draw->setFillColor( $textColor );
- /* Use png format */
- $im->setImageFormat( "png" );
- /* Annotate some text on the image */
- $im->annotateImage( $draw, 0, 0, 0, $text );
- /* Clone the canvas to create the shadow */
- $watermark = $im->clone();
- /* Set the image bg color to black. (The color of the shadow) */
- $watermark->setImageBackgroundColor( $textColor );
- /* Create the shadow (You can tweak the parameters
- to produce "different" kind of shadows */
- $watermark->shadowImage( 80, 2, 2, 2 );
- /* Composite the text on the background */
- $watermark->compositeImage( $im, Imagick::COMPOSITE_OVER, 0, 0 );
- /* Composite the watermark on the image to the top left corner */
- $image->compositeImage( $watermark, Imagick::COMPOSITE_OVER, 0, 0 );
- /* Display the results */
- header( "Content-Type: image/png" );
- echo $image;
- ?>
Here is strawberry.png with black watermark (this was chosen by the script):

And here is strawberry.png with white watermark:

Image32.png with white watermark (this was chosen by the script):

Image32.png with black watermark:

P.S. I opened a new page for the Windows Imagick builds. Seems like my dynamic builds work pretty well, so I will keep providing them thru this site.
#1 by Tom Taylor on October 13, 2007 - 3:41 pm
Quote
This is good, but shouldn’t the script select the pixel luminosity from the area where the watermark will go, and not from the picture as a whole.
#2 by Mikko Koppanen on October 13, 2007 - 3:43 pm
Quote
A quote from the code:
/* Get a region pixel iterator to get the pixels in the watermark area *
$it = $image->getPixelRegionIterator( 0, 0, $watermark['w'], $watermark['h'] )
Trackback: PHPDeveloper.org
Pingback: developercast.com » Mikko Koppanen’s Blog: Choosing watermark color based on the background luminosity
#3 by Jonathan on October 31, 2007 - 3:47 am
Quote
Is there any secret to getting the set font to work? I tried placing my font’s .ttf file in the same directory as the php script, with no luck. No matter what I try, the default font is always used. Any ideas?
Jonathan
#4 by Mikko Koppanen on October 31, 2007 - 12:26 pm
Quote
Jonathan,
are you sure the font is readable by the webserver user?
#5 by Jonathan on October 31, 2007 - 7:51 pm
Quote
Mikko,
I believe the font is readable by my server (apache). I can access it directly through a URL, which prompts me to download the file. Since the font is in the same folder as my php script, shouldn’t it be easy to get it working?
$draw->setFont( “WCManoNegraBta.ttf” );
Is there anything else that I might need to configure first?
Jonathan
#6 by Jonathan on October 31, 2007 - 7:55 pm
Quote
Mikko,
I just read your question again and realized you meant something else. I am actually running apache on windows right now (for development), so as far as I know, users/permissions shouldn’t be a problem with accessing the file. PHP should be able to access the font file for reading/writing, so I’d assume IMagick should be able to access it as well.
By the way:
I noticed that ImagickDraw.setFont always returns 1.
Jonathan
#7 by Mikko Koppanen on October 31, 2007 - 8:20 pm
Quote
Which version of imagick are you using ?
#8 by Jonathan on November 1, 2007 - 1:39 am
Quote
I’m using one of the DLLs that you’re building:
Imagick 2.0.1rc1 / PHP 5.2 / ImageMagick 6.3.6-Q16 – php_imagick_dyn-Q16.dll
Previously I was just using the latest .dll that shipped with PHP, but it looked to be several months old and I couldn’t get it to run your demo from this blog post without all kinds of errors.
Jonathan
#9 by Jonathan on November 1, 2007 - 1:49 am
Quote
I should also mention… since I’m using the dynamic DLL, I had to copy all of the DLLs from my imagemagick installation into my apache/bin folder. I know this is not ideal, but I couldn’t get it working any other way.
#10 by Mikko Koppanen on November 1, 2007 - 1:54 am
Quote
Ok,
I will upgrade the builds as soon as I get a new Visual Studio license. The problem should be fixed in the CVS!
#11 by Jonathan on November 1, 2007 - 2:43 am
Quote
Sweet, thanks. Looking forward to the new DLL.
#12 by Jonathan on November 1, 2007 - 2:47 am
Quote
Mikko,
I forgot to ask another question I have about this example.. I’d like to try placing the annotation text in the bottom right corner, but when I set the draw object’s gravity to southeast I’m still receiving the annotation in the top left corner. How can I move the annotation around?
Thanks,
Jonathan
#13 by jmaes on November 1, 2007 - 4:22 pm
Quote
How would you watermark an animated gif with imagick? I would assume that you have to use coalesce but haven’t seen any examples using imagick extension.
#14 by Mikko Koppanen on November 1, 2007 - 4:30 pm
Quote
jmaes,
Do you mean different color for each frame?
#15 by jmaes on November 1, 2007 - 10:54 pm
Quote
Based on the background color for the animation.
#16 by Mikko Koppanen on November 1, 2007 - 11:09 pm
Quote
jmaes,
take a look at snimating a gif image in this blog. Combining that with this example should give you the expected results.
#17 by kakapo on November 14, 2007 - 6:58 am
Quote
I change the line $draw->setFont( “./simhei.ttf” ), but it does not work.
my imagick version is 2.1.0-rc1;
and I want to know if it works, can Chinese character be display correctly?
#18 by Mikko Koppanen on November 14, 2007 - 12:10 pm
Quote
kakapo,
I see no reason why they wouldn’t be. Do you have “simhei.ttf” in the same directory with the script?
#19 by kakapo on November 14, 2007 - 12:53 pm
Quote
Sure, i have. Even though i change $draw->setFont(“Fixed”), it also does not work. I put all of the imagemagick configuration below, please help me to see which one is missing?
Host system type : i686-pc-linux-gnu
Option Value
——————————————————————————-
Shared libraries –enable-shared=yes yes
Static libraries –enable-static=yes yes
Module support –with-modules=yes yes
GNU ld –with-gnu-ld=yes yes
Quantum depth –with-quantum-depth=16 16
High Dynamic Range Imagery
–enable-hdri=no no
Delegate Configuration:
BZLIB –with-bzlib=yes yes
DJVU –with-djvu=no no
DPS –with-dps=yes no
FlashPIX –with-fpx=yes no
FontConfig –with-fontconfig=no no
FreeType –with-freetype=yes yes
GhostPCL None pcl6 (unknown)
Ghostscript None gs (7.07)
result_ghostscript_font_dir=’none’
Ghostscript fonts –with-gs-font-dir=default
Ghostscript lib –with-gslib=yes no
Graphviz –with-gvc=yes no
JBIG –with-jbig=yes no
JPEG v1 –with-jpeg=yes yes
JPEG-2000 –with-jp2=yes no
LCMS –with-lcms=yes no
Magick++ –with-magick-plus-plus=yes yes
OpenEXR –with-openexr=yes no
PERL –with-perl=yes /usr/bin/perl
PNG –with-png=yes yes
RSVG –with-rsvg=no no
TIFF –with-tiff=yes no
result_windows_font_dir=’none’
Windows fonts –with-windows-font-dir=
WMF –with-wmf=yes yes
X11 –with-x= no
XML –with-xml=no no
ZLIB –with-zlib=yes yes
X11 Configuration:
X_CFLAGS =
X_PRE_LIBS =
X_LIBS =
X_EXTRA_LIBS =
Options used to compile and link:
PREFIX = /usr/local
EXEC-PREFIX = /usr/local
VERSION = 6.3.6
CC = gcc
CFLAGS = -g -O2 -Wall -W -pthread
MAGICK_CFLAGS = -g -O2 -Wall -W -pthread
CPPFLAGS = -I/usr/local/include
PCFLAGS =
DEFS = -DHAVE_CONFIG_H
LDFLAGS = -L/usr/local/lib -Wl,–rpath -Wl,/usr/local/lib -lfreetype -lz
MAGICK_LDFLAGS = -L/usr/local/lib -L/usr/local/lib -Wl,–rpath -Wl,/usr/local/lib -lfreetype -lz
LIBS = -lMagick -lfreetype -ljpeg -lbz2 -lz -lm -lpthread
CXX = g++
CXXFLAGS = -g -O2 -Wall -W -pthread
#20 by Mikko Koppanen on November 14, 2007 - 1:17 pm
Quote
kakapo,
use $im->queryFonts(); to see what fonts are configured. Also check that ImageMagick configuration (type-ghostscript.xml) font names map to correct files.
#21 by kakapo on November 15, 2007 - 6:43 am
Quote
Thank you! MKoppanen. I renew to install the Imagemagick, and the problem is resolved.
Maybe the problem is that the freetype libs are not included by imagemagick before.
Because I fogot to delete the old configuration files and re-install imagemagick when I found freetype2 libs are missing .
#22 by kakapo on November 15, 2007 - 6:50 am
Quote
Mikko Koppanen, how to add a border to surround the font? borderImage is to surround the image with a border.
#23 by Mikko Koppanen on November 15, 2007 - 10:00 am
Quote
kakapo,
I assume the easiest way is to use Imagick::queryFontMetrics() and ImagickDraw::rectangle().
#24 by Anthony Thyssen on February 4, 2008 - 3:13 am
Quote
Alternative, rather than using a pixel iterator to determine the ‘average’ color for that area, why not just resize the region to 1 pixel and check just that single pixel!!!!
that would be a LOT easier, and quite fast if you used ‘scale’.
Anthony — IM examples webmaster
#25 by Mikko Koppanen on February 4, 2008 - 3:42 am
Quote
Anthony,
indeed that is a good trick! Is MagickScaleImage using “average” filter?
#26 by fedmich on May 27, 2008 - 10:18 am
Quote
cool as usual. Neat trick!
#27 by vhdkm on July 3, 2008 - 3:25 pm
Quote
Great site! Dont forget the lyrics audition dflfp
#28 by Marco Pracucci on August 30, 2008 - 7:58 pm
Quote
I have written a post about how to automatically select the image region where apply a watermark. Maybe you’re interested.
#29 by Marco Pracucci on September 6, 2008 - 1:51 pm
Quote
Sorry, the previous link does not work anymore:
http://blog.pracucci.com/2008/08/30/watermarks-with-php-and-imagick/
#30 by MrABC on September 29, 2008 - 4:50 pm
Quote
Hello i have a problem.
http://img401.imageshack.us/my.php?image=preview1222696230ct7.png
i use the same script with php 5.2 + php_imagick_st-Q8.dll
can some1 help me? i m newbie in imagick!
thx
#31 by MrABC on October 2, 2008 - 5:38 pm
Quote
hello! i try to make the shadow on the left site but..
$watermark->shadowImage( 80, 2, -2, -2 ); –> dont work
$watermark->shadowImage( 80, 2, -20, -20 ); –> dont work
$watermark->shadowImage( 80, 2, -2000, -2000 ); –> dont work
is that a bug? or i do wrong?
thx
#32 by Zashkaser on August 5, 2009 - 7:24 pm
Quote
Are you from San Diego?
#33 by John Anderson on April 16, 2011 - 2:42 pm
Quote
Thanks for sharing this code! This is very cool!
#34 by Peter on August 25, 2011 - 12:05 pm
Quote
Nice and useful code! It worked for me!