Пиксельный доступ к изображению в OpenCV

168
14 августа 2021, 12:50

Есть матрица OpenCV:

cv::Mat m;

Нужно, например, пробежаться по ней циклом и вывести значения всех каналов каждого пикселя. В одном проекте (OpenVINO samples, но это не так важно) для этого используется такой цикл:

for (size_t c = 0; c < channels; c++) {
    for (size_t h = 0; h < height; h++) {
        for (size_t w = 0; w < width; w++) {
            std::cout << m.at<cv::Vec3b>(h, w)[c];
        }
    }
}

Попытка использовать этот кусочек в моём приложении привела к тому, что все изображения оказались битыми. Пришлось разбираться с циклом. И я теперь не могу понять, не содержит ли он ошибку.

Допустим изображение 1-канальное (один из случаев, в котором я безуспешно пытался применить этот цикл). Тогда, казалось бы, все значения пикселей должны лежать последовательно как переменные unsigned char, это значит, что разница между адресами соседних пикселей должна быть равна 1, вывожу на экран:

std::cout << (void*)&(m.at<cv::Vec3b>(10, 7)[0]) << std::endl;
std::cout << (void*)&(m.at<cv::Vec3b>(10, 8)[0]) << std::endl;

и вижу совсем не то:

0000017C9F7123D5
0000017C9F7123D8

С другой стороны всё правильно, если мы интерпретируем позицию как 3-вектор, то разница адресов должна быть как раз 3 байта (uchar). Но тогда цикл выше некорректно работает для изображений, у которых не 3 канала. Выходит, что у Intel в их примерах ошибка.

Как же на самом деле следует обращаться к пикселям cv::Mat? Содержит ли цикл выше ошибку, или это я неправильно его применил?

Answer 1

Для одноканальных изображений (CV_8UC1) вместо m.at<cv::Vec3b> должно быть m.at<uchar>(), для другой канальности соответственно нужно поменять тип.

Да, каждая строка матрицы лежит последовательно (между строками может быть зазор, обусловленный выравниванием step), это можно использовать при работе на низком уровне - через ptr() как указатель на начало всего блока данных или конкретную строку адресовать байтовый массив

Answer 2
#include <cstdio>
#include <opencv2/imgcodecs.hpp>
int main(){
    cv::Mat m = cv::imread("img.bmp");
    unsigned size = m.cols * m.rows * m.channels();
    for(unsigned i = 0; i < size; ++i) printf((i+1) % m.channels() ? "%02X " : "%02X\n", m.data[i]);
    return 0;
}
READ ALSO
Скрол, когда он не нужен

Скрол, когда он не нужен

Имеется такой скрипт

154
Как обратиться к конкретному элементу из нескольких с одинаковым классом в jQuery?

Как обратиться к конкретному элементу из нескольких с одинаковым классом в jQuery?

Помогите разобраться, пожалуйстаИмеем несколько элементов с одинаковым классом

127
Как ввести выходные данные для приложения &ldquo;Поиск последнего созданного файла с заданным расширением&rdquo;:

Как ввести выходные данные для приложения “Поиск последнего созданного файла с заданным расширением”:

Дано такое задание: Разработать консольное приложение "Поиск последнего созданного файла с заданным расширением":

189
interop Word c# замена слов

interop Word c# замена слов

Я методом Find заменяю некоторые слова, потом сохраняю ворд как пдф, но мне так надо сделать 100 разИ что бы не сохранять изменения а получить...

157