Reputation: 231
How can I access an RGB mat as a 1D array? I looked at the documentation but couldn't find how the 3 channel data is laid out in that case.
I'm trying to loop over each pixel with 1 for loop going from n=0
to n = img.rows*img.cols - 1
, and access R, G, and B values at each pixel.
Any help would be greatly appreciated.
Upvotes: 2
Views: 5457
Reputation: 101
//C++ Code Below
//your RGB image
cv::Mat image;
//your 1D array
cv::Mat newimage;
//the function to convert the image into 1D array
image.reshape(0, 1).convertTo(newimage, CV_32F);
//http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-reshape
Upvotes: 0
Reputation: 9379
I don't really understand why you really need only 1 loop, so I will propose you several options (including 1 or 2 for-loops) that I know by experience to be efficient.
If you really want to iterate over all the values with only one loop in a safe way, you can reshape the matrix and turn a 3-channel 2D image into a 1-channel 1D array using cv::Mat::reshape(...)
(doc):
cv::Mat rgbMat = cv::imread(...); // Read original image
// As a 1D-1 channel, we have 1 channel and 3*the number of pixels samples
cv::Mat arrayFromRgb = rgbMat.reshape(1, rgbMat.channels()*rgbMat.size().area());
There are two caveats:
reshape()
returns a new cv::Mat
reference, hence its output needs to be assigned to a variable (it won't operate in-place)OpenCV stores the matrix data in row-major order. Thus, an alternative is to iterate over the rows by getting a pointer to each row start. This way, you will not do anything unsafe because of possible padding data at the end of the rows:
cv::Mat rgbMat = cv::imread(...);
for (int y = 0; y < rgbMat.size().height; ++y) {
// Option 1: get a pointer to a 3-channel element
cv::Vec3b* pointerToRgbPixel = rgbMat.ptr<cv::Vec3b>(y);
for (int x = 0; x < rgbMat.size().width; ++x, ++pointerToRgbPixel) {
uint8_t blue = (*pointerToRgbPixel )[0];
uint8_t green = (*pointerToRgbPixel )[1];
uint8_t red = (*pointerToRgbPixel )[2];
DoSomething(red, green, blue);
}
// Option 2: get a pointer to the first sample and iterate
uint8_t* pointerToSample = rgbMat.ptr<uint8_t>(y);
for (int x = 0; x < rgbMat.channels()*rgbMat.size().width; ++x) {
DoSomething(*pointerToSample);
++pointerToSample;
}
}
Why do I like the iteration over the rows ?
Because it is easy to make parallel.
If you have a multi-core computer, you can use any framework (such as OpenMP or GCD) to handle each line in parallel in a safe way.
Using OpenMP, it as easy as adding a #pragma parallel for
before the outer loop.
Upvotes: 4
Reputation: 4074
Yes it is referenced over there in the documentation.
And why don't you see the snippet below:
template<int N>
void SetPixel(Mat &img, int x, int y, unsigned char newVal) {
*(img.data + (y * img.cols + x) * img.channels() + N) = newVal;
}
int main() {
Mat img = Mat::zeros(1000, 1000, CV_8UC4);
SetPixel<0>(img, 120);
SetPixel<1>(img, 120);
SetPixel<2>(img, 120);
imwrite("out.jpg", img);
return 0;
}
But it is not the safe way, it assumes mat data lays continuously in the momory (and there is no space in bytes between its rows). So better check Mat::isContinous() before using this snippet.
Upvotes: 0