• No results found

Adding a watermark to an image

In document Graphics Programming With Perl (Page 167-175)

combining images

8.2 C OMBINING I MAGES

8.2.3 Adding a watermark to an image

Adding a watermark to an image can be accomplished in two ways: visibly and invis-ibly. Adding a visible watermark is normally done to assert ownership of the copy-right on an image in a clear way, and when compromising the visual integrity of the image does not present a problem.

Watermarks with GD

To add a visual watermark to an image, you can overlay your company logo or some text onto the image. The GD copyMerge() method (see page 140) provides a good way to overlay an image which is mostly transparent onto another image. We will dis-cuss how a small program takes a logo file and an image file, and produces an image with the logo overlaid. The program starts as follows:

#!/usr/local/bin/perl -w use strict;

use GD;

use Getopt::Long;

sub usage {

<<EOF;

Usage : $0 [options] logo image output_file Options:

--opacity N specify opacity of logo in percent.

--gravity SPEC specify where the logo should end up.

One of North, NorthEast, NorthWest, South, SouthEast, SouthWest, East, West, or Center.

EOF }

my $gravity = 'SouthEast';

my $opacity = 50;

GetOptions(

'gravity=s' => \$gravity, 'opacity=i' => \$opacity, ) or die usage();

die usage() unless @ARGV >= 3;

As the usage() subroutine specifies, this program takes the name of an input logo file, the name of the image on which to overlay the logo, and the name of an output file. It pastes the logo on top of the image, using the opacity and gravity specified.

O

Set some defaults

my $watermark_file = shift;

my $image_file = shift;

my $out_file = shift;

my $image = GD::Image->new($image_file) or die "GD cannot read $image_file: ", $! ? "$!\n" : "unknown error\n";

my $watermark = GD::Image->new($watermark_file) or die "GD cannot read $watermark_file: ", $! ? "$!\n" : "unknown error\n";

my ($x, $y) = calculate_coords($gravity,

$image, $watermark);

$image->copyMerge($watermark, $x, $y, 0, 0, $watermark->getBounds(), $opacity);

open(IM, ">$out_file") or die "$out_file: $!";

binmode(IM);

print IM $image->png;

close(IM);

b

This program uses the internals of GD to autodetect the image format3 instead of opening the files itself, which makes it necessary to distinguish between two cases of errors that might cause GD::Image->new() to fail: the errors that set the $! vari-able, and those that don’t. If $! is not set when an error occurs, the string unknown error is appended to the error message. While this is hardly enlightening, it is slightly more elegant than having no error message at all.

c

The coordinates where the watermark image should be placed are determined with a call to calculate_coords(), which is covered later.

d

For this simple program, the logo is always copied as a whole onto the image. This is accomplished by using the call to $watermark->getBounds() to specify the width and height of the rectangle to copy. For a program such as this it is hardly use-ful to allow these parameters to specify anything other than the whole logo, so we don’t provide any command-line switches for them.

sub calculate_coords {

my ($grav, $dst, $src) = @_;

my ($dst_width, $dst_height) = $dst->getBounds();

my ($src_width, $src_height) = $src->getBounds();

my $x = ($dst_width - $src_width )/2;

3 Using the GD::Image::new() method to autodetect the image format requires GD version 1.30 or newer.

O

Default to Center

/West$/i and $x = 0;

/East$/i and $x = $dst_width - $src_width;

}

return ($x, $y);

}

The destination coordinates $x and $y are calculated from the gravity specification by the calculate_coords() subroutine. The gravity specification has been delib-erately chosen to match that of Image::Magick, which makes it easier to replace GD with Image::Magick, should the need arise, and it provides us with a fairly consistent interface. There are no explicit checks in place to verify that the gravity specification is a valid one, since all invalid specifications will cause the logo to be centered on the output image.

The result of this program can be seen in figure 8.2.

Watermarks with Image::Magick

We will now look at an equivalent program, but with a few more options, e.g., using Image::Magick. This program has more options because there are more possibilities for watermarking images with Image::Magick.

#!/usr/local/bin/perl -w use strict;

use Image::Magick;

use Getopt::Long;

sub usage {

<<EOF;

Usage : $0 [options] logo image output_file Options:

--opacity N specify opacity of logo in percent.

--gravity SPEC specify where the logo should end up.

One of North, NorthEast, NorthWest, South, SouthEast, SouthWest, East,

Figure 8.2

Watermarks added to an image by overlaying a logo using GD and Image::Magick.

Both images were generated with the opacity set to 60 percent, and all other command line parameters left at the default value.

West, or Center.

b

The main variable in this code fragment is $compose, which will be used to identify what sort of watermark we want to create. If the emboss option is specified, the pro-gram will create watermarks that look as if they are stamped into the image; otherwise it will create the watermark by simply overlaying the second image. The value this variable has is the value of the compose attribute that we’ll be passing to the Compos-ite() method later on in the program.

c

After the options have been read, and the necessary variables have been declared, the source and watermark images are read from disk. Then, depending on whether the emboss option was specified, the real work can begin:

if ($compose eq 'Over') {

my ($w, $h) = $watermark->Get("width", "height");

my $mask = Image::Magick->new(size => "${w}x${h}");

my $g = 255 - $opacity/100 * 255;

my $color = sprintf "#%02x%02x%02x", $g, $g, $g;

$mask->Read("xc:$color");

my $cur_mask = $watermark->Clone();

$cur_mask->Channel('Matte');

$rc = $mask->Composite(

image => $cur_mask, compose => 'Plus');

warn $rc if $rc;

$rc = $watermark->Composite(

b

Set some defaults

c

Read the input images

d

Create a normal watermark

image => $mask,

$rc = $watermark->Shade(azimuth => 30, elevation => 30);

warn $rc if $rc;

$opts{'geometry'} = $opacity, }

d

Surprisingly, in Image::Magick we need to do quite a bit of work to achieve the same effect as with the presented methods for GD.4 First of all, we need to create a new image, which will be used as a transparency mask to add the desired opacity to the watermark (see also section 12.4.2, “Transparency and Image::Magick,” on page 230).

This image is created the same size as the watermark, and filled with a uniform gray calculated from the specified opacity. The matte channel from the original watermark is then extracted, and combined with the mask. This ensures that wherever the original watermark had a higher transparency (lower opacity) the original values are preserved.

Finally, the new alpha mask is added back into the original watermark.

e

If, instead, the emboss option was specified, and therefore the composition method is Modulate,5 the job becomes a lot easier. The original watermark is embossed with the Shade() filter, which ensures that areas with a uniform color become a medium gray.

This embossed watermark is then used to modulate the pixels in the original image.

Wherever the watermark has a neutral gray, the original pixels remain the same. The more the colors of the pixels in the watermark deviate from middle gray, the more the colors of the original image will be influenced. Figure 8.3 shows what can be done with this option, and what the intermediate embossed watermark looks like.

Now that we have created the watermark image, and prepared all the options that are needed to call Composite(), we combine the images, and save the result to disk:

$rc = $image->Composite(

$rc = $image->Write($out_file);

die $rc if $rc;

e

Create an embossed watermark

4 In versions of ImageMagick before 5.2.4, the Blend operation for the Compose() method could be used to merge images. However, this has been broken since 5.2.4 and removed from 5.2.9. The meth-od here is more elaborate, but probably works in more versions.

5 See the description on 144. This composition operator, as well as the tile option, requires Image::Magick 5.2.3 or later.

f

Combine the image with the watermark

g

Output the result to disk

f

The Composite() method is called on the original image, with the modified water-mark image as a composite. All the other options have been set earlier in the program.

g

Because we use the Write() method from Image::Magick, it is possible to specify any image format and file name that the module can understand. You can use the special file name '-' to output the result to STDOUT, or just specify a file name with a known extension to save the output in that format.

The result of this program without the emboss option can be seen in figure 8.2, next to an image produced with the earlier presented GD program. As you can see, the output is fairly similar. The output of this program with the emboss option can be seen in figure 8.3.

Occasionally, putting a logo in the corner of an image is not enough to assert owner-ship, and you want to ensure that the image is useless when copied, but that the gen-eral idea of the picture is still visible. For this purpose, you can tile a logo all over the image, with a very low opacity.

Hiding information in an image

If altering the visual appearance of an image is not an option, an invisible watermark can be added with a technique called steganography. Steganography is the general tech-nique (some people would say art) of hiding information in other data in such a way that a third party cannot, or is very unlikely to, detect this information. Apart from all the illicit applications that come to mind, it can also be used to hide digital watermarks in graphics. PerlMagick conveniently provides a very simple means to do this:

$image = Image::Magick->new();

$image->Read('leopard.tif');

$watermark = Image::Magick->new();

$watermark->Read('logo.png');

$image->SteganoImage(image => $watermark, offset => 14);

$image->Write('png:leopard-st.png');

Figure 8.3

Watermarks added to an image by modulating it with an embossed logo (far left), using Image:- :Magick. The image in the middle was produced with the opacity set to 60 per-cent and the gravity to NorthWest, and the one on the right was produced with an opacity of 25 per-cent and the tile option.

Read in the image in an Image::Magick object (in real code you would check for errors), read in the watermark image, and call the Stegano() method. Then write the image to disk in a suitable nonlossy format.

Later, if you want to check whether an image that is yours contains the watermark, you need to know the original size of the watermark, and the offset you used.6

$hidden = Image::Magick->new(size => "64x64+14");

$hidden->Read('stegano:leopard-st.png');

$hidden->Write('png:logo-st.png');

An example of an image treated this way can be seen in figure 8.4.

The image itself looks untouched, but it does contain the watermark. Note that this method of steganography does not actually store all the information of the original watermark in the image, but a simplified grayscale version of it.

Unfortunately, Image::Magick’s steganographic method is not especially robust. It will withstand some blurring and such of the image, but cropping or conversion to a lossy image format such as jpeg or gif might make the watermark irretrievable, which may not be a problem with the way this is implemented in Image::Magick, but more a deficiency in the steganography method itself. To protect your images from thievery, you would be better off using other means, such as good copyright statements and legal advice. Steganography is too easy to beat.

8.3

SUMMARY

In this chapter we have seen the most commonly used ways in which images are transformed, from adding a watermark to resizing images. With these methods as a base you should be able to achieve most effects you want. In chapter 12 we’ll be looking at more customized ways to manipulate images.

6 Reading of stegano images was broken in versions 5.2.7 and 5.2.9 of ImageMagick. If you want to run this example, either use 5.2.6 or before, or 5.3.0 and later.

Figure 8.4

This picture contains a steganography water-mark, which is invisible. The little frog on the top right is the original image hidden in the photo, and the little frog on the bottom right is the extracted image.

One area of image manipulation that was not described, but that might be of inter-est, is the application of special effects. Of course, many special effects can be achieved through convolution filters, and this is often the way they are implemented in image manipulation software (see also chapter 12). Appendix A contains a list of all the methods available through Image::Magick, including the special effect filters that it provides. It also includes example figures illustrating each effect. If you need to apply a blurring or solarizing filter, I suggest you take a look at that appendix.

C H A P T E R 9

Three-dimensional

In document Graphics Programming With Perl (Page 167-175)