Mat
クラスは、実行時にその「タイプ」を変更することができますテンプレートクラス、ではありません。タイプを変更すると便利です。ファイルから読み込むとき。ファンクションパラメータとしてMat
を使用する場合は、テンプレートではなく、テンプレート関数である必要はありません。
ただし、単一の要素(pointers、atまたは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>;
基本的にはいです。 'Mat.type()'で知っているか発見するのはあなたの責任です。 – William
[this](http://stackoverflow.com/questions/15965957/open-cv-generic-mat-function-headers/)も確認してください。 – William
上記のサンプルで私はそのタイプを知っているか、それを発見することができました。次は何? 'CV_64F'はランタイム値ですが、' at'テンプレートパラメータでは使用できません。 –