我如何使用opencv循环浏览图像,就好像它是一个二维数组来获取每个像素的rgb值?此外,对于此操作,是否优于iplimage?
答案 0 :(得分:42)
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;
这假设您需要一起使用RGB值。如果不这样做,您可以使用cv :: split分别获取每个通道。有关示例的链接,请参阅etarion的答案。
另外,在我的情况下,你只需要灰度图像。然后,您可以以灰度加载图像并将其作为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;
更新:使用拆分获取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
更新:感谢entorion发现我从cv :: Vec3b示例复制和粘贴时引入的错误。
答案 1 :(得分:10)
如果您使用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()来访问成员。
答案 2 :(得分:3)
文档显示了对Mat图像here进行迭代的不同方式的良好书写比较。
最快的方法是使用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;
}
使用at访问元素非常慢。
请注意,如果您的操作可以使用查找表执行,则内置函数LUT是最快的(也在文档中描述)。
答案 3 :(得分:3)
自OpenCV 3.3(see changelog)起,也可以将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;
答案 4 :(得分:1)
如果您想逐个修改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;
}
}
}
答案 5 :(得分:0)
这是一个老问题,但是由于opencv正在积极开发中,因此需要更新。最近,OpenCV引入了parallel_for_,它符合c ++ 11 lambda函数。这是示例
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));
}
});
值得一提的是,这种方法使用了现代计算机体系结构中的CPU内核。