LoginSignup
1
0

今日のコネタ: perf::matType, perf::matDepth

Last updated at Posted at 2023-12-25

TL;DR

  • OpenCVでmat typeに関するテストを書くときは、perf::matType, matDepthが便利だよ、というご紹介。

■ はじめに

誰得、私得の落穂ひろいネタです。

OpenCVの鬼門(?)の一つが、Mat Typeではないでしょうかね?

  • CV_8U / CV_8S / CV_16U / CV_16S / CV_32S / CV_32F / CV_64F / CV_16F (OpenCV4まで)
  • CV_16BF / CV_Bool / CV_64U / CV_64S / CV_32U (OpenCV5から)

例えば新機能などを作ったら、色々なMatDepthでテストもしたい。
場合によっては、チャンネル数も加えたMatTypeが欲しくなる。

そうなるとテストを書くときにどうするか非常に悩ましい。ここを掘り下げていく。

■ ダメな例 tiff

まず、ダメな例を見ていただこう。

https://github.com/opencv/opencv/blob/4.8.1/modules/imgcodecs/test/test_tiff.cpp

なんと汚らわしい!なんと汚らしい !

誰なんだ…? この汚いコードをcommitしたのは…? 誰だ…?誰だ…?誰…? あ… オレだった…!(*ノωノ)

// Re-define Mat type as enum for showing on Google Test.
enum CV_ddtCn{
  _CV_8UC1  = CV_8UC1,  _CV_8UC3  = CV_8UC3,  _CV_8UC4  = CV_8UC4,
  _CV_8SC1  = CV_8SC1,  _CV_8SC3  = CV_8SC3,  _CV_8SC4  = CV_8SC4,
  _CV_16UC1 = CV_16UC1, _CV_16UC3 = CV_16UC3, _CV_16UC4 = CV_16UC4,
  _CV_16SC1 = CV_16SC1, _CV_16SC3 = CV_16SC3, _CV_16SC4 = CV_16SC4,
  _CV_32SC1 = CV_32SC1, _CV_32SC3 = CV_32SC3, _CV_32SC4 = CV_32SC4,
  _CV_16FC1 = CV_16FC1, _CV_16FC3 = CV_16FC3, _CV_16FC4 = CV_16FC4,
  _CV_32FC1 = CV_32FC1, _CV_32FC3 = CV_32FC3, _CV_32FC4 = CV_32FC4,
  _CV_64FC1 = CV_64FC1, _CV_64FC3 = CV_64FC3, _CV_64FC4 = CV_64FC4,
};

static inline
void PrintTo(const CV_ddtCn& val, std::ostream* os)
{
    const int val_type = static_cast<int>(val);

    switch ( CV_MAT_DEPTH(val_type) )
    {
    case CV_8U  : *os << "CV_8U" ; break;
    case CV_16U : *os << "CV_16U" ; break;
    case CV_8S  : *os << "CV_8S" ; break;
    case CV_16S : *os << "CV_16S" ; break;
    case CV_32S : *os << "CV_32S" ; break;
    case CV_16F : *os << "CV_16F" ; break;
    case CV_32F : *os << "CV_32F" ; break;
    case CV_64F : *os << "CV_64F" ; break;
    default     : *os << "CV_???" ; break;
    }
    *os << "C" << CV_MAT_CN(val_type);
}

■ perf::MatDepthのご紹介

人は過ちをする生き物なのです。そこからどう立ち直るのかが重要なのです(?)。
それでは、半年後の熊太郎さんのテストコードを見てみよう

INSTANTIATE_TEST_CASE_P(/* */, Core_Arith_Regression24163,
    testing::Combine(
        testing::Values(perf::MatDepth(CV_8U), CV_8S, CV_16U, CV_16S, CV_32S), // MatType
        testing::Values( 3, 4, 5, 6),    // MatHeight
        testing::Values(-2,-1, 0, 1, 2), // src1
        testing::Values(   -1, 0, 1   )  // src2
    )
);

見慣れない perf::MatDepth() が使われている。これによって、テストを実行したときに、CV_8Uなどが表示されるようになっている!圧倒的大勝利!

じゃあ、これはどこに定義されているのか?というと、tsモジュールになる。

opencv/modules/ts/include/opencv2/ts/ts_perf.hpp

/*****************************************************************************************\
*                MatType - printable wrapper over integer 'type' of Mat                   *
\*****************************************************************************************/
class MatType
{
public:
    MatType(int val=0) : _type(val) {}
    operator int() const {return _type;}


private:
    int _type;
};

/*****************************************************************************************\
*     CV_ENUM and CV_FLAGS - macro to create printable wrappers for defines and enums     *
\*****************************************************************************************/


#define CV_ENUM(class_name, ...)                                                        \
    namespace {                                                                         \
    using namespace cv;using namespace cv::cuda; using namespace cv::ocl;               \
    struct class_name {                                                                 \
        class_name(int val = 0) : val_(val) {}                                          \
        operator int() const { return val_; }                                           \
        void PrintTo(std::ostream* os) const {                                          \
            const int vals[] = { __VA_ARGS__ };                                         \
            const char* svals = #__VA_ARGS__;                                           \
            for(int i = 0, pos = 0; i < (int)(sizeof(vals)/sizeof(int)); ++i) {         \
                while(isspace(svals[pos]) || svals[pos] == ',') ++pos;                  \
                int start = pos;                                                        \
                while(!(isspace(svals[pos]) || svals[pos] == ',' || svals[pos] == 0))   \
                    ++pos;                                                              \
                if (val_ == vals[i]) {                                                  \
                    *os << std::string(svals + start, svals + pos);                     \
                    return;                                                             \
                }                                                                       \
            }                                                                           \
            *os << "UNKNOWN";                                                           \
        }                                                                               \
        static ::testing::internal::ParamGenerator<class_name> all() {                  \
            const class_name vals[] = { __VA_ARGS__ };                                  \
            return ::testing::ValuesIn(vals);                                           \
        }                                                                               \
    private: int val_;                                                                  \
    };                                                                                  \
    static inline void PrintTo(const class_name& t, std::ostream* os) { t.PrintTo(os); } }

CV_ENUM(MatDepth, CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F, CV_16F)

なかなか面白いコードになっていて、 "CV_8U, CV_8S...”という指定を_VA_ARGS_から配列に移し入れ、指定されたら該当する部分を検索して出力するとなっている。

これを使えば、google testのValues()指定なども活用できます、やったー!!!

まとめ

  • OpenCVでmat typeに関するテストを書くときは、perf::matType, matDepthが便利だよ、というご紹介。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0