OpenCV: Basic Image Processing Functions Reference: http...
Transcript of OpenCV: Basic Image Processing Functions Reference: http...
OpenCV: Basic Image Processing Functions
Reference: http://www.opencv.org
By: Dr. Rajeev Srivastava, IIT(BHU), Varanasi
Highlights:
1. Functions for Reading, Writing, and Displaying Images in OpenCV
2. Functions for Discrete Fourier Transform for Images in OpenCV
3. Functions for Image Enhancement in OpenCV
4. Restoration of images: Filtering Theory
5. Edge Detection: Canny Edge detector
Experiment 1: To write and execute program for Load, Modify and Save an image
1. Reading an image from a file:
IplImage* img=0;
img=cvLoadImage(fileName);
if(!img) printf("Could not load image file: %s\n",fileName);
Supported image formats: BMP, DIB, JPEG, JPG, JPE, PNG, PBM, PGM, PPM,SR,
RAS, TIFF, TIF
By default, the loaded image is forced to be a 3-channel color image. This default can be
modified by using:
img=cvLoadImage(fileName,flag);
flag: >0 the loaded image is forced to be a 3-channel color image
=0 the loaded image is forced to be a 1 channel grayscale image
<0 the loaded image is loaded as is (with number of channels in the file).
2. Writing an image to a file:
if(!cvSaveImage(outFileName,img)) printf("Could not save: %s\n",outFileName);
The output file format is determined based on the file name extension.
Sample Code:
#include <cv.h>
#include <highgui.h>
using namespace cv;
int main( int argc, char** argv )
{
char* imageName = argv[1];
Mat image;
image = imread( imageName, 1 );
if( argc != 2 || !image.data )
{
printf( " No image data \n " );
return -1;
}
Mat gray_image;
cvtColor( image, gray_image, CV_BGR2GRAY );
imwrite( "../../images/Gray_Image.jpg", gray_image );
namedWindow( imageName, CV_WINDOW_AUTOSIZE );
namedWindow( "Gray image", CV_WINDOW_AUTOSIZE );
imshow( imageName, image );
imshow( "Gray image", gray_image );
waitKey(0);
return 0;
}
Output:
Fig 1: Display and Write Image
Experiment 2: To write and execute program for Discrete Fourier Transform of image
i. Expand the image to an optimal size
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero pixels
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT,
Scalar::all(0));
ii. Make place for both the complex and the real values.
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
iii. Make the Discrete Fourier Transform.
dft(complexI, complexI);
iv. Transform the real and complex values to magnitude.
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
v. Switch to a logarithmic scale.
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
vi. Crop and rearrange.
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
vii. Normalize.
normalize(magI, magI, 0, 1, CV_MINMAX);
Sample Code:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
int main(int argc, char ** argv)
{
const char* filename = argc >=2 ? argv[1] : "atom.jpg";
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if( I.empty())
return -1;
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
normalize(magI, magI, 0, 1, CV_MINMAX);
imshow("Input Image" , I ); // Show the result
imshow("spectrum magnitude", magI);
waitKey();
return 0;
}
Output:
Figure 2: Discrete Fourier Transform
Experiment 3: To write and execute program for Changing the contrast and brightness of
an image
i. We begin by creating parameters to save \alpha and \beta to be entered by the
user:
double alpha;
int beta;
ii. We load an image using imread and save it in a Mat object:
Mat image = imread( argv[1] );
iii. Now, since we will make some transformations to this image, we need a new Mat
object to store it. Also, we want this to have the following features:
Initial pixel values equal to zero
Same size and type as the original image
Mat new_image = Mat::zeros( image.size(), image.type() );
We observe that Mat::zeros returns a Matlab-style zero initializer based on image.size() and
image.type()
iv. Now, to perform the operation g(i,j) = \alpha \cdot f(i,j) + \beta we will access to
each pixel in image. Since we are operating with RGB images, we will have three
values per pixel (R, G and B), so we will also access them separately. Here is the
piece of code:
for( int y = 0; y < image.rows; y++ )
{ for( int x = 0; x < image.cols; x++ )
{ for( int c = 0; c < 3; c++ )
{ new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta ); }
}
}
v. Finally, we create windows and show the images, the usual way.
namedWindow("Original Image", 1);
namedWindow("New Image", 1);
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey(0);
Sample Code:
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace cv;
double alpha; /**< Simple contrast control */
int beta; /**< Simple brightness control */
int main( int argc, char** argv )
{
/// Read image given by user
Mat image = imread( argv[1] );
Mat new_image = Mat::zeros( image.size(), image.type() );
/// Initialize values
std::cout<<" Basic Linear Transforms "<<std::endl;
std::cout<<"-------------------------"<<std::endl;
std::cout<<"* Enter the alpha value [1.0-3.0]: ";std::cin>>alpha;
std::cout<<"* Enter the beta value [0-100]: "; std::cin>>beta;
/// Do the operation new_image(i,j) = alpha*image(i,j) + beta
for( int y = 0; y < image.rows; y++ )
{ for( int x = 0; x < image.cols; x++ )
{ for( int c = 0; c < 3; c++ )
{
new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
}
}
}
/// Create Windows
namedWindow("Original Image", 1);
namedWindow("New Image", 1);
/// Show stuff
imshow("Original Image", image);
imshow("New Image", new_image);
/// Wait until user press some key
waitKey();
return 0;
}
Output:
Running our code and using and
Figure 3: Changing the contrast and brightness of an image
Experiment 4: To write and execute program for Filtering images using following method
Eroding
Dilating
Smoothing
Inverting
Adjusting Brightness
Adjusting Contrast
Eroding
cvErode(img, img, 0, 2)
The 1st parameter is the source image.
The 2nd parameter is the destination image which is to be the eroded image.
Here the 3rd parameter is the structuring element used for erosion. If it is 0, a 3×3 rectangular
structuring element is used.
The 4th parameter is the number of times, erosion is applied.
Sample Program:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//erode and display the eroded image
cvErode(img, img, 0, 2);
cvNamedWindow("Eroded");
cvShowImage("Eroded", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Eroded");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 4: Original image (left), Image after applying erosion (right)
Dilating
cvDilate(img, img, 0, 2)
The 1st parameter is the source image.
The 2nd parameter is the destination image which is to be the dilated image.
Here the 3rd parameter is the structuring element used for dilation. If it is 0, a 3×3 rectangular structuring
element is used.
The 4th parameter is the number of times, dilation is applied.
Sample Code:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//dilate and display the dilated image
cvDilate(img, img, 0, 2);
cvNamedWindow("Dilated");
cvShowImage("Dilated", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Dilated");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 5: Original image (left), Image after applying dilation (right)
Smoothing
cvSmooth(img, img, CV_GAUSSIAN,3,3)
The 1st parameter is the source image.
The 2nd parameter is the destination image.
The 3rd parameter is the method, used for smoothing the image. There are several methods that you can
use for this parameter.
CV_BLUR_NO_SCALE
CV_BLUR
CV_GAUSSIAN
V_MEDIAN
CV_BILATERAL
Every of these methods has some features and restrictions listed below.
CV_BLUR_NO_SCALE supports for single channel images only.
CV_BLUR and CV_GAUSSIAN support for 1 or 3 channel, 8 and 32 bit floating point images. These
two methods can process images in place.
CV_MEDIAN and CV_BILATERAL support for 1 or 3 channel, 8 bit images and cannot process
images in place.
The 4th parameter is the aperture width. (It is a technical term. It defines how much you are going to
smooth the image). You have to use positive odd number (1,3,5,7,...)
The 5th parameter is the aperture height. (It is a technical term. It defines how much you are going to
smooth the image).
Sample Program:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//erode and display the smoothed image
cvSmooth(img, img, CV_GAUSSIAN,3,3);
cvNamedWindow("Smoothed");
cvShowImage("Smoothed", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Smoothed");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 6: Original image (left), Image after applying smmothing (right)
Inverting
cvNot(img, img)
This function inverts every bit in every element of the image in the 1st parameter and places the result in
the image in the 2nd parameter.
This function can process images in place. That means same variable can be used for the 1st and 2nd
parameters.
Sample Code:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//invert and display the inverted image
cvNot(img, img);
cvNamedWindow("Inverted");
cvShowImage("Inverted", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Inverted");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 7: Original image (left), Image after applying inversion (right)
Adjusting Brightness
cvAddS(img, cvScalar(70,70,70), img)
1st parameter is the source image.
3rd parameter is the destination image.
2nd parameter is the value; we are going to add to each and every pixel value of the source image. Here
I have usedcvScalar(70,70,70) because the source image consists of 3 channels. (Color images always
consist of 3 channels to represent red, green and blue). If the source image is a gray scale image which
consists of 1 channel, you may usecvScalar(70) as the 2nd parameter. If you want to decrease the
brightness, you may use cvScalar(-50,-50,-50) as the 2nd parameter.
This function can process images in place. That means same variable can be used for the 1st and 2nd
parameters.
Sample Program
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//increase the brightness and display the brightened image
cvAddS(img, cvScalar(70,70,70), img);
cvNamedWindow("Brightened");
cvShowImage("Brightened", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Brightened");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 8: Original image (left), Image after adjusting brightness (right)
Adjusting Contrast
cvScale(img, img, 2)
1st parameter is the source image.
2nd parameter is the destination image.
3rd parameter is the constant by which each and every pixel values are multiplied. If you use value 1,
there is no change. If you use a value more than 1, destination image will have a high contrast than the
source image. If you use a value less than 1, destination image will have a low contrast than the source
image.
Sample Code:
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
int main()
{
//display the original image
IplImage* img = cvLoadImage("C:/MyPic.jpg");
cvNamedWindow("MyWindow");
cvShowImage("MyWindow", img);
//increase the contrast and display the image
cvScale(img, img, 2);
cvNamedWindow("Contrast");
cvShowImage("Contrast", img);
cvWaitKey(0);
//cleaning up
cvDestroyWindow("MyWindow");
cvDestroyWindow("Contrast");
cvReleaseImage(&img);
return 0;
}
Output:
Figure 9: Original image (left), Image after adjusting contrast (right)
Experiment 5: To write and execute program for Canny edge detection of an image
i. Loads the source image:
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
ii. Create a matrix of the same type and size of src (to be dst)
dst.create( src.size(), src.type() );
iii. Convert the image to grayscale (using the function cvtColor)
cvtColor( src, src_gray, CV_BGR2GRAY );
iv. Create a window to display the results
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
v. Create a Trackbar for the user to enter the lower threshold for our Canny detector:
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold,
CannyThreshold );
vi. Observe the following:
The variable to be controlled by the Trackbar is lowThreshold with a limit of max_lowThreshold
(which we set to 100 previously)
Each time the Trackbar registers an action, the callback function CannyThreshold will be
invoked.
vii. Now, check the CannyThreshold function, step by step:
a. First, we blur the image with a filter of kernel size 3:
blur( src_gray, detected_edges, Size(3,3) );
b. Second, we apply the OpenCV function Canny:
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
where the arguments are:
detected_edges: Source image, grayscale
detected_edges: Output of the detector (can be the same as the input)
lowThreshold: The value entered by the user moving the Trackbar
highThreshold: Set in the program as three times the lower threshold (following Canny’s
recommendation)
kernel_size: We defined it to be 3 (the size of the Sobel kernel to be used internally)
viii. Fill a dst image with zeros (meaning the image is completely black).
dst = Scalar::all(0);
Finally, we will use the function copyTo to map only the areas of the image that are identified as
edges (on a black background).
src.copyTo( dst, detected_edges);
copyTo copy the src image onto dst. However, it will only copy the pixels in the locations where
they have non-zero values. Since the output of the Canny detector is the edge contours on a black
background, the resulting dst will be black in all the area but the detected edges.
ix. Display Result:
imshow( window_name, dst );
Sample Code:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// Global variables
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
char* window_name = "Edge Map";
void CannyThreshold(int, void*)
{
/// Reduce noise with a kernel 3x3
blur( src_gray, detected_edges, Size(3,3) );
/// Canny detector
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
/// Using Canny's output as a mask, we display our result
dst = Scalar::all(0);
src.copyTo( dst, detected_edges);
imshow( window_name, dst );
}
int main( int argc, char** argv )
{
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// Create a matrix of the same type and size as src (for dst)
dst.create( src.size(), src.type() );
/// Convert the image to grayscale
cvtColor( src, src_gray, CV_BGR2GRAY );
/// Create a window
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// Create a Trackbar for user to enter threshold
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold,
CannyThreshold );
/// Show the image
CannyThreshold(0, 0);
/// Wait until user exit program by pressing a key
waitKey(0);
return 0;
}
Output:
Figure 10: Canny edge detection: Original image (left), Detected edges (right)