C++ 使用 opencv 循环像素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4504687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cycle through pixels with opencv
提问by a sandwhich
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?
我如何能够使用 opencv 循环浏览图像,就好像它是一个二维数组来获取每个像素的 rgb 值?另外,对于此操作,mat 是否比 iplimage 更可取?
采纳答案by etarion
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-wayor using cv::Mat::at(), for example.
如果你使用C++,使用opencv的C++接口,然后你可以通过http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way或使用访问成员例如,cv::Mat::at()。
回答by Dat Chu
cv::Mat
is preferred over IplImage
because it simplifies your code
cv::Mat
首选,IplImage
因为它简化了您的代码
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.
这假设您需要一起使用 RGB 值。如果不这样做,您可以使用 cv::split 分别获取每个通道。有关示例链接,请参阅 etarion 的回答。
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.
此外,在我的情况下,您只需要灰度图像。然后,您可以加载灰度图像并将其作为 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
更新:使用 split 获得 3 个通道
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.
更新:感谢 entarion 发现了我在从 cv::Vec3b 示例复制和粘贴时引入的错误。
回答by user3188838
Since OpenCV 3.0, there are official and fastest way to run function all over the pixel in cv::Mat.
从 OpenCV 3.0 开始,有官方最快的方法可以在 cv::Mat 中的像素上运行函数。
void cv::Mat::forEach (const Functor& operation)
void cv::Mat::forEach (const Functor& 操作)
If you use this function, operation is runs on multi core automatically.
如果使用此功能,操作将自动在多核上运行。
Disclosure : I'm contributor of this feature.
披露:我是此功能的贡献者。
回答by Martin R.
Since OpenCV 3.3 (see changelog) it is also possible to use C++11 style for loops:
从 OpenCV 3.3(见变更日志)开始,也可以使用 C++11 风格的循环:
// 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;
回答by vipers36
The docs show a well written comparison of different ways to iterate over a Mat image here.
文档在此处对迭代 Mat 图像的不同方法进行了很好的比较。
The fastest way is to use C style pointers. Here is the code copied from the docs:
最快的方法是使用 C 风格的指针。这是从文档中复制的代码:
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.
使用 at 访问元素非常慢。
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).
请注意,如果您的操作可以使用查找表执行,则内置函数 LUT 是迄今为止最快的(也在文档中进行了描述)。
回答by Davood Falahati
This is an old question but needs to get updated since opencv is being actively developed. Recently, OpenCV has introduce parallel_for_ which complies with c++11 lambda functions. Here is the example
这是一个老问题,但需要更新,因为 opencv 正在积极开发中。最近,OpenCV 引入了符合 c++11 lambda 函数的 parallel_for_。这是例子
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.
值得一提的是,这种方法使用了现代计算机体系结构中的 CPU 内核。
回答by Acidic9
If you want to modify RGB pixels one by one, the example below will help!
如果你想一一修改RGB像素,下面的例子会有所帮助!
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;
}
}
}