Image metadata and applied 2D charting#

Two pragmatic corners of 2D work round out the toolkit arc. First, reading the data already inside image files: dimensions, format, and the EXIF block a camera writes. Second, the most common reason people draw at all, which is turning numbers into a chart. Neither is glamorous, and both come up constantly.

Reading metadata: Image::Info#

Before you draw on or resize an image, you often just need to know about it: how big is it, what format, what did the camera record. Image::Info is pure Perl, reads the headers without decoding the whole image, and returns a plain hash reference.

use Image::Info qw(image_info dim);

my $info = image_info('photo.jpg');
die $info->{error} if $info->{error};

my ($w, $h) = dim($info);
print "format: $info->{file_media_type}\n";     # image/jpeg
print "size:   ${w}x${h}\n";
print "created: $info->{DateTime}\n" if $info->{DateTime};

dim is a convenience that pulls width and height out of the hash. The rest of the keys depend on the format and on what the file carries: resolution, color type, and the common EXIF fields for photographs. For a quick ”what is this file“ it is all you need, and it costs almost nothing because it never decodes the pixels.

Reading and writing metadata: Image::ExifTool#

When you need the full metadata surface, and especially when you need to write it, Image::ExifTool is the definitive tool. It handles the metadata of essentially every image and media format, reads thousands of tags, and can edit them in place without touching the image data.

use Image::ExifTool qw(:Public);

my $et = Image::ExifTool->new;
$et->ExtractInfo('photo.jpg');

my $model = $et->GetValue('Model');             # camera model
my $when  = $et->GetValue('DateTimeOriginal');
print "shot on $model at $when\n" if $model;

# rewrite a tag and save
$et->SetNewValue('Artist' => 'Jane Doe');
$et->WriteInfo('photo.jpg');                     # edits metadata only

Image::Info answers ”how big, what format“ cheaply; Image::ExifTool is the one to reach for when the metadata itself is the job (stripping location tags for privacy, batch-tagging a photo library, reading maker-note fields). Both leave the pixels alone.

Charting: the honest state of the art#

Charting is applied 2D drawing: axes, bars, lines, and labels composed from the primitives you already know. The Perl charting landscape is thinner than it once was. Several once-popular modules are now unmaintained, so this section leads with what still works and names the rest plainly.

GD::Graph for quick raster charts#

GD::Graph builds on GD and is still maintained. It is the shortest path to a bar, line, or pie chart as a PNG. You give it data as a set of parallel arrays (the first is the x labels, the rest are data series) and it renders.

use GD::Graph::bars;

my @data = (
    ['Jan', 'Feb', 'Mar', 'Apr'],       # x-axis labels
    [  12,    19,    8,     23    ],     # one data series
);

my $chart = GD::Graph::bars->new(400, 300);
$chart->set(
    title       => 'Widgets shipped',
    y_max_value => 25,
    dclrs       => ['blue'],
) or die $chart->error;

my $gd = $chart->plot(\@data) or die $chart->error;

open my $fh, '>', 'chart.png' or die $!;
binmode $fh;
print $fh $gd->png;                      # plot() returns a GD::Image
close $fh;

GD::Graph::bars, ::lines, ::pie, and ::points are the workhorses. plot returns a GD::Image, so everything from the GD chapter about output applies. This is the pragmatic default for a chart on disk.

SVG for vector charts#

When the chart must stay sharp at any size, or live in a web page as markup, build it as SVG directly. There is no chart abstraction to learn: you compute the bar rectangles and axis lines yourself and emit them. That is more code than GD::Graph, but it is resolution-independent and needs no compiled dependency.

use SVG;

my @values = (12, 19, 8, 23);
my $svg = SVG->new(width => 400, height => 300);
my $base = 260;                          # baseline y
my $bw   = 60;                           # bar width

for my $i (0 .. $#values) {
    my $h = $values[$i] * 8;             # scale to pixels
    my $x = 40 + $i * ($bw + 20);
    $svg->rect(x => $x, y => $base - $h, width => $bw, height => $h,
               style => { fill => 'steelblue' });
}
$svg->line(x1 => 30, y1 => $base, x2 => 380, y2 => $base,
           style => { stroke => 'black' });

print $svg->xmlify;

For anything beyond a simple bar or line chart the hand-rolled SVG grows quickly, but for exactly the common cases it is clean and dependency-free.

The legacy options, named#

Two charting modules you will find referenced online are effectively unmaintained and are noted here only so their state is not a surprise. Chart::Clicker produces attractive Cairo-rendered charts but has not seen a release in years and carries a large dependency tree (including Moose); if you already run it, it works, but it is not a choice for new code. Chart::Gnuplot drives an external gnuplot binary and is likewise dormant; if you have gnuplot installed and want its plotting power, calling it directly (or through a thin wrapper of your own) is more maintainable than depending on the abandoned module. For scientific plotting specifically, the PDL ecosystem’s own graphics modules are the better-supported path and belong with PDL rather than here.

Where this leaves the toolkit arc#

You can now create, load, draw on, filter, save, vectorize, inspect, and chart 2D images entirely on disk. The one thing missing is a screen: getting a drawing into a live, interactive window. That is the next chapter, and it is also the bridge to the two capstone projects, which both need a window to run in.