Introduction to C C Lecture 10 Image Processing

  • Slides: 46
Download presentation
Introduction to C & C++ Lecture 10 – Image Processing with Open. CV JJCAO

Introduction to C & C++ Lecture 10 – Image Processing with Open. CV JJCAO

Open. CV (Open Source Computer Vision) is a library of programming functions for real

Open. CV (Open Source Computer Vision) is a library of programming functions for real time computer vision.

Supported Development Environment • Linux, Windows, Android • GCC, Eclipse, Visual Studio,

Supported Development Environment • Linux, Windows, Android • GCC, Eclipse, Visual Studio,

Install Open. CV 1. Basic Info: http: //opencv. willowgarage. com/wiki/Install. Guide 2. Necessary steps

Install Open. CV 1. Basic Info: http: //opencv. willowgarage. com/wiki/Install. Guide 2. Necessary steps for Qt Creator (Similar with VS): http: //www. laganiere. name/opencv. Cookbook/chap 1. shtml 3. Installation in Windows (too much of details): http: //opencv. itseez. com/doc/tutorials/introduction/windows_install. ht ml#windows-installation

How to build applications with Open. CV inside the Microsoft Visual Studio http: //opencv.

How to build applications with Open. CV inside the Microsoft Visual Studio http: //opencv. itseez. com/doc/tutorials/introduction/windows_v isual_studio_Opencv/windows_visual_studio_Opencv. html#win dows-visual-studio-how-to P 11 of Open. CV. 2. Computer. Vision. Application. Programming. Cookbook _2011

Load & Display an Image 1. Load an image (using imread) 2. Create a

Load & Display an Image 1. Load an image (using imread) 2. Create a named Open. CV window (using named. Window) 3. Display an image in an Open. CV window (using imshow)

1. Load an image (using imread) cv: : Mat image; image = cv: :

1. Load an image (using imread) cv: : Mat image; image = cv: : imread(argv[1], CV_LOAD_IMAGE_COLOR); // Read the file if( img. empty() ) // Check for invalid input { std: : cout << "Could not open or find the image" << std: : endl ; return -1; } Image formats supported: Bmp, jpg, png, tif, ppm, … CV_LOAD_IMAGE_UNCHANGED (<0) loads the image as is (including the alpha channel if present) CV_LOAD_IMAGE_GRAYSCALE (0) loads the image as an intensity one CV_LOAD_IMAGE_COLOR (>0) loads the image in the RGB format

2. Create a named Open. CV window (using named. Window) cv: : named. Window(

2. Create a named Open. CV window (using named. Window) cv: : named. Window( "Display window", CV_WINDOW_NORMAL|CV_WINDOW_FREERATIO ); // Create a window for display. CV_WINDOW_AUTOSIZE is the only supported one if you do not use the Qt backend. In this case the window size will take up the size of the image it shows. No resize permitted! CV_WINDOW_NORMAL on Qt you may use this to allow window resize. The image will resize itself according to the current window size. By using the | operator you also need to specify if you would like the image to keep its aspect ratio (CV_WINDOW_KEEPRATIO) or not (CV_WINDOW_FREERATIO).

3. Display an image in an Open. CV window (using imshow) cv: : imshow(

3. Display an image in an Open. CV window (using imshow) cv: : imshow( "Display window", image ); // Show our image inside it. cv: : wait. Key(0); // Wait for a keystroke in the window • Because we want our window to be displayed until the user presses a key (otherwise the program would end far too quickly), we use the wait. Key function whose only parameter is just how long should it wait for a user input (measured in milliseconds). • Zero means to wait forever.

Necessary Head Files #include <opencv 2/core. hpp> #include <opencv 2/highgui. hpp> using namespace cv;

Necessary Head Files #include <opencv 2/core. hpp> #include <opencv 2/highgui. hpp> using namespace cv; using namespace std; You’ll almost always end up using the: • core section, as here are defined the basic building blocks of the library • highgui module, as this contains the functions for input, output & GUI operations

Necessary libraries #ifdef _DEBUG #pragma comment(lib, "opencv_core 230 d. lib“ ) #pragma comment(lib, "opencv_highgui

Necessary libraries #ifdef _DEBUG #pragma comment(lib, "opencv_core 230 d. lib“ ) #pragma comment(lib, "opencv_highgui 230 d. lib“ ) #else #pragma comment(lib, "opencv_core 230. lib“ ) #pragma comment(lib, "opencv_highgui 230. lib“ ) #endif

Congratulation!

Congratulation!

From C to C++ • Open. CV has been around ever since 2001. In

From C to C++ • Open. CV has been around ever since 2001. In those days the library was built around a C interface. – user is responsible for taking care of memory allocation and de-allocation – Lots of old tutorials written in C • Once your code start to grow larger & larger, more & more a struggle to handle this rather than focusing on solving your goal • Finally C++ – automatic memory management (more or less) – less to write, to achieve more

class CV_EXPORTS Mat { public: //. . . a lot of methods. . .

class CV_EXPORTS Mat { public: //. . . a lot of methods. . . /*! includes several bit-fields: - the magic signature - continuity flag - depth - number of channels */ int flags; int dims; //! the array dimensionality, >= 2 int rows, cols; //! the number of rows and columns or (-1, -1) when the Head array has more than 2 dimensions int* refcount; //! pointer to the reference counter; user-allocated data, the pointer is NULL // other members. . . uchar* data; //! pointer to the data }; when array points to

Create a cv: : Mat A, C; // creates just the header parts A

Create a cv: : Mat A, C; // creates just the header parts A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // the method will allocate matrix (deep copy) Mat B(A); // Use the copy constructor, without copying the data (shadow copy) C = A; // Assignment operator, shadow copy Mat roi(A, Rect(10, 100, 100)); // select a ROI roi = Scalar(0, 255, 0); // fill the ROI with (0, 255, 0) (which is green in RGB space); the original A will be modified; see next page.

cv: : Scalar template <typename _Tp> class Scalar_ : public Vec<_Tp, 4> {. .

cv: : Scalar template <typename _Tp> class Scalar_ : public Vec<_Tp, 4> {. . . }; typedef Scalar_<double> Scalar; Being derived from Vec<_Tp, 4> , Scalar_ and Scalar can be used just as typical 4 -element vectors.

Deep Copy Mat F = A. clone(); Mat G; A. copy. To(G); Now modifying

Deep Copy Mat F = A. clone(); Mat G; A. copy. To(G); Now modifying F or G will not affect the matrix pointed by the Mat header.

What you need to remember 1. Output image allocation for Open. CV functions is

What you need to remember 1. Output image allocation for Open. CV functions is automatic (unless specified otherwise). – Example (next page) 2. No need to think about memory freeing with Open. CVs C++ interface. 3. The assignment operator and the copy constructor (ctor)copies only the header. 4. Use the clone() or the copy. To() function to copy the underlying matrix of an image.

Output image allocation for Open. CV functions is automatic • instead of writing: Mat

Output image allocation for Open. CV functions is automatic • instead of writing: Mat color; . . . Mat gray(color. rows, color. cols, color. depth()); cvt. Color(color, gray, CV_BGR 2 GRAY); • you can simply write: Mat color; . . . Mat gray; cvt. Color(color, gray, CV_BGR 2 GRAY);

How to scan images, lookup table & time measurement • • 1. 2. 3.

How to scan images, lookup table & time measurement • • 1. 2. 3. 4. How to go through each and every pixel of an image? How is Open. CV matrix values stored? How to measure the performance of our algorithm? What are lookup tables and why use them? Basic Mat info Storing methods Data type conversion Accessing Pixel Values

a simple color reduction method how_to_scan_images image. Name. jpg divide. With [G] uchar if(

a simple color reduction method how_to_scan_images image. Name. jpg divide. With [G] uchar if( argc == 4 && !strcmp(argv[3], "G") ) I = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); else I = imread(argv[1], CV_LOAD_IMAGE_COLOR); CV_8 U 3

Storing methods • How to store pixel values? – Color space • Gray-level (Black-and-White)

Storing methods • How to store pixel values? – Color space • Gray-level (Black-and-White) – unsigned 8 -bit values – 0: black, 255: white • RGB – A triplet of unsigned 8 -bit values – [0, 0, 0]: black, [255, 0, 0]: red, [255, 255]: white • HSV, HLS, …, CIE – Data type

bool Mat: : is. Continuous() • Matrices created with Mat: : create() are row

bool Mat: : is. Continuous() • Matrices created with Mat: : create() are row 1 row 2 … always continuous: row n • may no longer continuous – extract a part of the matrix using Mat: : col(), Mat: : diag() , and so on, – or constructed a matrix header for externally allocated data.

Storing methods • How to store pixel values? – Color space • Gray-level (Black-and-White)

Storing methods • How to store pixel values? – Color space • Gray-level (Black-and-White) – unsigned 8 -bit values – 0: black, 255: white • RGB – A triplet of unsigned 8 -bit values – [0, 0, 0]: black, [255, 0, 0]: red, [255, 255]: white • HSV, HLS, …, CIE – Data type CV_8 U - 8 -bit unsigned integers ( 0. . 255 ) CV_8 S - 8 -bit signed integers ( -128. . 127 ) CV_16 U - 16 -bit unsigned integers ( 0. . 65535 ) CV_16 S - 16 -bit signed integers ( -32768. . 32767 ) CV_32 S - 32 -bit signed integers ( -2147483648. . 2147483647 ) CV_32 F - 32 -bit floating-point numbers ( -FLT_MAX. . FLT_MAX, INF, NAN ) CV_64 F - 64 -bit floating-point numbers ( -DBL_MAX. . DBL_MAX, INF, NAN )

Inquire Mat Info 1. int Mat: : depth() const 2. int Mat: : channels()

Inquire Mat Info 1. int Mat: : depth() const 2. int Mat: : channels() const 3. int Mat: : type() // mixture of depth & channels – – – #define CV_8 UC 1 CV_MAKETYPE(CV_8 U, 1) #define CV_8 UC 2 CV_MAKETYPE(CV_8 U, 2) #define CV_8 UC 3 CV_MAKETYPE(CV_8 U, 3) #define CV_8 UC 4 CV_MAKETYPE(CV_8 U, 4) #define CV_8 UC(n) CV_MAKETYPE(CV_8 U, (n)) … 4. size_t Mat: : elem. Size() // matrix element (pixel) size in bytes – if the matrix type is CV_16 SC 3 , the method returns 3*sizeof(short) or 6. 5. size_t Mat: : elem. Size 1() // element size of a channel in bytes – if the matrix type is CV_16 SC 3 , the method returns sizeof(short) or 2. 6. M. step[] 7. size_t Mat: : step 1() // a matrix step divided by Mat: : elem. Size 1()

Accessing Pixel Values 1. The efficient way: c style access p = I. ptr<uchar>(i);

Accessing Pixel Values 1. The efficient way: c style access p = I. ptr<uchar>(i); p[j] 2. The iterator (safe) method 3. On-the-fly address calculation with reference returning I. at<uchar>(i, j) • Performance Difference quite large (2560 X 1600) image

Performance Difference Debug Release

Performance Difference Debug Release

More efficient Algorithm 1. divide and multiplication operations are bloody expensive for a system.

More efficient Algorithm 1. divide and multiplication operations are bloody expensive for a system. 2. cheaper operations such as a few subtractions, addition or in best case a simple assignment 3. limited number of input values, 256 to be exact in this problem

Lookup table int divide. With; // convert our input string to number - C++

Lookup table int divide. With; // convert our input string to number - C++ style stringstream s; s << argv[2]; s >> divide. With; if (!s) { cout << "Invalid number entered for dividing. " << endl; return -1; } uchar table[256]; for (int i = 0; i < 256; ++i) table[i] = divide. With* (i/divide. With);

Mat& Scan. Image. And. Reduce. C(Mat& I, const uchar* const table) { int i,

Mat& Scan. Image. And. Reduce. C(Mat& I, const uchar* const table) { int i, j; uchar* p; for( i = 0; i < n. Rows; ++i) { p = I. ptr<uchar>(i); for ( j = 0; j < n. Cols; ++j) { p[j] = table[p[j]]; } } return I; // accept only char type matrices CV_Assert(I. depth() != sizeof(uchar)); int channels = I. channels(); int n. Rows = I. rows; int n. Cols = I. cols * channels; if (I. is. Continuous()) { n. Cols *= n. Rows; n. Rows = 1; } }

Basic Mat Info 1. 2. 3. 4. bool Mat: : empty() size_t Mat: :

Basic Mat Info 1. 2. 3. 4. bool Mat: : empty() size_t Mat: : total() int Mat: : rows, Mat: : cols Size Mat: : size()

Mat& Scan. Image. And. Reduce. Iterator(Mat& I, const uchar* const table) { // accept

Mat& Scan. Image. And. Reduce. Iterator(Mat& I, const uchar* const table) { // accept only char type matrices CV_Assert(I. depth() != sizeof(uchar)); const int channels = I. channels(); switch(channels) { case 1: { Mat. Iterator_<uchar> it, end; for( it = I. begin<uchar>(), end = I. end<uchar>(); it != end; ++it) *it = table[*it]; break; } case 3: { Mat. Iterator_<Vec 3 b> it, end; for( it = I. begin<Vec 3 b>(), end = I. end<Vec 3 b>(); it != end; ++it) { (*it)[0] = table[(*it)[0]]; (*it)[1] = table[(*it)[1]]; (*it)[2] = table[(*it)[2]]; } } } return I; }

Mat& Scan. Image. And. Reduce. Random. Access(Mat& I, const uchar* const table) { case

Mat& Scan. Image. And. Reduce. Random. Access(Mat& I, const uchar* const table) { case 3: { Mat_<Vec 3 b> _I = I; // accept only char type matrices CV_Assert(I. depth() != sizeof(uchar)); const int channels = I. channels(); switch(channels) { for( int i = 0; i < I. rows; ++i) for( int j = 0; j < I. cols; ++j ) { _I(i, j)[0] = table[_I(i, j)[0]]; _I(i, j)[1] = table[_I(i, j)[1]]; _I(i, j)[2] = table[_I(i, j)[2]]; } I = _I; break; case 1: { for( int i = 0; i < I. rows; ++i) for( int j = 0; j < I. cols; ++j ){ I. at<uchar>(i, j) = table[I. at<uchar>(i, j)]; } break; } } } return I; }

The Core Function: cv: : LUT() LUT: replace all of given image values to

The Core Function: cv: : LUT() LUT: replace all of given image values to some other values Mat look. Up. Table(1, 256, CV_8 U); uchar* p = look. Up. Table. data; for( int i = 0; i < 256; ++i) p[i] = table[i]; for (int i = 0; i < times; ++i) cv: : LUT(I, look. Up. Table, J);

Conclusion 1. If possible, use the already made functions of Open. CV (instead reinventing

Conclusion 1. If possible, use the already made functions of Open. CV (instead reinventing these). 2. The fastest method turns out to be the LUT function. This is because the Open. CV library is multi-thread enabled via Intel Threaded Building Blocks. 3. However, if you need to write a simple image scan prefer the pointer method. The iterator is a safer bet, however quite slower. 4. Using the on-the-fly reference access method for full image scan is the most costly in debug mode. In the release mode it may beat the iterator approach or not, however it surely sacrifices for this the safety trait of iterators.

Type Conversion #include <opencv 2/imgproc. hpp> cvt. Color(I, J, CV_RGB 2 GRAY); for( int

Type Conversion #include <opencv 2/imgproc. hpp> cvt. Color(I, J, CV_RGB 2 GRAY); for( int i = 0; i < 0. 5*I. rows; ++i) for( int j = 0; j < 0. 5*I. cols; ++j ) { //J. at<uchar>(i, j) = 0; J. at<float>(i, j) = 0; } Mat: : convert. To()

void Mat: : convert. To(Output. Array m, int rtype, double alpha=1, double beta=0 )

void Mat: : convert. To(Output. Array m, int rtype, double alpha=1, double beta=0 ) • Converts an array to another datatype with optional scaling. • Parameters: – m – Destination matrix. If it does not have a proper size or type before the operation, it is reallocated. – rtype – Desired destination matrix type or, rather, the depth since the number of channels are the same as the source has. If rtype is negative, the destination matrix will have the same type as the source. – alpha – Optional scale factor. – beta – Optional delta added to the scaled values. • The method converts source pixel values to the target datatype. saturate_cast<> is applied at the end to avoid possible overflows:

One More Example: gradient infile='dataHappy. Fish. jpg'; im = imread(infile); info = imfinfo(infile); if

One More Example: gradient infile='dataHappy. Fish. jpg'; im = imread(infile); info = imfinfo(infile); if ~strcmp(info. Color. Type, 'grayscale' ) im = double(rgb 2 gray(im)) ; end [gx, gy] = gradient(im); figure; colormap(gray); imagesc(im); hold on; [x, y] = meshgrid(1: n, 1: m);

See the cv. Matlab and cv. Matlab. Test example void jj: : gradient(cv: :

See the cv. Matlab and cv. Matlab. Test example void jj: : gradient(cv: : Input. Array _src, cv: : Output. Array _dst, int xorder) { cv: : Mat src=_src. get. Mat(); _dst. create( src. size(), CV_MAKETYPE(src. depth(), src. channels()) ); cv: : Mat dst = _dst. get. Mat(); … }

See the cv. Matlab and cv. Matlab. Test example • Difference in x direction

See the cv. Matlab and cv. Matlab. Test example • Difference in x direction // single channel Mat src, dest; // same size, diff depth for( int i = 0; i < src. rows; ++i) for( int j = 1; j < src. cols-1; ++j ) { dest. at<uchar>(i, j) = (src. at<uchar>(i, j+1) - src. at<uchar>(i, j-1) ) /2. 0; } // 3 channels dst. at<cv: : Vec 3 b>(i, j)[0] = (src. at<cv: : Vec 3 b>(i, j+1)[0] - src. at<cv: : Vec 3 b>(i, j-1)[0] ) /2. 0; dst. col(j)=(src. col(j+1)-src. col(j-1))/2. 0;

Create a Mat object 1 • Mat() Mat M(2, 2, CV_8 UC 3, Scalar(0,

Create a Mat object 1 • Mat() Mat M(2, 2, CV_8 UC 3, Scalar(0, 0, 255)); cout << "M = " << endl << " " << M << endl; • Create a matrix with more than two dimensions int sz[3] = {2, 2, 2}; Mat L(3, sz, CV_8 UC(1), Scalar: : all(0)); // Specify its dimension, then pass a pointer containing the size for each dimension and the rest remains the same. • Create a header for an already existing Ipl. Image pointer Ipl. Image* img = cv. Load. Image("greatwave. png", 1); Mat mtx(img); // convert Ipl. Image* -> Mat

Create a Mat object 2 • Create() function M. create(4, 4, CV_8 UC(2)); cout

Create a Mat object 2 • Create() function M. create(4, 4, CV_8 UC(2)); cout << "M = "<< endl << " " << M << endl; // You cannot initialize the matrix values with this construction. It will only reallocate its matrix data memory if the new size will not fit into the old one. • MATLAB style initializer: zeros(), ones(), : eyes() Mat E = Mat: : eye(4, 4, CV_64 F); Mat O = Mat: : ones(2, 2, CV_32 F); Mat Z = Mat: : zeros(3, 3, CV_8 UC 1);

Create a Mat object 3 • For small matrices Mat C = (Mat_<double>(3, 3)

Create a Mat object 3 • For small matrices Mat C = (Mat_<double>(3, 3) << 0, -1, 5, -1, 0); • Create a new header for an existing Mat object and clone() or copy. To() it Mat Row. Clone = C. row(1). clone();

Create a Mat object 4 • Create a random matrix with randu() Mat R

Create a Mat object 4 • Create a random matrix with randu() Mat R = Mat(3, 2, CV_8 UC 3); randu(R, Scalar: : all(0), Scalar: : all(255)); cout << "R (default) = " << endl << R << endl; cout << "R (python) = " << endl << format(R, "python") << endl; cout << "R (csv) = " << endl << format(R, "csv" ) << endl; …

Resources • http: //opencv. willowgarage. com/wiki/ • C++ Cheatsheet. pdf • Online reference manual:

Resources • http: //opencv. willowgarage. com/wiki/ • C++ Cheatsheet. pdf • Online reference manual: 2. 3 documentation is here 2008 2011

References • Open. CV 2 Computer Vision Application Programming Cookbook, 2011.

References • Open. CV 2 Computer Vision Application Programming Cookbook, 2011.