2013-06-11 6 views
7

at()の要素タイプMatを正しく理解する必要がありますか?私はOpenCVで行列要素にアクセス/修正する方法は?なぜ()はテンプレート化されていますか?

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

を持っている場合たとえば、私は

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

私はどのテンプレート引数を使用する必要があり、その後でない場合は呼び出すことができますか?

なぜMat::atはテンプレート化されていますが、Mat自体はそうではありませんか?

UPDATE

今ここにある別のエラー、とこの質問含まれているサンプルコード:としては、すでに適切にウィリアムによって指摘How to fill Matrix with zeros in OpenCV?

+0

基本的にはいです。 'Mat.type()'で知っているか発見するのはあなたの責任です。 – William

+0

[this](http://stackoverflow.com/questions/15965957/open-cv-generic-mat-function-headers/)も確認してください。 – William

+0

上記のサンプルで私はそのタイプを知っているか、それを発見することができました。次は何? 'CV_64F'はランタイム値ですが、' at'テンプレートパラメータでは使用できません。 –

答えて

6

、あなたがのためのテンプレート引数としてのみ正しい型を提供する必要があります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または他のメソッド呼び出しについては手動で実行してください。

+0

あなたのアイデアを見ています。しかし、 'double 'を使うことは' CV_64F'で私にとってはうまくいきません。これはすべてについてです。私は 'CV_64F'についてのドキュメントを読んでいるので、最初から' double'を試しました。 Plsは私の更新を参照してください。 –

+1

あなたのケースの 'cv :: Mat'コンストラクタの最後の引数は、ユーザ割り当てのデータへのポインタです。画像のデフォルトの塗りつぶしではありません。したがって、あなたの場合、アドレスがゼロのメモリにアクセスしようとしています。それを取り除くとあなたは大丈夫です。あるいは 'cv :: Scalar(0)'に置き換えてください。 – Mikhail

4

よく今あなたのが編集されました投稿が異なります。整流:

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; 
+0

なぜ私は別のコンストラクタを試す必要がありますか?私の問題は何ですか? –

+0

cv構造体を値として使用する必要があります。代わりに 'cvScalar(0)'を使用してください。 – William

+0

@SuzanCioc私はあなたのエラーを修正したことを知っていますが、私はミハイルが提案した素晴らしいスニペットにクレジットを与えることに同意します。それにもかかわらず、あなたのサンプルコードに基づいて質問のタイルが間違っています。 – William

2

Matクラスは、実行時にその「タイプ」を変更することができますテンプレートクラス、ではありません。タイプを変更すると便利です。ファイルから読み込むとき。ファンクションパラメータとしてMatを使用する場合は、テンプレートではなく、テンプレート関数である必要はありません。

ただし、単一の要素(pointersatまたはiterators)にアクセスするには、データ型が必要です。パフォーマンス上の理由からこれが行われていると思います。これは、ランタイム型のシステムと矛盾し、コンパイル時に型が分からない場合に汎用コードを記述するのが難しくなります。それにもかかわらず、あなたは回避策でそれを行うことができます。

最も簡単な方法は、ただの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カスケード(関数run、以下を参照)を保持する関数の戻り値の型としても使用されるためです。ここで

は、「すべて」のチャンネル(1-4)及びタイプまたは指定されたセットをチェックすることができ、呼び出し元のコード、次のとおりです。

Mat img = imread("test.png"); 
int t = img.type(); 

// call functor, check for 1-4 channels and all 7 base types 
bool not_zero = CallFunctor::run(PrintPixel{img}, t); 

// call functor, check only for 1 or 3 channels and 8 bit unsigned int 
CallFunctorRestrictChannelsTo<1,3>::AndBaseTypesTo<CV_8U>::run(PrintPixel{img}, t); 

tがない場合は、後者の呼び出しは、例外がスローされますCV_8UC1またはCV_8UC3。同じ制限を頻繁に使用している場合は、使用の宣言を使用して省略することができます(下のヘッダーファイルの一番下を参照)。

これは使いやすいソリューションで、実行時の値から「作成」されたコンパイル時の値を使用できます。しかし、バックグラウンドでは、if-else-cascadeがすべてのチェックを行っていることを覚えておいてください(チャネルとタイプが指定された順に)。これは、チェックされるチャンネルとタイプの組み合わせごとに、1つの具体的なファンクタクラスが生成されることを意味します。それが大きければ、それは悪いかもしれません。したがって、タイプに依存する部分のみを含める必要があります。また、ファクト・ファンクターでも、テンプレート・クラスをインスタンス化して、非テンプレート・ベースに仮想関数を組み込んでコード・サイズを縮小することもできます。

これは、CvTypeTraitsクラスとテンプレートメタプログラム機能を含むヘッダーファイルの後に続きます。下部には、CallFunctorタイプが実際にタイプとチャンネルの「制限」の略語であることがわかります。あなたはまた、他の制限付きでそれを宣言することもできます。

#pragma once 

#include <cstdint> 
#include <type_traits> 
#include <array> 
#include <opencv2/core/types_c.h> 


template<int> struct BaseType { }; 
template<> struct BaseType<CV_8S> { using base_type = int8_t; }; 
template<> struct BaseType<CV_8U> { using base_type = uint8_t; }; 
template<> struct BaseType<CV_16S> { using base_type = int16_t; }; 
template<> struct BaseType<CV_16U> { using base_type = uint16_t; }; 
template<> struct BaseType<CV_32S> { using base_type = int32_t; }; 
template<> struct BaseType<CV_32F> { using base_type = float; }; 
template<> struct BaseType<CV_64F> { using base_type = double; }; 


template<int t> 
struct CvTypeTraits { 
    constexpr static int channels = t/CV_DEPTH_MAX + 1; 
    using base_type = typename BaseType<t % CV_DEPTH_MAX>::base_type; 
    using array_type = std::array<base_type, channels>; 
}; 


template<int currentChannel, int... otherChannels> 
struct find_chan_impl { 
    template<typename ret_type, int... types> 
    struct find_type_impl { 
     template<class Functor> 
     static inline ret_type run(Functor&& f, int const& c, int const& t) { 
      if (c == currentChannel) 
       return find_chan_impl<currentChannel>::template find_type_impl<ret_type, types...>::run(std::forward<Functor>(f), c, t); 
      else 
       return find_chan_impl<otherChannels...>::template find_type_impl<ret_type, types...>::run(std::forward<Functor>(f), c, t); 
     } 
    }; 
}; 

template<> 
struct find_chan_impl<0> { 
    template<typename ret_type, int... types> 
    struct find_type_impl { 
     template<class Functor> 
     [[noreturn]] static inline ret_type run(Functor&& f, int const& c, int const& t) { 
      throw std::runtime_error("The image has " + std::to_string(c) + " channels, but you did not try to call the functor with this number of channels."); 
     } 
    }; 
}; 

template<int channel> 
struct find_chan_impl<channel> { 
    template<typename ret_type, int currentType, int... otherTypes> 
    struct find_type_impl { 
     static_assert(currentType < CV_DEPTH_MAX, "You can only restrict to base types, without channel specification"); 

     template<class Functor> 
     static inline ret_type run(Functor&& f, int const& c, int const& t) { 
      if (t == currentType) 
       return find_type_impl<ret_type, currentType>::run(std::forward<Functor>(f), c, t); 
      else 
       return find_type_impl<ret_type, otherTypes...>::run(std::forward<Functor>(f), c, t); 
     } 
    }; 

    template<typename ret_type, int type> 
    struct find_type_impl<ret_type, type> { 
     template<class Functor> 
     static inline ret_type run(Functor&& f, int const& c, int const& t) { 
      return f.template operator()<CV_MAKETYPE(type,channel)>(); 
     } 
    }; 

    template<typename ret_type> 
    struct find_type_impl<ret_type, -1> { 
     template<class Functor> 
     [[noreturn]] static inline ret_type run(Functor&& f, int const& c, int const& t) { 
      throw std::runtime_error("The image is of base type " + std::to_string(t) + ", but you did not try to call the functor with this base type."); 
     } 
    }; 
}; 

template<int... channels> 
struct CallFunctorRestrictChannelsTo { 
    template<int firstType, int... types> 
    struct AndBaseTypesTo { 
     template<class Functor> 
     static inline auto run(Functor&& f, int t) -> decltype(f.template operator()<firstType>()) { 
      using functor_ret_type = decltype(f.template operator()<firstType>()); 
      std::div_t d = std::div(t, CV_DEPTH_MAX); 
      int c    = d.quot + 1; 
      int const& base_t = d.rem; 
      return find_chan_impl<channels..., 0>::template find_type_impl<functor_ret_type, firstType, types..., -1>::run(std::forward<Functor>(f), c, base_t); 
     } 
    }; 

    template<class Functor> 
    static inline auto run(Functor&& f, int t) -> decltype(f.template operator()<CV_8S>()) { 
     return AndBaseTypesTo<CV_8S, CV_8U, CV_16S, CV_16U, CV_32S, CV_32F, CV_64F>::run(std::forward<Functor>(f), t); 
    } 
}; 

template<int... types> 
using CallFunctorRestrictBaseTypesTo = CallFunctorRestrictChannelsTo<1,2,3,4>::template AndBaseTypesTo<types...>; 

using CallFunctor = CallFunctorRestrictChannelsTo<1,2,3,4>::template AndBaseTypesTo<CV_8S, CV_8U, CV_16S, CV_16U, CV_32S, CV_32F, CV_64F>; 
関連する問題