libgmic is a small, portable, thread-safe, multi-threaded, C++, image processing library that forms the foundation of
the G'MIC framework.
It is used by all available G'MIC interfaces.
It is built upon the CImg Library, a lower-level C++ template image processing library which actually implements
the core algorithms that G'MIC uses (a.k.a the native commands).
Despite its small size (approx. 5 MB ), libgmic provides even more image processing operators
than CImg, as well as a simpler API,
so that anyone can readily integrate all the G'MIC features
directly into their own code.
Here is a quick step-by-step guide to show how such an integration could be achieved.
It assumes you are using Linux, but of course, the steps are very similar for other operating systems.
Step 1: Get the required library files
To be able to use
libgmic in your own code, you need:
-
The header file gmic.h
which contains the declarations of the classes and methods allowing to use the libgmic library.
This file needs to be included by your source code.
-
The dynamic library file libgmic.so (or its static version libgmic.a) that needs to be linked
against your code. These binary files are of course platform-dependent.
It is recommended you compile them directly from the latest source
(following these instructions).
Alternatively, you may have the libgmic files already present on your system (in /usr/lib/),
if you have installed G'MIC from the .deb packages we provide.
Step 2: Write and compile your first code using libgmic
Now, you should be able to write your first
libgmic-based program
foo.cpp:
//----- File 'foo.cpp' -----
#include "gmic.h"
#include <cstdio>
int main() {
try {
gmic("testimage2d 512 output gmic_test.png");
} catch (gmic_exception& e) { // In case something went wrong:
std::fprintf(stderr,"ERROR : %s\n",e.what());
return -1;
}
return 0;
}
You can compile it simply like this (here, with
g++):
$ g++ -o foo foo.cpp -lgmic
Execute it:
$ ./foo
and if all goes well, you should have a new image file
gmic_test.png in your current folder, which looks like this:
Additional information:
- G'MIC is actually more than an image processing library. It defines an entire framework with
its own scripting language.
So, of course, libgmic embeds the corresponding script interpreter in addition to the image processing algorithms themselves.
- Actually, all libgmic entry points invoke the G'MIC interpreter, and
all actions you want libgmic to perform will be described as pieces of small G'MIC scripts.
That is exactly what we&apo;ve done in the file foo.cpp.
- By the way, it is then recommended to have some knowledge about how the G'MIC script language works,
to be able to use libgmic as its full potential. Fortunately, this is a pretty straightforward language,
and you can read very detailed tutorial pages which explain it, so this should not be a big deal.
- In the sample file foo.cpp above, we have just asked G'MIC to generate a new 512x512 color image
from scratch (with command testimage2d 512)
and save it as the image file gmic_test.png in the current folder
(with -output gmic_test.png).
Step 3: Dealing with image buffers
It is quite unlikely you want to manage the input/output of your image data to/from
libgmic using files only.
So here are details on how to pass image data using memory buffers instead. The important things to know are:
- G'MIC does all its processing using float-valued image buffers. So you need to feed libgmic with such buffers when you invoke it.
This may imply converting your current image data into buffers of 32bits float's before any libgmic call.
- G'MIC may take several images as input, and may output several images as well.
So, it primarily works with list of images. All libgmic entry points which take image buffers will actually take a list of images as an argument.
The execution of a G'MIC pipeline through the libgmic API modifies this list of images so that it contains the desired pipeline output.
- libgmic defines two simple classes gmic_image<float> and gmic_list<float>
to create and manage float-valued images and lists of images.
They have basic constructors/destructors for the allocation and deallocation of the corresponding float-valued memory buffers.
So, you'll probably never have to do the memory allocation/deallocation by yourself.
- To summarize, the only thing you need to do is: 1. construct an instance of a gmic_list<float> containing N (initially empty) images,
then 2. assign a size for each input image of the list,
3. fill the input image buffers (i.e. set the float values defining each image pixel),
and finally 4. invoke one of the gmic() constructor (i.e. one of the libgmic entry points) to apply a G'MIC
pipeline on the input images.
- When libgmic has returned, your input image list gmic_list<float> has been modified regarding the invoked processing pipeline.
It may now contain more or fewer images, with different sizes, etc.
G'MIC manages the necessary memory allocation/deallocation automatically to avoid memory leaks.
This second example
foo2.cpp illustrates these points:
//----- File 'foo2.cpp' -----
#include "gmic.h"
#include <cstdio>
#include <cmath>
int main() {
gmic_list<float> image_list;
image_list.assign(2); // Tell 'image_list' it has 2 images.
for (unsigned int i = 0; i<image_list._width; ++i) { // 2 input images.
// Initialize each image of 'image_list' to a 320x200x1x3 (color) image.
gmic_image<float>& img = image_list[i];
img.assign(320,200,1,3);
// Fill image buffer with random sinus waves.
float *ptr = img;
for (unsigned int c = 0; c<img._spectrum; ++c) // 3 channels.
for (unsigned int y = 0; y<img._height; ++y) // 200 rows.
for (unsigned int x = 0; x<img._width; ++x) // 320 columns.
*(ptr++) = (float)(128 + 127*std::cos(x/(5. + 10*i))*std::sin(y/(5. + 10*i + c)));
}
gmic_list<char> image_names; // We don't care about image names here, let this empty.
try {
gmic("water[0] 20 flower[1] 20 blend vividlight",
image_list,image_names);
} catch (gmic_exception &e) { // In case something went wrong.
std::fprintf(stderr,"ERROR : %s\n",e.what());
return -1;
}
// Here, 'image_list' now contains only 1 (color) image of size 320x200x1x3.
// You can retrieve the image data from the buffer at 'image_list[0]._data',
// as well as the image dimensions 'image_list[0]._xxxx', where
// 'xxxx' can be 'width', 'height', 'depth' or 'spectrum'.
(...) // Do whatever you want with the image data here.
image_list.assign(0); // Deallocate images, if you want to do it before the destructor.
return 0;
}
In foo2.cpp, we first define and initialize a list of two input images (with random sinus waves for the colors). They actually look like this:
Then, the G'MIC invocation creates this single output image buffer (but without saving the image into a file in this case):
This is the result of the following G'MIC pipeline:
1. Apply a water effect on the first image (command
water[0] 20),
2. Apply a flower effect on the second image (command
flower[1] 20),
and
3. Blend the resulting images together, using the
Vivid light mode (command
blend vividlight).
Easy, isn't it ?
At this stage, you already know the most complex things about the use of the libgmic API :).
Step 4: Additional information about the libgmic API
In bulk order, some other interesting things to know about the
libgmic API:
- A gmic_image<float> instance as defined in G'MIC has 4 dimensions:
width (number of columns), height (number of rows),
depth (number of slices) and spectrum (number of channels).
There are no hard limits for each image dimension, except the available memory required to store the image buffer.
You can set an arbitrary number of channels for an image (more than 4 for instance), or an arbitrary number of slices (to deal with 3D volumetric images).
A usual RGB color image has three channels (spectrum==3) and a single slice (depth==1).
That's why we define our color images with dimensions '320,200,1,3' in
the file foo2.cpp.
The encoding order of the pixel values in an image buffer is straightforward and follows the natural order of the image dimensions: It first stores the image variations along the
X-axis (columns), then Y (rows), then Z (slices), then finally C (channels). For instance, the memory buffer for a 2x2x1x3 RGB color image
is organized as follows (beware, the channels are not stored in an interleaved way!):
R(0,0),R(1,0),R(0,1),R(1,1),G(0,0),G(1,0),G(0,1),G(1,1),B(0,0),B(1,0),B(0,1),B(1,1)
- The call to gmic(...) is actually a call to a constructor. It constructs one temporary gmic instance
and destroys it just after it has completed its pipeline execution. It is possible to construct a more permanent gmic instance, and use it multiple times if necessary.
For instance, the call to libgmic in file foo2.cpp could have been decomposed like this:
gmic g_instance;
g_instance.run("water[0] 20",image_list,image_names);
g_instance.run("flower[1] 20",image_list,image_names);
g_instance.run("blend vividlight",image_list,image_names);
g_instance.run("output gmic_test.png",image_list,image_names);
- You may want to set names for your input images. For instance, in file foo2.cpp, we could have set the input image names like this:
const char *const name1 = "First image", *const name2 = "Second image";
gmic_list<char> image_names;
image_names.assign(2);
image_names[0].assign(std::strlen(name1)+1); // We don't forget the '\0'.
std::strcpy(image_names[0],name1);
image_names[1].assign(std::strlen(name2)+1); // We don't forget the '\0'.
std::strcpy(image_names[1],name2);
And similarly, you can get the name of the resulting image by:
const char *const output_name = image_names[0];
Setting image names is generally useless, except if one of the commands you invoke in your G'MIC pipeline takes care about it (there are some).
- If by chance, you are already a user of the CImg Library,
you might be happy to learn that the two main libgmic classes
gmic_image<float> and
gmic_list<float> are actually the same as the CImg classes
CImg<float> and
CImgList<float> respectively.
Indeed, the G'MIC project is based on CImg, and it is then straightforward
to use CImg in conjunction with libgmic, and get the power of both libraries at the same time.
The file foo3.cpp below illustrates the mixed use of CImg and libgmic:
//----- File 'foo3.cpp' -----
#include "CImg.h"
#include "gmic.h"
using namespace cimg_library;
int main() {
// Load input image files, using the CImg Library.
CImg<float>
img1("img/lena.jpg"),
img2("img/joconde.jpg");
// Move input images into a list.
CImgList<float> image_list;
img1.move_to(image_list);
img2.move_to(image_list);
// Define the corresponding image names.
CImgList<char> image_names;
CImg<char>::string("First image").move_to(image_names);
CImg<char>::string("Second image").move_to(image_names);
// Invoke libgmic to execute a G'MIC pipeline.
gmic("water[0] 15 wave[1] 15 "
"320,256 mandelbrot. -0.6918,-0.4831,-0.6375,-0.4338,256 "
"64,1,1,3,?(255) r. 256,1,1,3,3 point. 0 map.. . rm. "
"frame 2,2,0 to_rgba drop_shadow 3,3 montage X "
"i[0] 100%,100%,1,3,255 blend alpha "
"name \"Result image\"",
image_list,image_names);
// Display resulting image.
const char *const title_bar = image_names[0];
image_list.display(title_bar);
return 0;
}
Here, we start to load two input images lena.jpg and joconde.jpg in memory,
and after the libgmic invocation, we get this single resulting image:
Implementing the same pipeline entirely with CImg calls only would have involved much efforts for sure!
In the G'MIC source, you will a sample file
use_libgmic.cpp which contains all
the stuffs we've summarized in this page. Check it out and compile it to have another example on how the libgmic can do for you.
G'MIC is an open-source software distributed under the
CeCILL free software licenses (LGPL-like and/or
GPL-compatible).
Copyrights (C) Since July 2008,
David Tschumperlé - GREYC UMR CNRS 6072, Image Team.