Handle image
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.
#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");
- 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)...
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.
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)