Raw conversion

G'MIC is an image processing framework, and as such it can be used for image processing of many different kinds of images. Not all kinds of image processing concerns photographic images, but this tutorial will deal with photographic images. Some examples will use a special type of photographic images, images in raw image format, as they can be captured in modern digital SLR cameras. Such images always requires special processing before they can be displayed on the screen, and much of this processing can be performed by G'MIC. Usually this processing is delegated to a category of programs called raw converters, and in this tutorial a command line raw converter called dcraw will be used.

This tutorial assumes that commands are given on a Unix shell command line. G'MIC works also with Windows, but the interaction with the shell might work a little different in that case. The tutorial assumes version 1.3.3.3 of G'MIC.

G'MIC is a project and the command line program is called gmic. It is a kind of command parser, which reads images and applies image commands to them. The easiest way to use it is simply applying it to an image:

gmic image.jpg

This will display the file image.jpg given as argument, using a simple built-in image viewer. This viewer is good for many purposes because it shows the image as-is without interpolation - it is showing the actual pixels as you zoom in rather than smooth them out. The strength of gmic lies deeper though. For example, imagine you want to shrink the above image:

gmic image.jpg -resize 400,400 -output resized-image-1.jpg

Here gmic will take image.jpg and resize it to a square 400 pixels wide. resize is a gmic command, and the numbers following it, 400,400 are its arguments. Commands are recognisable because they start with a dash. So, the resize command will resize the image image.jpg to a square 400 pixels wide, distorting it if not already squared. The new image will then be used by a new command, called output. The output command will write the image to a new file, called resized-image-1.jpg:

Image resized without preserving aspect ratio

If you want to keep the image proportions, it is easier to use another variant:

gmic image.jpg -resize2dx 400,5 -o resized-image-2.jpg

The resize2dx command will resize the image in the x (width) dimension, while keeping the aspect ratio. The argument after the comma, 5 is tells the resize2dx command to use an interpolation method called bi-cubic interpolation - in simple words, the interpolation method is a good one.

Image resized preserving aspect ratio

G'MIC can open and save many image formats. The list is long: JPG, PNG, GIF and TIFF are only some of the most common. While gmic also can open raw images by itself - by internally using dcraw - I prefer to let dcraw decode them first because I want some extra control. Especially, I want to be able to apply the camera white balance which is encoded in the raw file. This way the image will color-wise be similar to what the camera suggested. dcraw has an argument that will apply the white balance the camera selected, and that is probably the best default for most situation. To pass the image between dcraw and gmic, it is possible to use shell redirection, and now it gets a little more complicated:

dcraw -c -w -4 image.cr2 | gmic -.ppm

Here, dcraw decodes the raw file image.cr2, which is a Canon raw file. It applies camera white balance (the -w argument) and writes a 16 bit, linear image (the -4 argument) to standard out (the -c argument). This output, which takes the form of a stream in the PPM image format, is piped to gmic by the shell. Dash means read from standard input, and -.ppm means read a PPM image stream from standard input. The incoming image stream will be treated by gmic just like as if it was read from file: since no other commands are given, the result will be shown in its image viewer.

Linear image from dcraw

Here we notice a few things though. The image is considerably darker than it should be. That is because the image decoded by dcraw is in linear format - a format that closely corresponds to the amount of light recorded by the camera sensor. To get the contrast of the image more pleasant for the eyes we need to apply gamma to it, to convert the image data to exponential form. So we can evolve the command line as follows:

dcraw -c -w -4 image.cr2 | gmic -.ppm -apply_gamma 2.2

Image with gamma applied

A gamma value of 2.2 is standard for many purposes, and now it looks more natural. However, the colors and contrast still leaves something to be desired. Now it would be very easy to get lost among the options offered by gmic. They are plentyful and advanced but if you are not an imaging researcher it is not so easy to understand what they do or how to put them together. But there are ways to find out..

G'MIC provides a plugin to the Gimp imaging program. In this plugin you can do many kinds of image operations using regular slide bars and clicking buttons, which is intuitive and familiar. If the Gimp is started from the command line, and you choose the "Output messages" option "Verbose", G'MIC will print the actual gmic operations to the console, so you can see what it does beneath the shiny GUI. And then it is a simple matter to copy and paste the image operations you have found that you prefer and apply them to your gmic command line. So, after some experimentation I found that I wanted to extend my command line as follows:

dcraw -c -w -4 image.cr2 | gmic -.ppm -div 256 \
-gimp_channel_processing 2.2,1,0,0,2,12,88,256,0,0 \
-gimp_mix_lab 1,0,0,1.3,0,0,1.3,0,0,0,2 \
-mul 256

Image with additional processing

Suddenly the picture is quite a bit more pleasant for the eyes! How did this work? Well a couple of things are new here. First of all, there are two new commands applied, gimp_channel_processing and gimp_mix_lab. gimp_channel_processing actually does several things. It applies gamma, and it normalizes values in the picture, so the darkest pixels becomes absolutely black, and the lightest pixels absolutely white. The apply_gamma command is therefore no longer necessary. It can do other things as well, but the above arguments worked well in this case, and were just copied them from the Gimp console. The gimp_mix_lab command provides what is often named "color boost", by converting the image to the Lab color space, and push the colors in direction of more red and blue without affecting the light component. This often improves the appearance of a color image, if done with some care.

Around these two Gimp commands (which are actually only called Gimp commands by name, they are G'MIC commands that are called by the G'MIC Gimp plugin) there are two mysterious commands -div 256 and -mul 256. These are cryptic but have a reason - the commands in between acts on colors in RGB colorspace, or performs conversions between RGB colorspace and Lab colorspace. Such commands expect the color values to be in an interval between 0 and 255, which is a familiar scale for anyone that worked with color in imaging applications. However, the color data that comes from dcraw is 16-bit (since we gave the -4 option above) and takes values between 0 and 65536. Therefore we need to scale down these values to a range where the commands make sense. We do that by dividing the color data with 256 since 65536 / 256 = 256.

This may seem like information is thrown away, but in fact it is not since G'MIC works with floating point values internally. So a pixel that had one color value of 1000 before now gets a value of approximately 3.92. This does not matter so much, all values are affected relative each other and in the end we multiply the data with 255 again to get back in the scale we began - in a 16 bit image scale.

Now the only thing that is left is to write our new gamma adjusted, contrast enhanced, color boosted image to disk:

dcraw -c -w -4 image.cr2 | gmic -.ppm -div 256 \
-gimp_channel_processing 2.2,1,0,0,2,12,88,256,0,0 \
-gimp_mix_lab 1,0,0,1.3,0,0,1.3,0,0,0,2 \
-mul 256 -c 0,65536 -output image.tiff,ushort

More mysterious commands. First of all, we notice that in the end we write the image data to a TIFF file. TIFF files are good for high-precision image processing because they can contain 16 bit image data. However TIFF files also can contain this data in many forms that other programs can't read. We want the data to be in 16 bit integer format, so we need to assert the data in that format prior to writing the file. That is what -c 0,65535 is about. Now you may understand why gmic is a tool developed at a university research department!

However, the end result is a high quality image that could very well be better than what the camera would have produced by itself. We can compare this. My Canon camera embeds a jpg image in its raw files and this jpg image can be retrieved from the raw file using dcraw:

dcraw -e image.cr2

Thumbnail stored in RAW

This image has quite high resolution although not as high as if I the camera was configured to to shoot directly in JPG using the highest possible resolution. But it is good enough for comparison. Let's compare this with a JPG created from the raw file itself, using a slightly adjusted command.

dcraw -c -w -4 image.cr2 | gmic -.ppm -resize2dx 1936,5 \
-div 256 \
-gimp_channel_processing 2.2,1,0,0,2,12,88,256,0,0 \
-gimp_mix_lab 1,0,0,1.3,0,0,1.3,0,0,0,2 \
-c 0,255 -type uchar -output comparisonimage.jpg

Changes to the previous command line is a rescaling to the same resolution as the embedded JPG, and some trickery to write a 8 bit JPG file rather than 16 bit TIFF. As you may notice, we don't multiply with 255 again - it is unnecessary since we are already in the 8 bit range. And we perform a slightly different assertion to prepare the data for writing a JPG file.

Crop from the thumbnail to the left, and the G'MIC-processed on the right:

Comparison

Defining custom commands

However, that command line is a little longer than I prefer to type each time I want to develop a picture. So let's see how this can be simplified! There are powerful ways to do this since G'MIC actually is a kind of command macro system. Commands are built from other commands. Below is a simple command definition:

#@gmic resize_thebigpicture : : Resize the image 990 px wide, the same width as in Boston Globe's "The Big Picture"
resize_thebigpicture :
-e "Resize image$?"
-v - -repeat $!
-resize2dx 990,5
-mv[-1] 0 -done -v +

Let's go through this code and see what it does. First we have a comment line, it starts with a hash (#) but with a twist: this is an "intelligent comment" that gmic uses when it provides online help. It is a good idea to begin each command definition with a comment that begins #@gmic. Following this comes the command name, the arguments it uses, and a descriptive sentence about what it does. These different parts are separated by colons - in this case the command does not take any arguments.

Next follows the actual command definition. This command is called resize_thebigpicture. It performs a resizing operation, with a predefined width - the same width as is used for images in the excellent The Big Picture blog. If you want your pictures to look as good as these, it is a good idea to resize them to this size!

The command definition starts with the command name, followed by a colon. Leave this on its own line, and on the following lines add the actual commands that should be performed by this command.

The gut of this command is the resize operation, -resize2dx 990,5 which resizes, keeping image proportions, to the width 990 pixels using bi-cubic interpolation. Surrounding this command is something I would call recommended boilerplate code. The -e "Resize image$?" line will print a helpful comment as the command executes. The dollarsign-questionmark $? part of this comment will be substituted with a note about what image this is applied to as it executes.

-v - will decrease the comment verbosity one notch for the following commands. This makes G'MIC print less details as it executes, to make the output of command invocation more concise. As you can see, the command definition ends with -v + which increases the comment verbosity again, so the change actually only applies within this command. This will make it possible to get more or less information from gmic as it executes.

Between these two comment modifiers is a form of loop. But to understand this loop it is important to understand that gmic works not only with one image at a time, but with any number of images. Think of gmic as applying commands to a number of images, just like cars running through an assembly line. But instead of cars there are images, and instead of robots doing welding and painting there are image commands. Also, in this assembly line, each robot will finish all its cars before the next robot starts.

To get a command to work on many images, we should write it to work with a loop. It begins with -repeat $! and ends with -done. This will repeat as many times as there are images waiting for the command. $! is shorthand for the number of arguments, and this is the number of repetitions that will be made. After each resize2dx command has been performed in this loop, the images waiting for the command will be rotated. -mv[-1] 0 will take care of this rotation. This is the move command. In this case the move command will move the last image in the list to be the first. Images are addressed like this: [0] is the first image in the list of images, 1 is the following and so on. [-1] is the last image, and [-2] is the one before that. Every command can act on any image, you only need to tell it which image it should be applied to.

This system may seem complex but it is part of what makes G'MIC powerful. Think of these lists as layers in Photoshop and you will understand what kind of operations can be performed this way!

To make a long story short, a custom command definition may look complex, but it is quite simple, if you consider that you can use copy and paste. And there is an excellent source to copy from - in fact a very big part of G'MIC is actually defined as commands just like this. gmic_stdlib.gmic contains approximately 160 commands defined in just in this way, and by studying a command that does approximately what you want to accomplish, you get lots of help to accomplish your goal.

Put your command in a separate text file called "custom.gmic" and give it to gmic as argument. Then call your new command

gmic -m custom.gmic image.jpg -resize_thebigpicture -output resized.jpg

and you will have a newly resized, stunning image to submit to The Boston Globe. But why not apply it to two images at once?

gmic -m custom.gmic image1.jpg image2.jpg -resize_thebigpicture -output[0] resized1.jpg -output[1] resized2.jpg

And let's get some help for our new command:

gmic -m custom.gmic -help resize_thebigpicture


gmic : GREYC's Magic Image Converter (Oct 21 2009, 02:06:35)


Version 1.3.2.8, Copyright (C) 2008-2009, David Tschumperle (http://gmic.eu)


-resize_thebigpicture
Resize the image 990 px wide, the same width as in Boston Globe's "The Big Picture"

With this introduction to command macros, it is time to apply it to our raw converter toolchain:

#@gmic resize_thebigpicture : : Resize the image 990 px wide, the same width as in Boston Globe's "The Big Picture"
resize_thebigpicture :
-e "Resize image$?"
-v - -repeat $!
-resize2dx 990,5
-mv[-1] 0 -done -v +

#@gmic linear16develop : '"outfile"' : Develop a linear 16 bit image and write to 16 bit file.
linear16develop :
-e "Develop image$?."
-v - -repeat $!
-linear16boost -write16 $1
-done -v +

#@gmic linear16develop_preview : '"outfile"' : Develop a linear 16 bit
#image, downsize it to preview size, and write to 8 bit file.
linear16develop_preview :
-e "Develop image$?."
-v - -repeat $!
-linear16boost -resize_thebigpicture -convert16to8 -write8 $1
-done -v +

#@gmic linear16boost : : Operations on linear 16 bit image: gamma, contrast, color boost.
linear16boost :
-e "Gamma, contrast, color boost on image$?."
-v - -type float -repeat $!
# RGB operations requires values in 8-bit range, so downsize
# (will still be floating point so high precision)
-div 256
-gimp_channel_processing 2.2,1,0,0,2,12,88,256,0,0
-gimp_mix_lab 1,0,0,1.3,0,0,1.3,0,0,0,2
-mul 256
-mv[-1] 0 -done -v +

#@gmic convert16to8 : : Convert image values retrieved in 16 bit to fit in 8 bit.
convert16to8 :
-e "Converting to 8 bit for image$?."
-v - -repeat $!
-div 256
-mv[-1] 0 -done -v +

#@gmic write16 : '"outfile"' : Write 16 bit image to file.
write16 :
-e "Writing image$?."
-v - -repeat $!
-c 0,65536 -type ushort -o $1
-mv[-1] 0 -done -v +

#@gmic write8 : '"outfile"' : Write 8 bit image to file.
write8 :
-e "Writing image$?."
-v - -repeat $!
-c 0,255 -type uchar -o $1
-mv[-1] 0 -done -v +

Here we have broken up the steps performed earlier in a number of commands and combined them so we both can apply them individually, using simpler names, do all steps at once, or combine them with other G'MIC commands:

dcraw -c -w -4 image.cr2 | gmic -m custom.gmic -.ppm \
-linear16develop_preview image_preview.jpg

dcraw -c -w -4 image.cr2 | gmic -m custom.gmic -.ppm \
-linear16develop image.tiff

dcraw -c -w -4 image.cr2 | gmic -m custom.gmic -.ppm \
-linear16boost -sharpen 50 -smooth 25 -write16 image2.tiff

All these commands were defined using the same pattern: a comment, the command name followed by its containing commands, in order: an output message, a logging adjustment, a loop, the actual operation of the command, rotation the image list, ending the loop and re-adjusting the logging level. The only thing that is new here are commands that take arguments, such as the filename in the image write commands. Arguments should be declared in the comment, and to access the argument, you use $1, $2, $3 etc for each argument in order. Much of this will feel familiar if you have developed shell scripts before.

Histograms

Trial and error, the classic method of learning, is applicable also to G'MIC. When using it as a command line program, it is a little more work than with a regular GUI program, since you need to read the documentation to find out what things can be experimented with. And after having tried a command it is also useful to figure out what it did. Here image histograms can help.

G'MIC can generate histograms, but they are not immediately useful for our purposes. The histogram command will generate an image that is 1 pixel high, and of custom width. In this image the brightness of each individual pixel corresponds to how common pixels of a certain brightness were in the original image. Compare with the histogram bars in regular histogram. It is as if looking at the histogram from above, where higher bars gets lighter. However, when viewed in a image viewer, this is hard to comprehend - it will look like a greyscale fading between white black and grey in a pattern depending on the source image.

To visualize this better, the histogram image can be given as argument to a plot command. The plot command will generate a graphical plot of a mathematical expression. The expression language is quite capable but in this case we only need to use a very simple function.

 gmic image.jpg -histogram 256 -plot 3

Here, the histogram command will create a histogram image, 1x256 pixels in size, with pixel values depending on the relative frequency in the source image (image.jpg). 256 basically means that we want to see 256 levels, which is the highest that makes sense for a JPG image since it is a 8-bit format.

Next we plot the data, using a plot of type "histogram" (type 3). There are other types, such as points or splines.

The result is a nice, interactive histogram, showing how brightness is distributed in the image.

Native histogram

There is another way to generate histograms that combines the different color channels using a kind of trick. G'MIC uses GraphicsMagick to read and write image formats. GraphicsMagick in turn supports ImageMagick metaformats, including a special format that represents an image histogram.

These details do not matter that much though. To generate a histogram for an image, you do

   gmic image.jpg -o histogram:name-of-file-for-histogram.miff

This will generate a file called name-of-file-for-histogram.miff, which is a file of the less well known MIFF format, an ImageMagick invention. Viewing the histogram is easy:

  gmic name-of-file-for-histogram.miff

Contrast-less image, histogram

The MIFF file will be quite big - it will contain the image in a form that takes lots of space to store, and the actual histogram will be a small addition in that big file. So if you want to keep it, it makes sense to convert it to a more well known and space-efficent format:

 gmic name-of-file-for-histogram.miff -o histogram.png

With this knowledge we can explore some fundamental G'MIC commands.

Normalization

Normalization is a process that adjusts the pixel values in an image so that it better utilizes the available values. For example in a JPG image, the red, green and blue colors can be represented with values between 0 and 255. Looking at the image histogram, this means shifting the bars along the x-axis. The goal is usually to use as much as possible of the available pixel values for the "important" pixels in the image.

This is easier to explain with an example. The example image we will use is the one we are already familiar with - an image that has been processed with dcraw and had gamma applied, but not been further processed. In this form, it lacks contrast, and looks pretty dull.

Contrast-less image

If we look at its histogram we can see that there are unused values both in the upper and lower part of the histogram:

Contrast-less image, histogram

When we normalize this image we need to tell G'MIC by how much. This can be done in different ways, for example using percentages:

 gmic normalizationexample.jpg -n 15%,85% -o normalizationexample-normalize15%-85%.jpg

-n is short for -normalize. This will adjust the darker parts of the histogram 15% "upwards", towards brigher values, while pulling the higher, brigher values "downwards", making them darker. The resulting image is not improved, on the contrary:

Even less contrast

and it is evident from the histogram that we moved the values in the wrong direction!

Even less contrast, histogram

So let's move the values in the opposite direction:

 gmic normalizationexample.jpg -n -15%,115% -o normalizationexample-normalize-15%-115%.jpg

The results is improved contrast but with... hmm let's say surprising if not horrible effects - the coal started glowing and set the boat on fire!

More contrast

Looking at the histogram, we can see what has happened. It looks like the darker values got pushed out from the lower (left) end of the histogram, and appeared at the higher end. See the spike in the far right. These pixels got maximized for some colors but not for others, depending on the original value. The result is colored and unwanted artifacts in the image.

More contrast, histogram

We need to prevent this from happening and we can accomplish this by cutting off pixels prior to normalizing them. So, for this we use the cut command, shortened -c:

gmic normalizationexample.jpg -c 15%,85% -o normalizationexample-cut15-85.jpg

This will "cut off" the lowest 15% of the histogram and the highest 15% as well. Afterwards, the histogram will be empty in these regions. However, cutting the histogram does not mean that the pixels somehow disappeared. Note that in the lower (left) end of the histogram, a high spike represents all those cut off pixels that were not allowed to be that dark. They got a higher brightness since they obviously need to have a value, they got the darkest allowed one. On the right side, the same thing happened but it is hard to see in the histogram. Blame all the dark coal for that - there is so much of it that the relative height of the histogram had to change to fit the leftmost bar, representing it all.

Cut pixels

Cut pixels, histogram

Studying the image and comparing it with the original we can easily see that with our own eyes. The shades, such as the darkest parts of the coal, or the darkness within the cabin has been replaced with a dull grey, and we have still not achieved an improved image.

But now it is turn to combine the normalization and cut commands, in order:

gmic normalizationexample.jpg -c 15%,85% -n -15%,115% -o normalizationexample-cut15-85-normalize-15%-115%.jpg

So, we first cut the pixel values, and normalize the image to "stretch back" the histogram with approximately the same amount. Finally we have an image with enhanced contrast without appearant artifacts:

Contrast stretched image

Contrast stretched image, histogram

Now, to save some future work, we can implement this operation as a G'MIC custom command. This way of increasing contrast is often called contrast_stretch:

   #@gmic contrast_stretch : cut_low[%], cut_high[%], normalize_low[%], normalize_high[%] : Stretch contrast of image.
   contrast_stretch :
      -e "Stretching contrast of image$?."
      -v - -repeat $!
      -c  $1,$2 -n $3,$4
      -mv[-1] 0 -done -v +

Put this in a command file and apply it applied as

gmic -m /path/to/commandfile image.jpg -contrast_stretch 15%,85%,-15%,115%

to increase the contrast of your images.