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