I want to declare, populate, access a Multi-Dimensional Matrix in OpenCV (C++) which is compatible with namespace cv. I found no quick and easy to learn examples on them. Can you please help me out?
Here is a short example from the NAryMatIterator documentation; it shows how to create, populate, and process a multi-dimensional matrix in OpenCV:
void computeNormalizedColorHist(const Mat& image, Mat& hist, int N, double minProb)
{
const int histSize[] = {N, N, N};
// make sure that the histogram has a proper size and type
hist.create(3, histSize, CV_32F);
// and clear it
hist = Scalar(0);
// the loop below assumes that the image
// is a 8-bit 3-channel. check it.
CV_Assert(image.type() == CV_8UC3);
MatConstIterator_<Vec3b> it = image.begin<Vec3b>(),
it_end = image.end<Vec3b>();
for( ; it != it_end; ++it )
{
const Vec3b& pix = *it;
hist.at<float>(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f;
}
minProb *= image.rows*image.cols;
Mat plane;
NAryMatIterator it(&hist, &plane, 1);
double s = 0;
// iterate through the matrix. on each iteration
// it.planes[*] (of type Mat) will be set to the current plane.
for(int p = 0; p < it.nplanes; p++, ++it)
{
threshold(it.planes[0], it.planes[0], minProb, 0, THRESH_TOZERO);
s += sum(it.planes[0])[0];
}
s = 1./s;
it = NAryMatIterator(&hist, &plane, 1);
for(int p = 0; p < it.nplanes; p++, ++it)
it.planes[0] *= s;
}
Also, check out the cv::compareHist function for another usage example of the NAryMatIterator here.
To create a multi-dimensional matrix that is of size 100x100x3, using floats, one channel, and with all elements initialized to 10 you write like this:
int size[3] = { 100, 100, 3 };
cv::Mat M(3, size, CV_32FC1, cv::Scalar(10));
To loop over and output the elements in the matrix you can do:
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++)
for (int k = 0; k < 3; k++)
std::cout << M.at<cv::Vec3f>(i,j)[k] << ", ";
However, beware of the troubles with using multi-dimensional matrices as documented here: How do i get the size of a multi-dimensional cv::Mat? (Mat, or MatND)
Related
In the following code I would like to assign a values to elements of a Mat variable in a loop. I get the runtime error below.
pair<Mat, Mat> meshgrid(vector<int> x, vector<int> y) {
int sx = (int)x.size();
int sy = (int)y.size();
Mat xmat = Mat::ones(sy, sx, CV_16U);
Mat ymat = Mat::ones(sy, sy, CV_16U);
for (int i = 0; i < sx; i++) {
for (int j = 0; j < sy; j++) {
xmat.at<int>(i, j) = j; // <------- here is place of error.
cout << j << "\t";
}
cout << endl;
}
for (int i = 0; i < sx; i++) {
for (int j = 0; j < sy; j++) {
ymat.at<int>(i, j) = i; // <------- here is place of error.
cout << j << "\t";
}
cout << endl;
}
return make_pair(xmat, ymat);
}
This picture when debuging;
This is the run time error I get:
OpenCV(...) Error: Assertion failed
(((((sizeof(size_t)<<28)|0x8442211) >> ((traits::Depth<_Tp>::value) &
((1 << 3) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file
...\include\opencv2\core\mat.inl.hpp, line 1108
Thank you for your answers.
I assume you meant to generate output similar to numpy.meshgrid, and Matlab meshgrid.
There are several errors in your code:
The cv::Mat is initialized with type CV_16U (i.e. 16 bit unsigned value), but when you access the elements with at you use int (which is 32bit signed).
You should change it to at<unsigned short> (or change the type of the cv::Mat to 32 bit signed - CV_32S).
You initialized the cv::Mat with wrong sizes: xmat has size of (sy, sx), but ymat has size of (sy, sy).
The indices (row, col) you used to access the mat elements were incorrect. To make it easier to use correctly, I changed the names of the dimentions to rows, cols,
and the loop indices to iRow, iCol.
The values in the matrices should come from the values in x and y vectors (not the indices).
See updated code below (and the notes following it regarding the changes):
#include <opencv2/core/core.hpp>
#include <vector>
#include <iostream>
std::pair<cv::Mat, cv::Mat> meshgrid(std::vector<unsigned short> const & x, std::vector<unsigned short> const & y)
{
int cols = static_cast<int>(x.size());
int rows = static_cast<int>(y.size());
cv::Mat xmat(rows, cols, CV_16U);
cv::Mat ymat(rows, cols, CV_16U);
for (int iRow = 0; iRow < rows; ++iRow) {
auto * pRowData = xmat.ptr<unsigned short>(iRow);
for (int iCol = 0; iCol < cols; ++iCol) {
pRowData[iCol] = x[iCol];
std::cout << pRowData[iCol] << "\t";
}
std::cout << std::endl;
}
std::cout << std::endl;
for (int iRow = 0; iRow < rows; ++iRow) {
auto * pRowData = ymat.ptr<unsigned short>(iRow);
for (int iCol = 0; iCol < cols; ++iCol) {
pRowData[iCol] = y[iRow];
std::cout << pRowData[iCol] << "\t";
}
std::cout << std::endl;
}
return std::make_pair(std::move(xmat), std::move(ymat));
}
int main()
{
std::vector<unsigned short> xxx{ 1,2 };
std::vector<unsigned short> yyy{ 10,11,12 };
auto p = meshgrid(xxx, yyy);
return 0;
}
Output:
1 2
1 2
1 2
10 10
11 11
12 12
Some notes:
I might have misunderstood which values you wanted to set in the cv::Mat's. But at least now you have code that does not crash. You can change the assigned values as you wish.
Using at to access cv::Mat elements one after the other is very inefficient, because at contains some validations for every access.
It's a lot more efficient to use the cv::Mat method ptr, that gives you a pointer to the data of a row. Then you can use this pointer to traverse the row more efficiently - see above
In any method, it is more efficient to traverse a cv::Mat one row after another (and not column by column). This causes you to access continous memory, and decrease the number of cache misses.
In your real code, it's better to separate calculations from I/O. Therefore it's better if your meshgrid function will only create the matrices. Print them outside if you need.
No need to initialize the cv::Mats to ones, because immediatly afterwards we set the values for all elements.
In my code x and y are passed to the function by const refernce. It is more efficient (avoid copy) and also forces the compiler to verify the vectors are not modified.
Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?.
From similar reasons I recomend to avoid using namespace cv as well.
I have a cv::Mat1f vector which size is kxd. How can I fill it by appending k different 1xd vectors?
I want to do something like:
int k = 3, d = 3;
cv::Mat1f testMat(1,k*d);
for(int i=0; i<k;i++){
cv::Mat1f partial(1,d,i);
testMat.push_back(partial);
}
Notice that the example above is much simpler that my real case.
I found a solution, but I don't know if it's the best one
int k = 3, d = 3;
cv::Mat1f testMat(1,k*d);
for(int i=0; i<k;i++){
cv::Mat1f partial(1,d,i);
testMat.push_back(partial);
}
testMat = testMat.reshape(1,1);
I want to loop through a binarized cv::Mat and save all coordinates of pixels with a value of 255.
cv::Mat bin;
std::vector<cv::Point2i> binVec;
int h = 0;
int white = 254; //Just for comparison with pointer of Matrix value
for (int i = 0; i < bin.rows; i++, h++) {
for (int j = 0; j < bin.cols; j++, h++) {
int* p = bin.ptr<int>(h); //Pointer to bin Data, should loop through Matrix
if (p >= &white) //If a white pixel has been found, push i and j in binVec
binVec.push_back(cv::Point2i(i, j));
}
}
This snippet is not working, and I don't know why.
Exception thrown at 0x76C6C42D in example.exe: Microsoft C++ exception: cv::Exception at memory location 0x0019E4F4.
Unhandled exception at 0x76C6C42D in example.exe: Microsoft C++ exception: cv::Exception at memory location 0x0019E4F4.
So how can I count h and let the pointer work?
You can avoid to scan the image. To save the coordinates of all white pixels in a vector you can do like:
Mat bin;
// fill bin with some value
std::vector<Point> binVec;
findNonZero(bin == 255, binVec);
You can use Point instead of Point2i, since they are the same:
typedef Point2i Point;
If you really want to use a for loop, you should do like:
const uchar white = 255;
for (int r = 0; r < bin.rows; ++r)
{
uchar* ptr = bin.ptr<uchar>(r);
for(int c = 0; c < bin.cols; ++c)
{
if (ptr[c] == 255) {
binVec.push_back(Point(c,r));
}
}
}
Remember that:
you binary image is probably CV_8UC1, and not a CV_32SC1, so you should use uchar instead of int.
bin.ptr<...>(i) gives you a pointer to the start of the i-th row, so you should take it out of the inner loop.
you should compare the values, not the address.
Point take as parameters x (cols) and y (rows), while you are passing i (rows) and j (cols). So you need to swap them.
this loop can be further optimized, but for your task I strongly recommend the findNonZero approach, so I don't show it here.
You should only increment h in the inner loop
You should compare the value pointed at by p with h, not compare p with the address of h.
So
cv::Mat bin;
std::vector<cv::Point2i> binVec;
int h = 0;
int white = 254; //Just for comparison with pointer of Matrix value
for (int i = 0; i < bin.rows; i++) {
for (int j = 0; j < bin.cols; j++) {
int* p = bin.ptr<int>(h++); //Pointer to bin Data, should loop through Matrix
if (*p >= white) //If a white pixel has been found, push i and j in binVec
binVec.push_back(cv::Point2i(i, j));
}
}
I want to do an operation like this however I cannot get the values of the vector Mat and change them. table is a 1 dimensional array by the way. Thanks.
vector<Mat> orjchannel;
vector<Mat> refchannel;
// There are some functions here
for (int i = 0; i < 512; i++){
for (int j = 0; j < 512; j++){
double value = refchannel[i][j]; // This part does not work
orjchannel[i][j] = tables[value];
With OpenCV, you typically access the values of a Mat with the at<DATATYPE>(r,c) command. For example...
// Mat constructor
Mat data(4, 1, CV_64FC1);
// Set Value
data.at<double>(0,0) = 4;
// Get Value
double value = data.at<double>(0,0);
How would I be able to cycle through an image using opencv as if it were a 2d array to get the rgb values of each pixel? Also, would a mat be preferable over an iplimage for this operation?
cv::Mat is preferred over IplImage because it simplifies your code
cv::Mat img = cv::imread("lenna.png");
for(int i=0; i<img.rows; i++)
for(int j=0; j<img.cols; j++)
// You can now access the pixel value with cv::Vec3b
std::cout << img.at<cv::Vec3b>(i,j)[0] << " " << img.at<cv::Vec3b>(i,j)[1] << " " << img.at<cv::Vec3b>(i,j)[2] << std::endl;
This assumes that you need to use the RGB values together. If you don't, you can uses cv::split to get each channel separately. See etarion's answer for the link with example.
Also, in my cases, you simply need the image in gray-scale. Then, you can load the image in grayscale and access it as an array of uchar.
cv::Mat img = cv::imread("lenna.png",0);
for(int i=0; i<img.rows; i++)
for(int j=0; j<img.cols; j++)
std::cout << img.at<uchar>(i,j) << std::endl;
UPDATE: Using split to get the 3 channels
cv::Mat img = cv::imread("lenna.png");
std::vector<cv::Mat> three_channels = cv::split(img);
// Now I can access each channel separately
for(int i=0; i<img.rows; i++)
for(int j=0; j<img.cols; j++)
std::cout << three_channels[0].at<uchar>(i,j) << " " << three_channels[1].at<uchar>(i,j) << " " << three_channels[2].at<uchar>(i,j) << std::endl;
// Similarly for the other two channels
UPDATE: Thanks to entarion for spotting the error I introduced when copying and pasting from the cv::Vec3b example.
Since OpenCV 3.0, there are official and fastest way to run function all over the pixel in cv::Mat.
void cv::Mat::forEach (const Functor& operation)
If you use this function, operation is runs on multi core automatically.
Disclosure : I'm contributor of this feature.
If you use C++, use the C++ interface of opencv and then you can access the members via http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way or using cv::Mat::at(), for example.
This is an old question but needs to get updated since opencv is being actively developed. Recently, OpenCV has introduced parallel_for_ which complies with c++11 lambda functions. Here is the example
parallel_for_(Range(0 , img.rows * img.cols), [&](const Range& range){
for(int r = range.start; r<range.end; r++ )
{
int i = r / img.cols;
int j = r % img.cols;
img.ptr<uchar>(i)[j] = doSomethingWithPixel(img.at<uchar>(i,j));
}
});
This is mention-worthy that this method uses the CPU cores in modern computer architectures.
Since OpenCV 3.3 (see changelog) it is also possible to use C++11 style for loops:
// Example 1
Mat_<Vec3b> img = imread("lena.jpg");
for( auto& pixel: img ) {
pixel[0] = gamma_lut[pixel[0]];
pixel[1] = gamma_lut[pixel[1]];
pixel[2] = gamma_lut[pixel[2]];
}
// Example 2
Mat_<float> img2 = imread("float_image.exr", cv::IMREAD_UNCHANGED);
for(auto& p : img2) p *= 2;
The docs show a well written comparison of different ways to iterate over a Mat image here.
The fastest way is to use C style pointers. Here is the code copied from the docs:
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
Accessing the elements with the at is quite slow.
Note that if your operation can be performed using a lookup table, the built in function LUT is by far the fastest (also described in the docs).
If you want to modify RGB pixels one by one, the example below will help!
void LoopPixels(cv::Mat &img) {
// Accept only char type matrices
CV_Assert(img.depth() == CV_8U);
// Get the channel count (3 = rgb, 4 = rgba, etc.)
const int channels = img.channels();
switch (channels) {
case 1:
{
// Single colour
cv::MatIterator_<uchar> it, end;
for (it = img.begin<uchar>(), end = img.end<uchar>(); it != end; ++it)
*it = 255;
break;
}
case 3:
{
// RGB Color
cv::MatIterator_<cv::Vec3b> it, end;
for (it = img.begin<cv::Vec3b>(), end = img.end<cv::Vec3b>(); it != end; ++it) {
uchar &r = (*it)[2];
uchar &g = (*it)[1];
uchar &b = (*it)[0];
// Modify r, g, b values
// E.g. r = 255; g = 0; b = 0;
}
break;
}
}
}