Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV indexing through Mat of unknown type

Tags:

opencv

matrix

I would like to access the elements of a matrix of unknown type:

for(int ii = 0; ii < origCols; ii++)
{
  colIdx.at<img.type()>(0,ii) = ii+1; // make one based index
}

The expression defining the type (inside the <>) has to be a constant, so the above code will not work. Is there a way to do this other than just switching across the different image types?

like image 885
zzzz Avatar asked Jul 16 '12 20:07

zzzz


2 Answers

After looking through some of the docs, I don't think there is a native OpenCV way to do it without avoiding branching.

If you are just concerned about cleaner code, you could try a template approach so long as you don't mind templates:

template <typename T> void dostuff(cv::Mat& colIdx, int origCols)
{
   for(int ii = 0; ii < origCols; ii++)
   {
       colIdx.at<T>(0,ii) = (T)(ii+1); // make one based index
   }
}

void dostuff_poly(cv::Mat& colIdx, int origCols)
{
    switch(colIdx.type())
    {
        case CV_8UC1: dostuff<char>(colIdx, origCols); break;
        case CV_32FC1: dostuff<float>(colIdx, origCols); break;
        case CV_64FC1: dostuff<double>(colIdx, origCols); break;
        // and so on
        default:
    }
}

In this example, the code is rather small, so templates seems like it wouldn't be a bad choice and would give you the polymorphism you want without writing a bunch of redundant code.

Maybe some of these tutorials would give you a better idea:

OpenCV docs: core module tutorials

OpenCV docs: How to scan images

like image 115
Matteo Mannino Avatar answered Oct 01 '22 03:10

Matteo Mannino


There is no native Opencv solution to your problem, and it is a frequent pain with this library. There are three possible solutions:

  1. Always use matrices with the same depth. I guess that's not what you want to hear.
  2. Template the method that calls your code by the type of the elements contained in the matrix: this will only work for single channel matrices, as the template of the .at<> method will have to be something by cv::Point2f for matrices for multiple channels.
  3. Create a "smart" iterator class that will know how to access matrix data based on the matrix depth. Something like:

    class PtrMat
    {
      PtrMat(cv::Mat& mat, int row)
      {
        if(mat.depth() == CV_32F) { _ptr = new PtrFloat(mat, row); }
        else if(mat.depth() == CV_8U) { _ptr = new PtrUchar(mat, row); }
        ...
      }
      Ptr* _ptr
    };
    
    class Ptr
    {
      virtual void set(const float& val)=0;
    };
    class PtrFloat: public Ptr
    {
      PtrFloat(const cv::Mat& mat, int row){ _val = mat.ptr<float>(row); }
      void set(const float& val) { _val = val; }
      float* _val;
    }
    class PtrUchar: public Ptr
    {
      PtrUchar(const cv::Mat& mat, int row){ _val = mat.ptr<uchar>(row); }
      void set(const float& val) { _val = val; }
      uchar* _val;
    }
    

Of course, with the third solution you end up with lots of duplicated code. Float casting can also slow down your loops. No solution is perfect in this case.

like image 25
Régis B. Avatar answered Oct 01 '22 03:10

Régis B.