PARP Research Group | Universidad de Murcia |
The QVImage classIn the QVision framework, images are represented as objects. These objects are created from the QVImage class. They contain the pixel values corresponding to a raster or bitmap image representation. The QVImage class is parametrized with the type of the values contained in each pixel, and the number of channels of the image. For example, the following code will create an unsigned 8 bit-depth image, with 3 channels: #include <QVImage> // Header file required to include the class QVImage [...] QVImage<unsigned char, 3> image; Image typesThe QVision defines several short name types to specify the image bit depth. A comprehensive list can be seen in the next table:
Some examples of QVImage object creation with these defined types follows: // Creates an unsigned 8-bit, 1 channel image, using the C++ type for the 8-bit depth. QVImage<unsigned char, 1> imageA; // Creates another unsigned 8-bit, 1 channel image, using the QVision predefined type for the 8-bit depth. QVImage<uChar, 1> imageB; // Creates a signed 32-bit, 3 channel image, using the C++ type for the 32-bit depth. QVImage<signed int, 3> imageC; // Creates another signed 32-bit, 3 channel image, using the QVision predefined type for the 32-bit depth. QVImage<sInt, 3> imageD; // Creates a single precission floating point, 1 channel image, using the C++ type for the floating point. QVImage<float, 1> imageE; // Creates another single precission floating point, 1 channel image, using the QVision predefined type for the floating point. QVImage<sFloat, 1> imageF; Programmers can use the QVision defined types, as well as the corresponding C++ types. The former are just short terms defined for usability. As for the image channel number, technically you can create image objects with an arbitrarily number of channels. In practice you generally won't find practical functionality applied to images with a channel number other than 1, 3 or 4. Default template parametersThe QVImage template parameters can be omitted. By default, when declaring a QVImage object, the bit-depth is 8-bits, and the channel number is one. For example, the following variable declarations are all equivalent, and declare 8-bit one channel images: // Creates a 8-bit, 1 channel image. QVImage<uChar, 1> imageB; // Creates another 8-bit, 1 channel image, omiting the channel number. QVImage<uChar> imageC; // Creates yet another 8-bit, 1 channel image, omitting both bit depth and channel number. QVImage<> imageD; Image initializationThe class QVImage has several different constructors. The default constructor creates a 1x1 size image: // Creates a 1x1 size, 3 channel image. QVImage<sInt, 3> image; This constructor is useful for completeness. Another constructor creates the image by specifying its size in rows and cols. For example: // Creates a 320x240 size, 3 channel image. QVImage<sInt, 3> imageA(320, 240); That line will create a 3-channel image with 32 bit-depth, of size 320x240 pixels. Also, the size and content of image objects can be initialized from an image file, using the image file loader constructor. It only requires the file name as the input parameter: // 'imageB' will load its contents from the image file 'lena.png'. QVImage<uChar, 3> imageB("lena.png"); That instruction will load the content of the file lena.png on the object imageD, provided that the file exists in the current directory and is readable by the application. The last constructor is the copy constructor. It initializes the size and content of the image from another image, of the same bit-depth and channel number: // Images 'imageC' and 'imageCbis' will contain both copies of 'imageA'. QVImage<sInt, 3> imageC = imageA; QVImage<sInt, 3> imageCbis(imageA); Copying and converting imagesThe copy constructor can also be used to convert an image to a different bit-depth and/or channel number. An example of this usage follows: // The RGB image 'imageB' will be converted to the grayscale images 'imageD' and 'imageDbis'. QVImage<uChar, 1> imageD = imageB; QVImage<uChar, 1> imageDbis(imageB); The copy operator has a similar use to the copy and convert constructors. It can be used to copy the size and content of an image onto an already initialized one: // Construct default image objects. QVImage<uChar, 3> imageE; QVImage<sInt, 1> imageF; [...] // Copy 'imageA' to 'imageE'. imageE = imageA; // Convert unsigned 8-bit three channel image 'imageA', to signed 32-bit one channel image 'imageF'. imageF = imageA; The object imageE will contain a copy of the contents of the object imageA, and the object imageF will contain a 32 bit-depth, one channel conversion of the same contents. Image pixel accessThe QVision provides several ways to access the pixels of an image. The simplest and less efficient of them is using the parenthesis operator. The following code shows how it can be used to retrieve and update the value of the pixels in an image: QVImage<uChar, 3> imageA("lena.png"); QVImage<uChar, 3> imageB(imageA.getCols(), imageA.getRows()); for(int i = 0; i < imageA.getCols(); i++) for(int j = 0; j < imageA.getRows(); j++) { imageB(i, j, 0) = imageA(i, j, 1); imageB(i, j, 1) = imageA(i, j, 0); imageB(i, j, 2) = imageA(i, j, 2); } That code swaps the content of the first and the second channels of the image contained in the file lena.png. The default value for the third parameter of the parenthesis operator is 0. Thus if the image has only one channel, the third parameter of the function call operator can be omitted: QVImage<uChar, 3> imageA("lena.png"); QVImage<uChar, 1> imageB(imageA.getCols(), imageA.getRows()); for(int i = 0; i < imageA.getCols(); i++) for(int j = 0; j < imageA.getRows(); j++) imageB(i, j) = 0.30* imageA(i, j, 0) + 0.59* imageA(i, j, 1) + 0.11* imageA(i, j, 2); This code obtains a gray-scale version of the lena.png image in the object imageB. A more efficient way of accessing the contents of an image is using the following macros: Macros QVIMAGE_INIT_READ and QVIMAGE_INIT_WRITE are used to initialize access to an image in read or read/write mode respectively. Macro QVIMAGE_PIXEL is used to access a pixel in an image, previously initialized with the other four macros. The previous code example can be rewritten using the macros in the following way: QVImage<uChar, 3> imageA("lena.png"); QVImage<uChar, 1> imageB(imageA.getCols(), imageA.getRows()); QVIMAGE_INIT_READ(uChar, imageA); QVIMAGE_INIT_WRITE(uChar, imageB); for(int i = 0; i < imageA.getCols(); i++) for(int j = 0; j < imageA.getRows(); j++) QVIMAGE_PIXEL(imageB, i, j, 0) = 0.30* QVIMAGE_PIXEL(imageA, i, j, 0) + 0.59* QVIMAGE_PIXEL(imageA, i, j, 1) + 0.11* QVIMAGE_PIXEL(imageA, i, j, 2); By using these macros, the code becomes somewhat less legible, but time performance is better. The macros QVIMAGE_INIT_READ, QVIMAGE_INIT_WRITE, QVIMAGE_PTR_INIT_READ and QVIMAGE_PTR_INIT_WRITE must always receive a plain variable identifier as the second parameter, not an expression of type QVImage. Thus an image pointer cannot be used with the QVIMAGE_INIT_READ or QVIMAGE_INIT_WRITE, because that would imply including the dereference operator * in the second parameter. Macros QVIMAGE_PTR_INIT_READ and QVIMAGE_PTR_INIT_WRITE are equivalent to QVIMAGE_INIT_READ and QVIMAGE_INIT_WRITE, but can receive a pointer to an image as the second input parameter. An equivalent code to the previous example, using image pointers and those macros would be the following: // Input and output images QVImage<uChar, 3> imageA("lena.png"); QVImage<uChar, 1> imageB(imageA.getCols(), imageA.getRows()); // Get pointers to images. QVImage<uChar, 3> *ptrImageA = &imageA; QVImage<uChar, 1> *ptrImageB = &imageB; // Read the input image QVIMAGE_PTR_INIT_READ(uChar, ptrImageA); QVIMAGE_PTR_INIT_WRITE(uChar, ptrImageB); for(int i = 0; i < ptrImageA->getCols(); i++) for(int j = 0; j < ptrImageA->getRows(); j++) QVIMAGE_PIXEL(ptrImageB, i, j, 0) = 0.30* QVIMAGE_PIXEL(ptrImageA, i, j, 0) + 0.59* QVIMAGE_PIXEL(ptrImageA, i, j, 1) + 0.11* QVIMAGE_PIXEL(ptrImageA, i, j, 2); |