Как получить доступ / изменить элемент матрицы в OpenCV? Почему at () является шаблонизированным?

Я знаю Mat тип элемента at() правильно? Например, если у меня

Mat rose = Mat(1,180, CV_64F, 0);

тогда я могу позвонить

rose.at<short>(i,j)++;

если нет, то какой аргумент шаблона должен я использовать?

почему Mat::at это templatized а нет?

обновление

этот вопрос содержал пример кода с другой ошибкой, которая теперь здесь:Как заполнить матрицу нулями в OpenCV?

3 ответов


как уже правильно отметил Уильям, вы должны предоставить только правильный тип в качестве аргумента шаблона для at. Я верю этому cv::Mat сам шаблон не сделан только для упрощения. Однако команда OpenCV пытается поддерживать функции C++, включая шаблоны. Таким образом, интерфейс стал немного неоднородным.

вы не могли вывести тип компилятора из переменной типа во время выполнения по очевидной причине. Однако вы можете вывести его во время компиляции, если ваша переменная типа известна на данный момент, используя класс признаков:

template<int I>
struct CvType {};

template<>
struct CvType<CV_64F> { typedef double type_t; };
template<>
struct CvType<CV_32F> { typedef float type_t; };
template<>
struct CvType<CV_8U> { typedef unsigned char type_t; };
// Other types go here

void main()
{
  const int type = CV_64F;
  cv::Mat mat(10, 10, type);
  mat.at<CvType<type>::type_t>(1, 1);
}

в этом случае вы можете изменить значение type и не нужно будет менять типы вручную для всех at или другие вызовы методов.


Ну теперь ваш редактировать пост отличается. Исправлено:

Mat m1 = Mat(1,1, CV_64F, cvScalar(0.));
m1.at<double>(0,0) = 0;

или попробуйте иначе:

Mat m1 = cv::Mat::zeros(1,1, CV_64F);
m1.at<double>(0,0) = 0;

на Mat class не является классом шаблона, который позволяет изменять его "тип" во время выполнения. Изменение типа полезно, например, при чтении из файла. Не быть шаблоном удобно, при использовании Mat как параметр функции, так как функция не обязана быть функцией шаблона.

Впрочем, для доступа к отдельным элементам (с указатели, at или итераторы) вам нужен тип данных. Я думаю, это сделано для производительности. Это противоречит системе типов среды выполнения и затрудняет написание универсального кода, когда вы не знаете тип во время компиляции. Тем не менее, вы можете сделать это с решение.

самый простой метод - просто использовать каскад if-else:

Mat img = imread("test.png");
if (img.channels() == 1) {
    if (img.depth() == CV_8U) {
        cout << (int)(img.at<uint8_t>(0,0)) << endl;
    }
    else if (img.depth() == CV_8S) {
        /* ... */
    }
    /* ... */
}
/* ... */
else if (img.channels() == 3) {
    if (img.depth() == CV_8U) {
        auto p = img.at<array<uint8_t,3>>(0,0);
        cout << (int)(p[0]) << ";" << (int)(p[1]) << ";" << (int)(p[2]) << endl;
    }
    /* ... */
}
/* ... */

но вы можете представить, что это становится громоздким, если вы пишете это для всех типов и каналов. Вы должны ограничить количество каналов в любом случае, так как нет жесткого ограничения OpenCV. Мы выберите 4 из следующих вариантов.

я написал вспомогательный шаблон заголовка метапрограммы, который выполняет эту работу. Вы можете предоставить функтор с шаблоном operator(). Затем вызовите метапрограмму шаблона, которая вызовет функтор с типом времени компиляции. См. этот пример для функтора, который печатает первый пиксель и возвращает, не все ли равно нулю:

struct PrintPixel {
    Mat img;

    // this template function will be called from the metaprogram
    template<int cv_type> // compile time value e.g. CV_8UC3
    bool operator()() {
        using elem_t  = typename CvTypeTraits<cv_type>::base_type;
        using array_t = typename CvTypeTraits<cv_type>::array_type;
        // you could also do static_asserts here

        array_t pixel = img.at<array_t>(0,0);
        for (elem_t val : pixel)
            cout << (double)(val) << ", ";
        cout << endl;
        return any_of(pixel.begin(), pixel.end(), [](elem_t v){return v != 0;});
    }
};

Примечание, возвращаемый тип operator() может быть произвольным, но не может зависеть от типа изображения cv_type к сожалению. Это связано с тем, что он также используется в качестве возвращаемого типа функции, которая содержит каскад if-else (