Как бы я мог циклически перемещаться по изображению с помощью opencv, как если бы это был массив 2d, чтобы получить значения rgb для каждого пикселя? Кроме того, будет ли матовый предпочтительнее ipl для этой операции?
Цикл через пиксели с opencv
Ответ 1
Если вы используете С++, используйте С++-интерфейс opencv, а затем вы можете получить доступ к членам через 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
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; i<img.cols; j++)
std::cout << img.at<uchar>(i,j) << std::endl;
UPDATE: использование 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: благодаря entarion для определения ошибки, которую я представил при копировании и вставке из примера cv:: Vec3b.
Ответ 3
Начиная с OpenCV 3.0, существует официальный и быстрый способ запускать функцию по всему пикселю в cv:: Mat.
void cv:: Mat:: forEach (const Functor & operation)
Если вы используете эту функцию, операция выполняется на многоядерном ядре автоматически.
Раскрытие информации: я являюсь разработчиком этой функции.
Ответ 4
Документы показывают хорошо написанное сравнение различных способов итерации над изображением Mat здесь.
Самый быстрый способ - использовать указатели стиля 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 на сегодняшний день является самой быстрой (также описанной в документах).
Ответ 5
Если вы хотите изменить 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;
}
}
}