Skip to content
Joshua Z. Zhang edited this page Nov 13, 2015 · 2 revisions

Motivation

In some situations, we just need very basic operations to modify an image. In such case, libraries such as OpenCV are too heavy.

Zupply came up with a very light-weight solution to it, providing IO(read/write) to main-stream image formats, pixel access, cropping, resize fuctions to help you out in case you need it.

Let's make it clear first, if you have to do a lot image processing related stuff, use OpenCV or similar libraries instead. This Image class is designed for light-weight use, consistent to the goal of Zupply project.

Examples

#include "zupply.hpp"
using namespace zz;

void test_image()
{
    Image img("../data/demo.png"); // read image from disk
    if (!img.empty())
    {
        Image img2 = img; // Image class use copy-on-write, see details later
        // crop image
        img.crop(Point(10, 10), Point(img.cols() - 10, img.rows() - 10));
        img.resize(1.5); // resize 1.5x, keep aspect ratio
        img.resize(800, 600); // resize to 800x600, regardless of aspect ratio
        img(100, 100, 0) = 128; // access pixel and change the value
        img.save("demo_out.jpg", 95); // with JPG, you can set quality from 0-100
        img2.save("demo_original.jpg"); // img2 is not affected
    }
}

ImageHdr is quite similar to Image class(both derived from same templated container), the only difference is ImageHdr uses single float to store data, providing finer precision over 8-bit unsigned char Image.

//ImageHdr provide conversion to/from Image
Image img("test.jpg");
ImageHdr imghdr(img, 1.0); // use 1.0 as range in this case, all value range from [0, 1.0]
Image img2 = imghdr.to_normal(1.0);
// ImagHdr can read any image directly
imghdr.load("test.jpg");
// and HDR image
imghdr.load("hdr_image.hdr");
// save to hdr
imghdr.save("hdr_out.hdr");

Full documentation

Design notes

Image storage

  • Channel order: RGB(A) by default
  • Row major order

The channels are interleaved 1-D array in memory, i.e., the data in memory should look like this: r(0, 0)g(0, 0)b(0, 0)r(0, 1)g(0, 1)b(0, 1)...

Copy-on-write

To avoid expensive copy operation, and to avoid error-prone explicit use of shallow and deep copy(why .clone() is not a perfect solution), Image class use copy-on-write strategy. That is, when copy instances, only shape info are copied, the underlying data is shared by all instances.

Image img1, img2, img3;
img1.load("xxx.jpg");
img3 = img2 = img1; // they share the same data buffer

When you modify one of the instances, it will detach from the shared resource buffer, to avoid affecting the other instances.

img1.resize(0.5); // img1 now use new data buffer, img2 and img3 not affected.
img2(10, 20, 0) = 100; // img2 changed, img3 not affected.

For all mutable operations, Image class will handle the process properly.

Performance tip

To avoid allocating new storage, read operation is suggested to use immutable operations, i.e.

// use .at() to read
unsigned char value = img.at(row, col, channel); 
// instead of mutable operation
unsigned char value = img(row, col, channel)