OpenCV C ++:当垫子类型未知时,无法访问垫子元素?

时间:2018-09-28 23:52:06

标签: c++ opencv element mat

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. 如果我们正在从数据中学习类型,这是访问mat元素的最快方法吗?如果没有,我该如何改善?

2 个答案:

答案 0 :(得分:1)

OpenCV具有自己的模板特征类名称:DataType。是的,与类不同,您使DataType的模板参数是类型(uchar,...,double, cv::Vec2b,... cv::Vec4d)。 您已经将type()的方法depth()Mat弄混了。方法type()返回类型标志,即类型及其通道(例如CV_8UC3),而方法depth()仅返回类型(例如,如果使用彩色图像{{1 }}。

如果我们正在从数据中学习类型,这是访问mat元素的最快方法吗?如果没有,我该如何改善?

实际上,有几种方法可以在某些OpenCV的cuda模块(例如双边过滤器)中使用,包括创建一个模板函数,该函数接受通用参数并设置一些类型特定的容器并执行一些工作。

例如

CV_8U

注意:上面的代码仅作为示例,可能无法正常工作或无法编译。

该方法在内存方面很有趣,因为您无需转换即可处理数据。 实际上,在上面由于某些溢出问题,您可能希望顺序排列不同于A,B,C的类型的矩阵D。

然而,有一种 // 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); } 元素的更简单的使用方法,这些元素包含转换和整形数据,以简化处理,然后再将其整形再返回。

例如

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); } } )时,由于OpenCV将通道按隔行存储,因此列数乘以通道数。 在某些情况下,可能不重塑一个通道并直接与通道一起使用可能是有用的,然后可以将这两种方法融合在一起。

最后一点是对数据和/或工作类型先使用。 例如

.reshape(1)

答案 1 :(得分:1)

您不能从cv :: Mat对象的类型声明数据类型。

要访问未知类型的元素,一种方法是使用if / switch语句,就像@Kinght金说的那样。您可以在opencv源代码中找到类似的用法,例如cv::fitEllipse

cv::RotatedRect cv::fitEllipse( InputArray _points )
{
    // ...
    bool is_float = depth == CV_32F;
    const Point* ptsi = points.ptr<Point>();
    const Point2f* ptsf = points.ptr<Point2f>();
    // ...
    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        c += p;
    }
    // ...
}

如果您知道代码中cv :: Mat的类型,则另一种方法是使函数模板化:

template <typename T>
void func(const cv::Mat& src)
{
    // ...
    auto val = src.at<T>(row, col);
    // ...
}

func<int>(matrix); // call function with input type CV_32SC1

类型声明和模板参数在编译时进行评估(这就是为什么它们必须是常量表达式,即constexpr)的原因,但是传递给函数的cv :: Mat的类型只能在运行时中确定(可以从文件中读取)。

现在,opencv中的

TypeDepth被标记为已弃用,并且别无选择。也许opencv开发人员发现它完全没用。