Book Image

Learning OpenCV 3 Application Development

By : Samyak Datta
Book Image

Learning OpenCV 3 Application Development

By: Samyak Datta

Overview of this book

Computer vision and machine learning concepts are frequently used in practical computer vision based projects. If you’re a novice, this book provides the steps to build and deploy an end-to-end application in the domain of computer vision using OpenCV/C++. At the outset, we explain how to install OpenCV and demonstrate how to run some simple programs. You will start with images (the building blocks of image processing applications), and see how they are stored and processed by OpenCV. You’ll get comfortable with OpenCV-specific jargon (Mat Point, Scalar, and more), and get to know how to traverse images and perform basic pixel-wise operations. Building upon this, we introduce slightly more advanced image processing concepts such as filtering, thresholding, and edge detection. In the latter parts, the book touches upon more complex and ubiquitous concepts such as face detection (using Haar cascade classifiers), interest point detection algorithms, and feature descriptors. You will now begin to appreciate the true power of the library in how it reduces mathematically non-trivial algorithms to a single line of code! The concluding sections touch upon OpenCV’s Machine Learning module. You will witness not only how OpenCV helps you pre-process and extract features from images that are relevant to the problems you are trying to solve, but also how to use Machine Learning algorithms that work on these features to make intelligent predictions from visual data!
Table of Contents (16 chapters)
Learning OpenCV 3 Application Development
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface

Digging inside Mat objects


We have learnt how to create Mat objects and even populate them with data from an image read from the disk or with arbitrary numerical values. Now it's time to get a little more information regarding the internal workings of the Mat object. This will help you make some important design decisions while writing code for your applications.

As we have discussed earlier, Mat objects are composed of a header and the actual matrix of values with the size of the matrix being (usually) much greater than the size of the header. We have already seen that a modestly sized image with dimensions of 100 pixels by 100 pixels can take up as much as 30 kilobytes of space. Images are known to be much bigger in size than that. Moreover, when you are developing a computer vision-based application, your code is typically working with multiple images or multiple copies of images. These images (and their copies) are passed to-and-fro the various modules of your code. They may be the input to or store the result of some OpenCV function. The more sophisticated a system we are trying to build, the greater the complexity of these interactions.

If that is the case, with Mat being a memory-intensive data structure, how does OpenCV prevent its processes from running out of memory? The answer to the question lies in the manner in which the internal workings of the Mat objects are handled by the library. OpenCV is smart enough to avoid duplication of the Mat object data matrix wherever it possibly can. This is going to be the topic of our discussions in this section.

We have discussed several ways to declare and initialize Mat objects. One more method we will touch upon now is by initializing it with another Mat object (much like what a copy constructor does). So, we can do something like this:

Mat image = imread("lena.jpg"); 
Mat another_image(image); 
Mat yet_another_image = image; 

Now, your intuition might tell you that since there are three Mat objects, the data of the image read from the disk must have been duplicated three times in memory. Had that been the case, and had the original image, lena.jpg, contained a significant number of pixels, it would have meant using up a lot of memory. However, while using the copy constructor for Mat, OpenCV only creates a separate copy of the header and not the data matrix. Same is the case with using the equality operator. So, for all the three Mat objects, the header is different, but the data matrix is shared. The headers for each of the three objects point to the same data matrix in memory. In essence, we have three different aliases providing access to the same underlying data matrix. Modifying any of the three objects will change the same data and affect all three. It is very important to keep this in mind while writing code to avoid unnecessary complications and potential loss of data by overwriting!

Another place where such an issue might crop up is while passing images to functions in your code. Suppose you have a function in your application that looks something like this:

void processImage(Mat image) { 
    // Does some processing on the Mat 
} 

When you invoke the preceding function, the processImage() method works on the same data matrix. Another way to put it is that Mat objects are always passed by reference (the actual data matrix is not copied). Therefore, modifying the image in the called function will modify it in the function from where it was called.

Let's test this using a concrete example that you can execute for your and check. We will start with the inclusion of the relevant header files and namespace declarations:

#include <iostream>  
#include <opencv2/core/core.hpp> 
#include <opencv2/highgui/highgui.hpp> 
 
using namespace std; 
using namespace cv; 

We have an implementation of the processImage() method that turns all the pixels of the input image black:

void processImage(Mat input_image) { 
    int channels = input_image.channels(); 
    int numRows = input_image.rows; 
    int numCols = input_image.cols * channels; 
 
    for (int i = 0; i < numRows; ++i) { 
        uchar* image_row = input_image.ptr<uchar>(i); 
        for (int j = 0; j < numCols; ++j) 
            image_row[j] = 0; 
    } 
} 

Don't worry if you aren't able to understand the meaning of these lines for now. Traversing Mat objects will be covered in the subsequent sections of this chapter. You can copy the code verbatim and it will execute just fine:

int main() { 
    Mat image = imread("lena.png"); 
    processImage(image); 
     
    imshow("Output", image); 
    waitKey(0); 
 
    return 0; 
} 

As you can see here in the main() function, we read an image from the disk (lena.png), loaded the image data into a Mat object named image, and then passed the same object to our processImage() method, which was defined previously. When we attempt to display the same Mat object using imshow(), we see that the image is now completely black (which is what the processImage() method was expected to do!). This means that the processImage() method has worked with the same data matrix as that of the input Mat object.

But what about the cases where you actually do want to copy the data matrix as well? OpenCV provides a couple of alternatives to achieve this. copyTo() and clone() are two methods belonging to the Mat class that allow you to create separate Mat objects by copying the data matrix along with the header. A typical use case for this might be when you want a copy of the original image to be preserved before sending the image through your processing pipeline:

Mat cloned_image = image.clone(); 
Mat another_cloned_image; 
image.copyTo(another_cloned_image); 

Let's test this on our previous example. The processImage() method remains unchanged. We will modify the main() function to look like this:

int main() { 
    Mat image = imread("lena.png"); 
    Mat image_clone = image.clone(); 
    processImage(image); 
     
    imshow("image", image); 
    imshow("image_clone", image_clone); 
    waitKey(0); 
 
    return 0; 
} 

Notice that now, we create a copy of the input Mat object's data matrix by invoking the clone() method. If you run this, the image_clone parameter is Mat object would have remained unchanged, whereas the original data matrix has undergone the modifications of the processImage method.

This finishes our discussion of the Mat object. We have been through all the topics that you might need to begin working with images in your code. In the next section, we take a dive in and start by iterating through these images and playing around with their pixel values. Having learnt about the image, we now move on to some processing.