![]() |
University of Murcia, Spain ![]() |
Basic image processingIn 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 8 bit-depth image, with 3 color channels:
QVision<uChar, 3> image; In the case of one channel images, the second template parameter for the QVImage class can be omitted. For example, the following two variable declarations are equivalent:
QVision<uChar, 1> imageA; QVision<uChar> imageA; The uChar type is an alias for the unsigned char C++ type. The QVision defines several ad-hoc types for bit depth and precision image types. A comprehensive list can be seen in the next table:
We encourage programmers to use them for image types, instead of the corresponding C++ types. 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. Image initializationThe class QVImage has several different constructors. The first one creates the image by specifying its size in rows and cols. For example:
QVImage<sInt, 3> imageA(320, 240); That line will create a 3-channel image with 32 bit-depth, of size 320x240 pixels. The second is the copy constructor. It initializes the size and content from another image, of the same bit-depth and channel number:
QVImage<sInt, 3> imageB = imageA; The third one is the convert constructor. It is used in the same fashion than the copy constructor, and can be used to initialize the size and the content of another image, with different bit-depth and/or channel number:
QVImage<uChar, 1> imageC = imageB; Also, the size and content of image objects can be initialized loading the content of an image file stored in the file-system, using the image file loader constructor. It only requires the file name as the input parameter:
QVImage<uChar, 3> imageD("lena.png"); That instruction will load the content of the file lena.png on the object imageD, providing the file exists in the datapath and is readable by the application. Copying imagesThe 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. An example usage follows:
QVImage<uChar, 3> imageE("lena.jpg"), imageF; // Construct image objects. QVImage<sInt, 1> imageG; [...] imageF = imageE; imageG = imageF; After the above instructions the object imageF will contain a copy of the contents of the object imageE, and the object imageG will contain a 32 bit-depth version 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.33* imageA(i, j, 0) + 0.33* imageA(i, j, 1) + 0.33* 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) = imageB(i, j) = 0.33* QVIMAGE_PIXEL(imageA, i, j, 0) + 0.33* QVIMAGE_PIXEL(imageA, i, j, 1) + 0.33* QVIMAGE_PIXEL(imageA, i, j, 2); The code becomes somewhat less legible, but time performance is better this way. 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) = imageB(i, j) = 0.33* QVIMAGE_PIXEL(ptrImageA, i, j, 0) + 0.33* QVIMAGE_PIXEL(ptrImageA, i, j, 1) + 0.33* QVIMAGE_PIXEL(ptrImageA, i, j, 2);
|