OpenCV C ++: невозможно получить доступ к элементу mat, когда тип mat неизвестен? - PullRequest
0 голосов
/ 29 сентября 2018

Изучение https://stackoverflow.com/a/37484930/1434693, Я создал следующие шаблоны в своем заголовочном файле MatOperations.h

template<int _depth> class TypeDepth
{
    public:
        enum { depth = CV_USRTYPE1 };
        typedef void value_type;
};

template<> class TypeDepth<CV_8U>
{
    public:
        enum { depth = CV_8U };
        typedef uchar value_type;
};

template<> class TypeDepth<CV_8S>
{
    public:
        enum { depth = CV_8S };
        typedef schar value_type;
};

template<> class TypeDepth<CV_16U>
{
    public:
        enum { depth = CV_16U };
        typedef ushort value_type;
};

template<> class TypeDepth<CV_16S>
{
    public:
        enum { depth = CV_16S };
        typedef short value_type;
};

template<> class TypeDepth<CV_32S>
{
    public:
        enum { depth = CV_32S };
        typedef int value_type;
};

template<> class TypeDepth<CV_32F>
{
    public:
        enum { depth = CV_32F };
        typedef float value_type;
};

template<> class TypeDepth<CV_64F>
{
    public:
        enum { depth = CV_64F };
        typedef double value_type;
};

Теперь, чтобы получить доступ к элементу матрицы в MatOperations.cpp, я использую следующую команду:

int type = gray_image.type();
typedef TypeDepth<type>::value_type access_type;
std::cout << gray_image.at<access_type>(1, 1);

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

  1. Как это исправить?
  2. Это самый быстрый способ доступа к элементу мата, если мы изучаем тип из данных?Если нет, как я могу улучшить это?

1 Ответ

0 голосов
/ 29 сентября 2018

OpenCV имеет свои собственные имена классов шаблонов: DataType.Да, в отличие от класса, который вы сделали, параметр шаблона DataType является типом (uchar,...,double, cv::Vec2b,... cv::Vec4d).Вы перепутали метод type() и depth() из Mat.Метод type() возвращает флаг типа, то есть тип И его каналы (например, CV_8UC3), а метод depth() возвращает только тип (например, если вы работаете с цветным изображением CV_8U).

Это самый быстрый способ получить доступ к элементу мата, если мы изучаем тип по данным?Если нет, то как я могу улучшить его?

На самом деле есть несколько способов сделать это, который используется в некоторых модулях cuda OpenCV, таких как двусторонний фильтр состоитсоздать шаблонную функцию, которая принимает общие аргументы, устанавливает некоторые типы контейнеров и выполняет некоторую работу.

например,

    // Anonymous namespace allow not to put static in from of every function declared in its scope.
        namespace
        {
                // without using specialized container
                template<class type>
                void fma_worker_gen(const cv::Mat& _A, const cv::Mat& _B, const cv::Mat& _C, cv::Mat& _D)
                {
                   for(int r=0;r<A.rows;r++)
                      for(int c=0;c<A.cols;c++)
                          _D.at<type>(r,c) = _A.at<type>(r,c) * _B.at<type>(r,c) + _C.at<type>(r,c);
                }
            // with specialized container. Remember by assignment there is not copy see documentation for more explanation about it.
                template<class type>
                void fma_worker_spec(const cv::Mat& _A, const cv::Mat& _B, const cv::Mat& _C, cv::Mat& _D)
                {

                  cv::Mat_<type> A = _A;
                  cv::Mat_<type> B = _B;
                  cv::Mat_<type> C = _C;
                  cv::Mat_<type> D = _D;

                   for(int r=0;r<A.rows;r++)
                      for(int c=0;c<A.cols;c++)
                          D(r,c) = A(r,c) * B(r,c) + C(r,c);
                }
           } // anonymous namespace


            // callable function.
            void fma(cv::InputArray _A, cv::InputArray _B, cv::InputArray _C, cv::OutputArray _D)
            {
              typedef void(*function_type)(const cv::Mat&, const cv::Mat&, const cv::Mat&, cv::Mat&);
    // I set fma_worker_gen but fma_worker_spec could also fit.
              static const funcs[7][4] = 
                                       {
                                         {fma_worker_gen<uchar>, fma_worker_gen<cv::Vec2b>, fma_worker_gen<cv::Vec3b>, fma_worker_gen<cv::Vec4b>},
    {fma_worker_gen<schar>, fma_worker_gen<cv::Vec<schar,2> >, fma_worker_gen<cv::Vec<schar,3> >, fma_worker_gen<cv::Vec<schar,4> >},
    {fma_worker_gen<ushort>, fma_worker_gen<cv::Vec2w>, fma_worker_gen<cv::Vec3w>, fma_worker_gen<cv::Vec4w>},
    {fma_worker_gen<short>, fma_worker_gen<cv::Vec2s>, fma_worker_gen<cv::Vec3s>, fma_worker_gen<cv::Vec4s>},
    {fma_worker_gen<int>, fma_worker_gen<cv::Vec2i>, fma_worker_gen<cv::Vec3i>, fma_worker_gen<cv::Vec4i>},
    {fma_worker_gen<float>, fma_worker_gen<cv::Vec2f>, fma_worker_gen<cv::Vec3f>, fma_worker_gen<cv::Vec4f>},
    {fma_worker_gen<double>, fma_worker_gen<cv::Vec2d>, fma_worker_gen<cv::Vec3d>, fma_worker_gen<cv::Vec4d>},
                                       };
    CV_Assert( (_A.size()==_B.size()) && (_A.size()==_C.size()) && (_A.type()==_B.type()) && (_A.type()==_C.type()) );

   _D.create(_A.size(),_A.type()); // Allocate memory only size or type does not fit the arguments.

   cv::Mat A = _A.getMat();
   cv::Mat B = _B.getMat();
   cv::Mat C = _C.getMat();
   cv::Mat D = _D.getMat();

   function_type fun = nullptr;

   fun = funcs[A.depth()][A.channels()-1];

   CV_Assert(fun);

   fun(A,B,C,D);

            }

Примечание: приведенный выше код выглядит какиллюстрация и может не работать и не компилироваться как есть.

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

Однако существует гораздо более простой способ работы с Mat элементами, которыесостоят в том, чтобы преобразовывать и преобразовывать данные, чтобы упростить обработку перед изменением формы перед ее возвратом.

например

void fma(cv::InputArray _A, cv::InputArray _B, cv::InputArray _C, cv::OutputArray _D)
                {
    CV_Assert( (_A.size()==_B.size()) && (_A.size()==_C.size()) && (_A.type()==_B.type()) && (_A.type()==_C.type()) );

   _D.create(_A.size(),_A.type()); // Allocate memory only size or type does not fit the arguments.

   cv::Mat A = _A.getMat();
   cv::Mat B = _B.getMat();
   cv::Mat C = _C.getMat();
   cv::Mat D = _D.getMat();

   const int stype = A.type();
   const int sdepth = CV_MAT_DEPTH(stype);
   const int wdepth = std::max(sdepth,CV_32F);
   const int cn = CV_MAT_CN(stype);
   const int wtype = CV_MAKETYPE(wdepth,cn);

   // now a temporary variable is needed.
   cv::Mat tmp = cv::Mat::zeros(A.size(),wtype);

   // if wdepth != sdepth a copy is make during the conversion nothing happen otherwise.
   A = A.convertTo(A,wdepth);
   B = B.convertTo(B,wdepth);
   C = C.convertTo(C,wdepth);

   // OpenCV's Mat container management the memory as row aligned and the channels are interlaced. This mean that reshaping an matrix with C channels to a matrix with a single channel is unlikely to return a copy.

   A = A.reshape(1);
   B = B.reshape(1);
   C = C.reshape(1);
   tmp = tmp.reshape(1);

   if(wdepth==CV_32F)
   {
     for(int r=0;r<A.rows;r++)
        for(int c=0;c<A.cols;c++)
           tmp.at<float>(r,c) = A.at<float>(r,c)*B.at<float>(r,c)+C.at<float>(r,c);
   }
   else
   {
     for(int r=0;r<A.rows;r++)
        for(int c=0;c<A.cols;c++)
           tmp.at<double>(r,c) = A.at<double>(r,c)*B.at<double>(r,c)+C.at<double>(r,c);
   }
   tmp = tmp.reshape(cn);
   tmp.convertTo(D,sdepth); 

   // Because there is no copy by assignement if the source type (sdepth) and the working type (wdepth) are the same then the fact to reshape the matrix may also have reshape it outside the function. By reshaping every matrix to its original shape any unwilled influence is eliminate.

   if(sdepth == wdepth)
    {
      A = A.reshape(cn);
      B = B.reshape(cn);
      C = C.reshape(cn);
     }
}

Примечание: приведенный выше код имеет видиллюстрация и может не работать и не компилироваться как есть.

С использованием рабочего типа работать быстрее и проще, потому что некоторые типы могут быть сделаны относительно типов.В последнем примере каналы управляются более неявным образом, т. Е. Когда матрицы преобразуются в один канал (.reshape(1)), число столбцов умножается на количество каналов из-за того, что OpenCV хранит каналы как чересстрочные.В некоторых обстоятельствах может оказаться полезным не изменять форму одного канала и работать непосредственно с каналами, тогда вы можете смешать два подхода.

Последний пункт состоит в том, чтобы использовать предварительно ваши данные и / или рабочий тип.,например,

cv::Mat1s a = {1,2,3,4,5};
cv::Mat1s b = {6,7,8,9,10};
cv::Mat1s c = {11,12,13,14,15};

cv::Mat1f tmp = cv::Mat1f::zeros(a.size()); // or cv::Mat1f tmp = a*b+c;
for(int i=0;i<a.total();i++)
   tmp(i) = a(i)*b(i)+c(i);

cv::Mat1s d = tmp;
...