Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom defined type for cv::Mat in OpenCV

Tags:

c++

opencv

I want to create an array using cv::Mat with my custom defined type. Here is my code:

typedef struct{
  int x;
  int y;
  float prob;
}CellXY;

void main(){

  cv::Mat_<CellXY> map(10, 10);

  std::cout << "Width = " << map.cols << ", Height = " << map.rows << std::endl;
  std::cout << "### Assignment ###" << std::endl;
  for (int j=0; j<map.rows; j++){
    for (int i=0; i<map.cols; i++){
      map.at<CellXY>(j, i).x = i;
      map.at<CellXY>(j, i).y = j;
      map.at<CellXY>(j, i).prob = (float)(i + j * map.cols);

      std::cout << map.at<CellXY>(j, i).prob 
        << " (" << map.at<CellXY>(j, i).x
        << "," << map.at<CellXY>(j, i).y
        << ")\t";
    }
    std::cout << std::endl;
  }

  std::cout << "### Display ###" << std::endl;
  for (int j=0; j<map.rows; j++){
    for (int i=0; i<map.cols; i++){
      std::cout << map.at<CellXY>(j, i).prob 
        << " (" << map.at<CellXY>(j, i).x
        << "," << map.at<CellXY>(j, i).y
        << ")\t";
    }
    std::cout << std::endl; 
  }
  map.release();
}

However results between "Assignment" and "Display" sections are not the same:

How do I make these results the same?

like image 771
puwanan Avatar asked Nov 02 '25 03:11

puwanan


1 Answers

This is becasue OpenCV doesn't know the DataType of your class. You need to a specialization for it, something like:

typedef struct {
    int x;
    int y;
    float prob;
}CellXY;


// Add this!!!!!!
namespace cv {
template<> class DataType<CellXY>
{
public:
    typedef CellXY      value_type;
    typedef value_type  work_type;
    typedef value_type  channel_type;
    typedef value_type  vec_type;
    enum { generic_type = 0,
        depth        = CV_32F, // Might cause problems... I'm saying it's all made of floats (and not 2 int a 1 float)
        channels     = 3,
        fmt          = (int)'p',
        type         = CV_MAKETYPE(depth, channels)
    };
};
}

void main() {
    ...
}

But, in general, I think it's better to avoid messing up with these implementation details, and just use a better data structure. The recommendation is: use Mat only for primitive types. In fact, you can't use it in other OpenCV functions anyway...

Here a few ways to solve your problem:

  1. Use another data structure: e.g. a std::vector<std::vector<CellXY>> or an "unrolled" std::vector<CellXY>
  2. Make a class CellXYMatrix which deals with storing the CellXY:

    class CellXYMatrix
    {
         std::vector<CellXY> _mat; // your matrix of CellXY
         int _r; // rows
         int _c; // cols
    
    public:
         CellXYMatrix(int rows, int cols) : _r(rows), _c(cols), _mat(rows * cols) {};
    
         void set(int row, int col, const CellXY& cell) {
            _mat[row * _c + col] = cell;
         }
         CellXY& get(int row, int col) {
            return _mat[row * _c + col];
         }  
    }
    

    You can eventually overload operator() to make the access similar to OpenCV Mats.

  3. If the x and y fields in CellXY refer to the matrix position, why do you need them? You can simply have a Mat1f (aka Mat_<float>) for your probabilities, and x,y are the position in the matrix.

like image 123
Miki Avatar answered Nov 03 '25 21:11

Miki