6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[OpenCV 5予習] 新Mat Depthを確認しよう

Last updated at Posted at 2024-11-30

この記事はOpenCV Advent Calendar 2024の1日目の記事です。
他の記事は目次にまとめられています。

■ ご挨拶

OpenCV Advent Calendarに興味を持って頂き、ありがとうございます!何かの気づき、参考になりましたら幸いに存じます。

また、OpenCV Advent Calendar寄稿者の方も、お忙しい中、ご参加いただき誠にありがとうございます!今後とも何卒よろしくお願いいたします。

■ TL;DR

  • OpenCV5では、CV_16BF(bFloat16)、CV_BoolCV_32UCV_64UそしてCV_64Sが拡張
  • imwrite()するとき非サポート型データは、CV_8Uに丸められるので注意
  • objc bindingがまだできてないかも!

■ 来年2025年は、OpenCV 5元年になりそうです!

(11/24) こちらのマイルストーンを見ると、5.0にalphaリリース計画が!日程はございませんが、そろそろぼちぼちリリースされそうですね!(と言い続けて早何年だろうか)

  • 4.11.0
  • 5.0-alpha
  • 5.0-release

■ Mat DepthサポートがOpenCV5から拡張されます!

OpenCV 5では、Mat Depthが拡張されます!これに伴い、最大チャネル数も若干減ります。このあたりを軽く掘り下げていきます。

〇 Mat depthの変更

ヘッダの場所はこちらでございます。 modules/core/include/opencv2/core/hal/interface.h

https://github.com/opencv/opencv/blob/979428d5908b5396237b18cbd50d0b18265e2463/modules/core/include/opencv2/core/hal/interface.h#L72-L89
https://github.com/opencv/opencv/blob/3fddea2ade81d772fc269efe15dabad4c372e4d3/modules/core/include/opencv2/core/hal/interface.h#L69-L80

CV_CN_MAX(最大チャネル数)が、512から128に減っております。また、CV_CN_SHIFTが3から5に増えて、DEPTHへの割り当てが5bitに増えています。

- #define CV_CN_MAX     512
+ #define CV_CN_MAX     128
- #define CV_CN_SHIFT   3
+ #define CV_CN_SHIFT   5
  #define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)

  #define CV_8U   0
  #define CV_8S   1
  #define CV_16U  2
  #define CV_16S  3
  #define CV_32S  4
  #define CV_32F  5
  #define CV_64F  6
  #define CV_16F  7
+ #define CV_16BF 8
+ #define CV_Bool 9
+ #define CV_64U  10
+ #define CV_64S  11
+ #define CV_32U  12
+ #define CV_DEPTH_CURR_MAX 13

つまり、cv::Mat::type()でCV_8UC1などを取得したときに、OpenCV 4と5とで異なる値になるということですね!

#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE

〇CV_16BF

DNN moduleなどで扱うために、bfloat16型サポートが拡張されました!

このあたりは、昨年、@tomoaki_teshima先生、@fukushima1981先生が深堀して下さっております。

浮動小数表現において、float/doubleと同じ指数部表現にして、仮数部をその分犠牲にした型になります。例えば、float <--> bfloat16の変換もfp16に比べれば早いですね!

一般名称 OpenCV 符号 指数部 仮数部
fp16 CV_16F 1 bit 5 bit 10 bit
bfloat16 CV_16BF 1 bit 8 bit 7 bit
float CV_32F 1 bit 8 bit 23 bit
double CV_64F 1 bit 8 bit 52 bit

どれくらいbfloat16型が扱いやすいかというと、cvdev.h内の変換コードあたりが比較しやすいですかね?なかのロジックを見てもビットシフトなんかもない分、かなり簡略化できています。

CV_16F(hfloat)のための実装107行
class hfloat
{
public:
#if CV_FP16_TYPE
    hfloat() = default;
    operator float() const { return (float)h; }
#if defined __ARM_FP16_FORMAT_IEEE
    explicit hfloat(float x) { h = (__fp16)x; }
protected:
    __fp16 h;
#else
    explicit hfloat(float x) { h = (_Float16)x; }
    explicit operator _Float16() const { return h; }
protected:
    _Float16 h;
#endif


#else
    hfloat() : w(0) {}
    explicit hfloat(float x)
    {
    #if CV_FP16 && CV_AVX2
        __m128 v = _mm_load_ss(&x);
        w = (ushort)_mm_cvtsi128_si32(_mm_cvtps_ph(v, 0));
    #else
        Cv32suf in;
        in.f = x;
        unsigned sign = in.u & 0x80000000;
        in.u ^= sign;


        if( in.u >= 0x47800000 )
            w = (ushort)(in.u > 0x7f800000 ? 0x7e00 : 0x7c00);
        else
        {
            if (in.u < 0x38800000)
            {
                in.f += 0.5f;
                w = (ushort)(in.u - 0x3f000000);
            }
            else
            {
                unsigned t = in.u + 0xc8000fff;
                w = (ushort)((t + ((in.u >> 13) & 1)) >> 13);
            }
        }


        w = (ushort)(w | (sign >> 16));
    #endif
    }


    operator float() const
    {
    #if CV_FP16 && CV_AVX2
        float f;
        _mm_store_ss(&f, _mm_cvtph_ps(_mm_cvtsi32_si128(w)));
        return f;
    #else
        Cv32suf out;


        unsigned t = ((w & 0x7fff) << 13) + 0x38000000;
        unsigned sign = (w & 0x8000) << 16;
        unsigned e = w & 0x7c00;


        out.u = t + (1 << 23);
        out.u = (e >= 0x7c00 ? t + 0x38000000 :
                 e == 0 ? (static_cast<void>(out.f -= 6.103515625e-05f), out.u) : t) | sign;
        return out.f;
    #endif
    }


protected:
    ushort w;


#endif
};


inline hfloat hfloatFromBits(ushort w) {
#if CV_FP16_TYPE
    Cv16suf u;
    u.u = w;
    hfloat res(float(u.h));
    return res;
#else
    Cv32suf out;


    unsigned t = ((w & 0x7fff) << 13) + 0x38000000;
    unsigned sign = (w & 0x8000) << 16;
    unsigned e = w & 0x7c00;


    out.u = t + (1 << 23);
    out.u = (e >= 0x7c00 ? t + 0x38000000 :
            e == 0 ? (static_cast<void>(out.f -= 6.103515625e-05f), out.u) : t) | sign;
    hfloat res(out.f);
    return res;
#endif
}
CV_16BF(bfloat)のための実装26行
class bfloat
{
public:
    bfloat() : w(0) {}


    explicit bfloat(float x)
    {
        Cv32suf in;
        in.f = x;
        w = (ushort)((in.u + (((in.u & 0x7fffffff) <= 0x7f7f7fff) << 15)) >> 16);
    }


    operator float() const
    {
        Cv32suf out;
        out.u = w << 16;
        return out.f;
    }


protected:
    ushort w;
};

〇CV_Bool

真偽情報をそのまま入れる事のできる型ですね。Tensor関係で使っている、らしいでございまする。

〇CV_32U / CV_64U / CV_64S

uint32_tuint64_tint64_t型でございます。
OpenCVは、大きな整数というとint32_tだけだったんですよね…。

■ imwrite()/imencode()でCV_8Uに丸められるかも!

さて、ここでTIPSを1つ。ここは覚えていって欲しいポイントでございます。

imwrite()等の画像書き出し機能では、Encoderがサポートしていない色深度の画像は、CV_8Uに強制変換されます。一応、Warningでるように追加パッチは出しましたけどね…。

        Mat temp;
        if( !encoder->isFormatSupported(image.depth()) )
        {
            CV_LOG_ONCE_WARNING(NULL, "Unsupported depth image for selected encoder is fallbacked to CV_8U.");
            CV_Assert( encoder->isFormatSupported(CV_8U) );
            image.convertTo( temp, CV_8U );
            image = temp;
        }

まだ、TIFF Encoderなんかも新型をサポートしてないので、要注意でございます!
(contributeするチャンスですね!)

はい、https://github.com/opencv/opencv/pull/26508 で、TIFF Encoder/Decoderに、CV_32U, CV_64U, CV_64Sのサポートを追加させていただきましたー!(11/29追記)

objc bindingがまだ非サポートかも? (12/1追記)

記事公開直前に、objcで新depthサポートが足りてない事に気が付いてしまい、issueも発行しておきました。

https://github.com/opencv/opencv/issues/26549

実際にビルドして確認し、issue発行してくれる方がいらっしゃいました!!今後は、こちらでバグ管理になると思います!ありがとうございます!!(ということで、obj-c binding userの方はまだOpenCV5使えないです)(12/2追記)

modules/core/include/opencv2/core/hal/interface.h
#define CV_CN_MAX     128
#define CV_CN_SHIFT   5
#define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)
modules/core/misc/objc/common/CvTypeExt.swift
    static let CV_CN_MAX = 512
    static let CV_CN_SHIFT = 3
    static let CV_DEPTH_MAX = 1 << CV_CN_SHIFT
modules/core/misc/objc/test/MatTestObjc.m
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))

以上となります。ありがとうございました!!

明日は、 @fukushima1981 先生の「OpenCVで使える画像圧縮フォーマットの比較」でございます!よろしくお願いいたします!

6
1
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
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?