Putting an oldtime tint in your digital images
Pixel Patina
With the GIMP image editing program, and a little help from Perl, you can enhance your digital photos and transform a modern image into a nostalgic turn-of-the-century shot.
If you look at 100-year-old black-and-white images, you can't help noticing the sepia tint. The black areas in particular are likely to have yellowed significantly over the years, whereas the lighter hues will only have a slight tint. On my last trip to my home country, Germany, I had the idea of converting a couple of shots I had taken there with my digital camera (Figure 1) into black-and-white and aging them artificially (Figure 2), just to illustrate how much more colorful I perceive the scenery in my current residence, San Francisco.
According to Wikipedia [3], the sepia tint of old photographs comes from a pigment that was used in photography as of the late 19th century. It was taken from a cuttlefish that is indigenous to the English Channel and that has the official Latin name Sepia officinalis. To achieve the same effect with digital images, the artist has to tint the darker parts of the image yellowish brown (Figure 3). The colors at the middle of the spectrum are not greatly affected by this, and the light parts not at all.
It is not sufficient to simply remove the color information from the image and dye it yellow uniformly – to be convincing, more subtle measures are needed. Digital photo specialist Eric Jeschke published a number of GIMP operations [4], which, if applied in sequence, produce convincingly original "pre-war" pictures. The CPAN Gimp Perl module lets you combine the individual steps as a Perl script, which you can then run against a number of photos.
Getting There
The sepiafy script (Listing 1) expects an image file at the command line, performs a number of transformations on it, and then outputs an artificially aged black-and-white image with a yellow tint. For example, the sepiafy image.jpg command results in a file called image-sepia.jpg, which can then be viewed with a tool like eog (Eye of Gnome).
As explained in one of my earlier columns [5], GIMP scripts need to cut through a fair amount of red tape before they can actually talk to GIMP. The register()function called in line 21 defines a name, a menu entry, and a number of other mandatory values that are fairly useless in the command-line version, such as the name of the author and help text. Line 34 finally enters GIMP by calling the event loop main(). Once there, thanks to the entry in line 31, GIMP then calls sepiafy() defined in lines 37ff.
Listing 1
sepiafy
Wipe Those Colors
The gimp_file_load() function in line 41loads image files in any format supported by GIMP and converts them into GIMP's native, internal format. If the load procedure fails because the specified file does not exist or is unreadable, gimp_file_load() returns undef and the test condition in line 45 terminates the program with an error message.
To manipulate an image in GIMP, you first need to specify the so-called "drawable" abstraction of the image the operation should relate to. In this case, it is the active (and only) layer in the GIMP image. The image_get_active_layer() function returns this layer if you pass it the $img reference returned by the previous call to the imager loader.
The desaturate_full() method (line 53) removes all the color information from the drawable layer. The parameter 2 specifies the DESATURATE-AVERAGE algorithm for this. Alternatively, you could have used DESATURATE-LIGHTNESS (0) and DESATURATE-LUMINOSITY (1), which convert colors to grayscales on the basis of their lightness or luminosity values, respectively, and return slightly different results.
Beyond Grayscales
The result is a grayscale image (Figure 4) that still lacks the brightness-based sepia tint. To add the tint, the layer_copy() function (line 57) creates a copy of the original layer ($sepia_layer) in the image. An empty layer would have been sufficient for the purposes here, but by copying the existing layer, the height and width are automatically correct. The parameter of 1 tells the new layer to create an alpha channel, which you will need later to create a so-called layer mask.
Line 60 calls layer_set_mode() with the parameter COLOR_MODE to define how the new layer will overlay the original to create an overall image. In this way, their colors mix evenly whereas other modes wipe out their peer layers, or "burn" or "dodge" them. A final call to image_add_layer() (line 65) drops the newly created, but unassigned, layer at the top of the layer dialog describing the image being edited. A parameter of -1 tells it to take the uppermost position.
But why do you need the new $sepia_layer? The script fills it with the RGB color (162,138,101), the yellowish-brown sepia hue, and then applies it to the original image layer, adding a layer mask that is used to apply the color on the basis of the brightness of the original pixels. The drawable_fill() method in line 70 takes care of filling the layer with GIMP's foreground color, set via gimp_context_set_foreground() in line 68. A value of 0 passed to drawable_fill() instructs GIMP to use the FOREGROUND-FILL mode.
If my aim was simply to spread the sepia tint evenly over the original layer, the script could collapse the two layers right now and finish up because COLOR_MODE in line 61 would gently merge the color information (Figure 5). As a matter of fact, this is similar to how GIMP's preset sepia tint function works, but I like the results of Jeschke's process much better.
Because sepiafy needs to apply more tint to the darker parts of the image than to the lighter parts, I will use a layer mask. The mask specifies how the sepia-tinted layer interacts with the original layer of the image. Line 76 creates the mask. The parameter 0 stands for ADD-WHITE-MASK, which is a mask comprising white-only pixels. The subsequent call to layer_add_mask() (line 79) adds the newly created mask to the $sepia_mask layer.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Juno Computers Launches Another Linux Laptop
If you're looking for a powerhouse laptop that runs Ubuntu, the Juno Computers Neptune 17 v6 should be on your radar.
-
ZorinOS 17.1 Released, Includes Improved Windows App Support
If you need or desire to run Windows applications on Linux, there's one distribution intent on making that easier for you and its new release further improves that feature.
-
Linux Market Share Surpasses 4% for the First Time
Look out Windows and macOS, Linux is on the rise and has even topped ChromeOS to become the fourth most widely used OS around the globe.
-
KDE’s Plasma 6 Officially Available
KDE’s Plasma 6.0 "Megarelease" has happened, and it's brimming with new features, polish, and performance.
-
Latest Version of Tails Unleashed
Tails 6.0 is based on Debian 12 and includes GNOME 43.
-
KDE Announces New Slimbook V with Plenty of Power and KDE’s Plasma 6
If you're a fan of KDE Plasma, you'll be thrilled to hear they've announced a new Slimbook with an AMD CPU and the latest version of KDE Plasma desktop.
-
Monthly Sponsorship Includes Early Access to elementary OS 8
If you want to get a glimpse of what's in the pipeline for elementary OS 8, just set up a monthly sponsorship to help fund its continued existence.
-
DebConf24 to be Held in South Korea
Busan will be the location of the latest DebConf running July 28 through August 4
-
Fedora Unleashes Atomic Desktops
Fedora has combined its solid distribution with rpm-ostree system to make it possible to deliver a new family of Fedora spins, called Fedora Atomic Desktops.
-
Bootloader Vulnerability Affects Nearly All Linux Distributions
The developers of shim have released a version to fix numerous security flaws, including one that could enable remote control execution of malicious code under certain circumstances.