Opencv: конвертировать матрицу RGB в массив 1d

Как я могу получить доступ к мату RGB как 1D массив? Я посмотрел на документацию, но не смог найти, как в этом случае выкладываются 3-канальные данные.

Я пытаюсь перебрать каждый пиксель с 1 для цикла, идущего от n=0 в n = img.rows*img.cols - 1и получить доступ к значениям R, G и B в каждом пикселе.

Любая помощь будет принята с благодарностью.

3 ответа

Решение

Я не очень понимаю, почему вам действительно нужен только 1 цикл, поэтому я предложу вам несколько вариантов (включая 1 или 2 цикла for), которые, как я знаю по опыту, будут эффективными.

Если вы действительно хотите перебирать все значения одним циклом безопасным способом, вы можете изменить форму матрицы и преобразовать 3-канальное 2D-изображение в 1-канальный 1D-массив, используя cv::Mat::reshape(...) ( док):

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

Есть две оговорки:

  • reshape() возвращает новый cv::Mat ссылка, следовательно, ее вывод должен быть присвоен переменной (она не будет работать на месте)
  • Вы не можете изменять количество элементов в матрице.

OpenCV сохраняет данные матрицы в порядке следования строк. Таким образом, альтернативой является итерация по строкам путем получения указателя на начало каждой строки. Таким образом, вы не будете делать ничего небезопасного из-за возможного заполнения данных в конце строк:

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

Почему мне нравится итерация по строкам? Потому что это легко сделать параллельно. Если у вас многоядерный компьютер, вы можете использовать любую среду (например, OpenMP или GCD) для безопасной обработки каждой линии параллельно. Используя OpenMP, это так же просто, как добавить #pragma parallel for перед внешней петлей.

Да, на это есть ссылка в документации.

И почему вы не видите фрагмент ниже:

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

Но это не безопасный способ, он предполагает, что матовые данные непрерывно лежат в памяти (и между строками нет пробелов в байтах). Поэтому лучше проверьте Mat::isContinous() перед использованием этого фрагмента.

//C++ код ниже

// ваше изображение RGB

cv:: Mat image;

// ваш 1D массив

cv:: Mat newimage;

// функция для преобразования изображения в одномерный массив
image.reshape (0, 1).convertTo (newimage, CV_32F);

// http://docs.opencv.org/modules/core/doc/basic_structures.html

Другие вопросы по тегам