Qt 5 and OpenCV 4 Computer Vision Projects
上QQ阅读APP看书,第一时间看更新

Blurring images

Finally, we have the OpenCV library installed and configured. Now, let's try to use it to blur the image in the slot that's connected to our blur action.

First, we will add the following lines to the beginning of the mainwindow.cpp file so that we can include the OpenCV header file:

     #include "opencv2/opencv.hpp"

The preparation is now done, so let's focus on the implementation of the slot method. Like any slot that intends to operate on a single open image, before doing anything, we need to check whether there is an image open at the current time:

         if (currentImage == nullptr) {
QMessageBox::information(this, "Information", "No image to edit.");
return;
}

As you can see, we prompt a message box and return immediately from the function if there is no image open.

After we make sure that there is an image open at the current time in our application, we know that we can obtain the open image as an instance of QPixmap. But how can we blur the image, which is in the form of a QPixmap using OpenCV? The answer is, we can't. Before we do any manipulation on an image with OpenCV, we must have the image in the form of how OpenCV holds an image, which is typical as an instance of the Mat class. The Mat class in OpenCV means matrix—any image is, in fact, a matrix with a given width, height, number of channels, and depth. In Qt, we have a similar class called QImage, which is used to hold the matrix data of an image. This means that we have an idea of how to blur QPixmap using OpenCV—we need to convert the QPixmap into a QImage, construct a Mat using QImage, blur the Mat, then convert the Mat back to QImage and QPixmap, respectively.

We have to do quite a bit of work in terms of conversion. Let's discuss this by going through the following lines of code:

         QPixmap pixmap = currentImage->pixmap();
QImage image = pixmap.toImage();

This snippet is quite straightforward. We obtain the data of the current image as an instance of QPixmap, and then convert it into a QImage instance by calling its toImage method.

The next step is to convert QImage into Mat, but things get a little complex here. The image we are opening may be in any formatit might be a monochrome image, a grayscale image, or a color image with different depths. To blur it, we must know its format, so we convert it into a normal format with an 8-bit depth and three channels, despite its original format. This is represented by QImage::Format_RGB888 in Qt, and CV_8UC3 in OpenCV. Let's now see how we can do the conversion and construct the Mat object:

         image = image.convertToFormat(QImage::Format_RGB888);
cv::Mat mat = cv::Mat(
image.height(),
image.width(),
CV_8UC3,
image.bits(),
image.bytesPerLine());

Finally, it's a scrutable piece of code. Now that we have the Mat object, let's blur it:

         cv::Mat tmp;
cv::blur(mat, tmp, cv::Size(8, 8));
mat = tmp;

The blur function is provided by OpenCV in its imgproc module. It blurs an image using the normalized box filter with a kernel. The first argument is the image we want to blur, while the second argument is where we want to place the blurred image. We use a temporary matrix to store the blurred image and assign it back to the original one after the blurring finishes. The third argument is the size of the kernel. Here, the kernel is used to tell OpenCV how to change the value of any given pixel by combining it with different amounts of neighboring pixels.

Now that we have the blurred image as an instance of Mat, we have to convert it back into an instance of QPixmap and show it on our scene and view:

         QImage image_blurred(
mat.data,
mat.cols,
mat.rows,
mat.step,
QImage::Format_RGB888);
pixmap = QPixmap::fromImage(image_blurred);
imageScene->clear();
imageView->resetMatrix();
currentImage = imageScene->addPixmap(pixmap);
imageScene->update();
imageView->setSceneRect(pixmap.rect());

The new part of the preceding code for us is constructing the QImage object, image_blurred, from the mat object, and converting the QImage object into a QPixmap using the QPixmap::fromImage static method. Although this is new, it's clear enough. The rest of this code isn't new to us— it's the same code we use in the showImage method of the MainWindow class.

Now that we've displayed the blurred image, we can update the message on the status bar to tell users that this image they are viewing is an edited one, and not the original image they've opened:

         QString status = QString("(editted image), %1x%2")
.arg(pixmap.width()).arg(pixmap.height());
mainStatusLabel->setText(status);

At this point, we've finished the MainWindow::blurImage method. Let's rebuild our project by issuing the qmake -makefile and make commands in our Terminal, and then run the new executable.

If you installed OpenCV in a path that isn't /usr or /usr/local, like me, you may encounter a problem while running the executable:

     $ ./ImageEditor
./ImageEditor: error while loading shared libraries: libopencv_core.so.4.0: cannot open shared object file: No such file or directory

This is because our OpenCV libraries are not in the library search path of the system. We can add its path to the library search path by setting the LD_LIBRARY_PATH environment variable on Linux, and DYLD_LIBRARY_PATH on macOS:

     $ export LD_LIBRARY_PATH=/home/kdr2/programs/opencv/lib/
$ ./ImageEditor

When opening an image with our app, we get the following output:

After clicking the Blur button on the toolbar, it appears as follows:

We can see that our image has been successfully blurred.