Циклический просмотр пикселей с помощью opencv - PullRequest
24 голосов
/ 22 декабря 2010

Как бы я мог циклически проходить по изображению, используя opencv, как если бы это был двумерный массив для получения значений rgb каждого пикселя? Кроме того, будет ли мат предпочтительнее, чем iplimage для этой операции?

Ответы [ 7 ]

42 голосов
/ 22 декабря 2010

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

ОБНОВЛЕНИЕ: Спасибо entarion за обнаружение ошибки, которую я представил при копировании и вставке из примера cv :: Vec3b.

11 голосов
/ 26 августа 2015

Начиная с OpenCV 3.0, существует официальный и самый быстрый способ запуска функции по всему пикселю в cv :: Mat.

void cv :: Mat :: forEach (const Функтор и операция)

Если вы используете эту функцию, операция выполняется на многоядерном процессоре автоматически.

Раскрытие информации: я внес вклад в эту функцию.

10 голосов
/ 22 декабря 2010

Если вы используете C ++, используйте интерфейс C ++ 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 ().

3 голосов
/ 28 июня 2018

Поскольку 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;
3 голосов
/ 27 мая 2015

Документы показывают хорошо написанное сравнение различных способов итерации по изображению циновки здесь .

Самый быстрый способ - использовать указатели в стиле 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 является самой быстрой (также описанной в документации).

1 голос
/ 21 сентября 2017

Если вы хотите изменить пиксели 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;
    }
    }
}
0 голосов
/ 10 февраля 2019

Это старый вопрос, но его нужно обновить, так как opencv активно разрабатывается.Недавно в OpenCV появился параллель_для_, который соответствует лямбда-функциям c ++ 11.Вот пример

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));
    }
});

Примечательно, что этот метод использует ядра ЦП в современных компьютерных архитектурах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...