datahacker.rs@gmail.com

OpenCV #003 Pixel Intensity and Watermarks

OpenCV #003 Pixel Intensity and Watermarks

Digital Image Processing using OpenCV (Python & C++)

Highlight: In this blog post, we will explain how to scale the pixel’s intensity and make the image brighter and/or darker. We will show how to add logos onto a given image. This is known as a watermark, and we will also show how to add it in a transparent mode. Finally, we show-off with some cool effects for beginners, such as fading in and out using OpenCV.

1. Increasing the intensity values of all pixels

After we load the image, we can multiply its pixel’s values with a scalar. Note that multiplying the image with a constant value greater than 1 produces a brighter image. In our example case, we multiplied the intensity values by 1.03, thereby making the photo brighter.

If you use a higher constant value, you might notice some washed-out effects in certain areas. This happened when some value been multiplied by the constant reached values above 255. That is, OpenCV truncates these values to 255 since an image is represented as an 8-bit binary digit.

Let’s have a look at the following example. We are making a loop of 50 iterations, where we gradually brighten the image. Pretty nice, huh? 🙂

C++

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main() {

cv::Mat img;

// using the following argument we will load a preprocessed image
// in this case RGB image will be converted into a gray scale image

// changing some grayscale pixel values
for (int k = 0; k<50; k++) {

for(int j=0;j<img.rows;j++){

for (int i=0;i<img.cols;i++){

// prevents that multiplication with 1.03, creates a number larger than 255
if (img.at<uchar>(j,i)* 1.03 < 255){

img.at<uchar>(j,i) = (uchar)img.at<uchar>(j,i) * 1.03; //white

}
}
}
cv::imshow("Updated",img);
cv::waitKey(40);
}
cv::destroyAllWindows();
return 0;
}

2. Decreasing the intensity values of all pixels

To decrease the pixels intensity level, or to make the image darker, we will multiply pixel values with a constant value less than 1. For instance, we can use a value of 0.97. If we repeat this multiplication in a loop, for lets says 50 times, we can get interesting visual effects. See below! 🙂 Decreasing the pixel values in a given image

C++

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main() {

cv::Mat img;

// using the following argument we will load a preprocessed image
// in this case RGB image will be converted into a gray scale image

// changing some grayscale pixel values
for (int k = 0; k<50; k++) {

for(int j=0;j<img.rows;j++){

for (int i=0;i<img.cols;i++){

img.at<uchar>(j,i) = (uchar)img.at<uchar>(j,i) * 0.97 ; //darker
}

}

cv::imshow("Updated",img);
cv::waitKey(40);
}
cv::destroyAllWindows();
return 0;
}

3. Creating a watermark

Now let’s say, that you want to stamp your photographs with your brand logo, name, or another identifying mark, you can add what is called a watermark.

How is this done ?

After selecting an image to be processed, a logo image can be stamped onto it.

C++

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
Mat image = imread("car.jpg",1);
Mat logo = imread("datahacker.png",1);

// initialization
char file_name;

// checking shapes
cout << "Width : " << image.cols << endl;
cout << "Height : " << image.rows << endl;

cout << "Width : " << logo.cols << endl;
cout << "Height : " << logo.rows << endl;

// our starting coordinates
int x = image.cols-logo.cols;
int y = image.rows-logo.rows;

// our image region of interest
Mat imageROI = image(cv::Rect(x,y,logo.cols,logo.rows));

// add the logo onto the image

cv::imshow("output",image);
cv::waitKey(0); // wait for a key pressed
cv::destroyAllWindows();

// save image
imwrite(file_name, image);

cv::destroyAllWindows();
}

4. Creating a transparent watermark

Before we proceed further, let’s clarify what the mask is. In simple words, a mask selects a region of an image that we would like to process, whereas the other parts of the image we wish to remain unchanged. To accomplish this we usually create the image of the same size as our image of interest. We may place 1s to select our region of interest, whereas 0s will be placed on the remaining pixel locations.

Even further, it is interesting to note that .png image type has four channels. The fourth channel is the alpha value, and it may have values from 0 to 1.  In images with an alpha channel, each pixel has not only a color value, but also a numerical transparency. This can assist us to create very interesting effects.

Finally, a transparent watermark can be created without the need for the alpha channel value (c++). In this case, we have only used two binary values for defining a mask. We have added a black text, whereas the white values of the logo image did not affect the original image.

C++

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main() {

// file name
char file_name;

// read the image and logo
cv::Mat image=  cv::imread("car.jpg", 1);
cv::Mat logo=  cv::imread("datahacker.png", 1);

// our starting coordinates
int x = image.cols-logo.cols;
int y = image.rows-logo.rows;

// define image ROI at image bottom-right
Mat imageROI= image(cv::Rect(x,y, logo.cols, logo.rows));

// here we inverted the color (so all black are now white and all white are now black)
cv::Mat invSrc =  cv::Scalar::all(255) - logo;

// use the logo as a mask (must be gray-level)

// insert by copying only at locations of non-zero mask

cv::imshow("Image", image); // show the image
cv::namedWindow("Image");
cv::waitKey(0); // wait for a key pressed
cv::destroyAllWindows();

// save image
imwrite(file_name, image);

return 0;
}

5. Fading in and out effect on an image

This is one of our favourites. Manipulating the pixel values in each color channel $$(R, G, B)$$ produces quite interesting results, like the image below. A fade-in/fade-out effect in color is created by accessing every pixel in the channel and multiplying it with a constant value. In this example, 1.02/1.03 values were used.

C++

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;
int main() {

Mat original;

// a loop that iteratively increases/decreases certain color channels
// we create a fade-in/fade-out effect in color.
for (int k = 0; k<50; k++) {

for (int r=0; r<original.rows; r++){

for (int c=0; c< original.cols; c++){

// have a look how we are going to process a color image.
// we will use Vec3b
// in this case we can treat a single position as a vector of 3 bytes (b)
// try for yourself how changing some values produce different examples

original.at<Vec3b>(r, c) = original.at<Vec3b>(r, c)  * 0.f;
original.at<Vec3b>(r, c) = original.at<Vec3b>(r, c)  * 1.03f;
original.at<Vec3b>(r, c) = original.at<Vec3b>(r, c)  / 1.02f;
}
}

cv::imshow("Updated",original);
cv::waitKey(40);
}

return 0;

}

Summary

In this blog post, we learned how to scale the intensity of pixel values, creating a watermark by adding a transparent logo, fade in and out effects on a photograph. If you like this post, please make sure to share this post with your mates getting started on digital image processing. In the next post, we will talk more about some common type of noise we can have on an image.

More resources on the topic:

For more resources about image processing, check these other sites. 