Creating Mat Objects
We'll learn how we can write a matrix to an image file, however, for debugging purposes it's much more convenient to see the actual values. We do this using the << operator of Mat.
Here is a testing file, t.cpp:
#include <opencv2/core/core.hpp> #include <iostream> using namespace cv; using namespace std; int main( int argc, char** argv ) { Mat img(2,2, CV_8UC3, Scalar(126,0,255)); cout << "img = \n " << img << "\n\n"; return 0; }
and with the cmake file :
cmake_minimum_required(VERSION 2.8) project( Tutorials ) find_package( OpenCV REQUIRED ) add_executable( Tutorials t.cpp ) target_link_libraries( Tutorials ${OpenCV_LIBS} )
So, put the two files in a directory called Tutorials and run cmake:
$ cmake . -- Configuring done -- Generating done -- Build files have been written to: /home/khong/OpenCV/workspace/Tutorials $ make Scanning dependencies of target Tutorials [100%] Building CXX object CMakeFiles/Tutorials.dir/t.cpp.o Linking CXX executable Tutorials [100%] Built target Tutorials
If we run the code:
$ ./Tutorials img = [126, 0, 255, 126, 0, 255; 126, 0, 255, 126, 0, 255]
If we modify the row from 2 to 5:
Mat img(2,2, CV_8UC3, Scalar(126,0,255)); ==> Mat img(5,2, CV_8UC3, Scalar(126,0,255));
We get:
img = [126, 0, 255, 126, 0, 255; 126, 0, 255, 126, 0, 255; 126, 0, 255, 126, 0, 255; 126, 0, 255, 126, 0, 255; 126, 0, 255, 126, 0, 255]
Here is the convention for the data type used in CV_8UC3:
CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]
So, here is the meaning of the CV_8UC3:
we want to use unsigned char types('U') that are 8 bit long and each pixel has 3 of these to form the 3 channels. The Scalar is four element short vector.
Single channel array
Here is a single channel array with 8 bit unsigned integers. As the datatype of this array is 8 bit unsigned integers, each element should have a value from 0 to 255.
- CV_8U (8 bit unsigned integer)
- CV_8S (8 bit signed integer)
- CV_16U (16 bit unsigned integer)
- CV_16S (16 bit signed integer)
- CV_32S (32 bit signed integer)
- CV_32F (32 bit floating point number)
- CV_64F (64 bit float floating point number)
Multi channel array
Here is a single channel array with 8 bit unsigned integers. As the datatype of this array is 8 bit unsigned integers, each element should have a value from 0 to 255.
- CV_8UC1 (single channel array with 8 bit unsigned integers)
- CV_8UC2 (2 channel array with 8 bit unsigned integers)
- CV_8UC3 (3 channel array with 8 bit unsigned integers)
- CV_8UC4 (4 channel array with 8 bit unsigned integers)
- CV_8UC(n) (n channel array with 8 bit unsigned integers (n can be from 1 to 512) )
Note: CV_8U = CV_8UC1 = CV_8UC(1)
Here are the samples of the convention:
- Mat img(2, 4, CV_32F ); // 2x4 single-channel array with 32 bit floating point numbers
- Mat img(3, 5, CV_8FC(4) ); // 3x5 4-channel array with 8 bit floating point numbers
- Mat img(Size(480, 360), CV_64UC3 ); //480x360 3-channel array with 64 bit unsigned integers
We can create a matrix with more than two dimensions. We can use array to initialize the constructor:
int arr[3] = {4,3,2}; Mat L(3, arr, CV_8UC(1), Scalar::all(0));
We specified its dimension of 3, then pass a pointer containing the size for each dimension.
We can create a header for an already existing IplImage pointer:
IplImage* img = cvLoadImage("sample.png", 1); Mat mtx(img);
Mat mtx(img) makes IplImage pointer img to point Mat.
Let's run the follow code:
#include <opencv2/core/core.hpp> #include <iostream> using namespace cv; using namespace std; int main( int argc, char** argv ) { Mat img(5,4, CV_8UC3, Scalar(0,0,255)); cout << "img = \n"<< " " << img << "\n\n"; img.create(5,4, CV_8UC(3)); cout << "img = \n"<< " " << img << "\n\n"; return 0; }
The output is:
img = [ 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255] img = [ 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255]
No problem here. However, if we run after modifying the size:
#include <opencv2/core/core.hpp> #include <iostream> using namespace cv; using namespace std; int main( int argc, char** argv ) { Mat img(5,4, CV_8UC3, Scalar(0,0,255)); cout << "img = \n"<< " " << img << "\n\n"; img.create(3,2, CV_8UC(3)); cout << "img = \n"<< " " << img << "\n\n"; return 0; }
The new output after changing the size:
img = [ 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255; 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255] img = [ 50, 53, 53, 0, 0, 0; 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0]
As we can see from the above example, we cannot initialize the matrix values with this constructor. It will only reallocate its matrix data memory if the new size will not fit into the old one.
Let's look at other types of initializer which are MATLAB style:
Mat imgA = Mat::eye(4, 4, CV_64F); cout << "imgA = \n " << imgA << "\n\n"; Mat imgB = Mat::ones(2, 2, CV_32F); cout << "imgB = \n " << imgB << "\n\n"; Mat imgC = Mat::zeros(3,3, CV_8UC1); cout << "imgC = \n " << imgC << "\n\n";
Output:
imgA = [1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1] imgB = [1, 1; 1, 1] imgC = [ 0, 0, 0; 0, 0, 0; 0, 0, 0]
We can also use comma separated initializers:
#include <opencv2/core/core.hpp> #include <iostream> using namespace cv; using namespace std; int main( int argc, char** argv ) { Mat img = (Mat_<int>(3,3) << 1, 0, 1, 0, -2, 0, 3, 0, 3); cout << "img = \n " << img << "\n\n"; return 0; }
Output:
img = [1, 0, 1; 0, -2, 0; 3, 0, 3]
Here is an example of using clone():
#include <opencv2/core/core.hpp> #include <iostream> using namespace cv; using namespace std; int main( int argc, char** argv ) { Mat img = (Mat_<int>(3,3) << 1, 0, 1, 0, -2, 0, 3, 0, 3); cout << "img = \n " << img << "\n\n"; Mat row_cloned_img = img.row(1).clone(); cout << "row_cloned_img = \n " << row_cloned_img << "\n\n"; return 0; }
Output:
img = [1, 0, 1; 0, -2, 0; 3, 0, 3] row_cloned_img = [0, -2, 0]
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization