基本構造体 =============== .. highlight:: cpp DataType -------- OpenCV のその他のプリミティブなデータ型に対する「traits」クラステンプレート :: template class DataType { // value_type は常に _Tp と等しくなります. typedef _Tp value_type; // _Tp の処理で利用される中間型です. // uchar, signed char, unsigned char, signed short, int に対しては int , // float に対しては float , double に対しては double , ... typedef <...> work_type; // マルチチャンネルデータの場合,これは各チャンネルのデータ型になります. typedef <...> channel_type; enum { // CV_8U ... CV_64F depth = DataDepth::value, // 1 ... channels = <...>, // '1u', '4i', '3f', '2d' など. fmt=<...>, // CV_8UC3, CV_32FC2 ... type = CV_MAKETYPE(depth, channels) }; }; .. クラステンプレート ``DataType`` は,OpenCV の基本的なデータ型や以下の定義に従うその他の型に対する説明的なクラスです. OpenCV の基本的なデータ型は, ``unsigned char, bool, unsigned char, signed char, unsigned short, signed short, int, float, double`` の1つ,あるいは,1つの型の値のタプルで,その場合タプル内の値は全て同じ型になります. OpenCV の :ref:`CvMat` の表記法 CV _ 8U ... CV _ 32FC3, CV _ 64FC2 に慣れているならば, 基本的な型は ``CV_{U|S|F}C`` の形式で一意な識別子を与えることができるもの,として定義できます. :ref:`Vec` は,この基本的なデータ型の1つのインスタンスを格納できる OpenCV の汎用的な構造です. また, ``std::vector`` , ``Mat`` , ``Mat_`` , ``MatND`` , ``MatND_`` , ``SparseMat`` , ``SparseMat_`` や, :func:`Vec` のインスタンスを格納できるその他のコンテナは,このような型の複数のインスタンスを格納できます. ``DataType`` クラスは基本的に,対象となるクラスにフィールドやメソッドを追加せずに(C/C++ の基本的なデータ型に何かを追加するのは実際に不可能),このような基本的なデータ型の説明を提供するために利用されます.このテクニックは,C++ では traits クラスとして知られています.使われるのは ``DataType`` 自身ではなく,これを特殊化したものです.例えば: :: template<> class DataType { typedef uchar value_type; typedef int work_type; typedef uchar channel_type; enum { channel_type = CV_8U, channels = 1, fmt='u', type = CV_8U }; }; ... template DataType > { typedef std::complex<_Tp> value_type; typedef std::complex<_Tp> work_type; typedef _Tp channel_type; // DataDepth は別の traits クラス enum { depth = DataDepth<_Tp>::value, channels=2, fmt=(channels-1)*256+DataDepth<_Tp>::fmt, type=CV_MAKETYPE(depth, channels) }; }; ... .. このクラスの主な目的は,コンパイル時の型情報を OpenCV と互換性のあるデータ型識別子に変換することです.例えば: :: // 30x40 の浮動小数点型行列を確保します. Mat A(30, 40, DataType::type); Mat B = Mat_ >(3, 3); // 以下の文は, 6, 2 を表示します /* つまり, depth == CV_64F, channels == 2 */ cout << B.depth() << ", " << B.channels() << endl; .. つまり,この traits クラスは,ユーザがどんなデータ型を使用しているかを,たとえそのデータ型が OpenCV の組み込み型ではないとしても,OpenCV に教えるために利用されます (OpenCV は適切に特殊化されたクラステンプレート ``DataType >`` を定義するので,上述の行列 ``B`` の初期化はコンパイルされます)また,この機構は generic algorithm の実装に役立ちます(OpenCV内でもそのように使われています). Point\_ ------- 2次元座標上の点のためのクラステンプレート. :: template class Point_ { public: typedef _Tp value_type; Point_(); Point_(_Tp _x, _Tp _y); Point_(const Point_& pt); Point_(const CvPoint& pt); Point_(const CvPoint2D32f& pt); Point_(const Size_<_Tp>& sz); Point_(const Vec<_Tp, 2>& v); Point_& operator = (const Point_& pt); template operator Point_<_Tp2>() const; operator CvPoint() const; operator CvPoint2D32f() const; operator Vec<_Tp, 2>() const; // 内積を計算します. (this->x*pt.x + this->y*pt.y) _Tp dot(const Point_& pt) const; // 倍精度の演算を用いて内積を計算します. double ddot(const Point_& pt) const; // この点が矩形 "r" 内に入って入れば真値を返します. bool inside(const Rect_<_Tp>& r) const; _Tp x, y; }; .. このクラスは,その座標値 :math:`x` と :math:`y` によって指定される2次元の点を表現します. このクラスのインスタンスは,Cの構造体 ``CvPoint`` や ``CvPoint2D32f`` と交換可能です.また,点の座標を指定の型に変換するためのキャスト演算子も存在します.浮動小数点型座標から整数座標への変換は,丸めることで行われます.通常は,それぞれの座標に対して 演算を行います.上述の宣言にあるクラスメンバに加えて,座標点に対する以下の処理が実装されています: :: pt1 = pt2 + pt3; pt1 = pt2 - pt3; pt1 = pt2 * a; pt1 = a * pt2; pt1 += pt2; pt1 -= pt2; pt1 *= a; double value = norm(pt); // L2 norm pt1 == pt2; pt1 != pt2; .. 利便性のために,以下のように型の別名が定義されています: :: typedef Point_ Point2i; typedef Point2i Point; typedef Point_ Point2f; typedef Point_ Point2d; .. ここでは,短い例を示します: :: Point2f a(0.3f, 0.f), b(0.f, 0.4f); Point pt = (a + b)*10.f; cout << pt.x << ", " << pt.y << endl; .. Point3\_ -------- 3次元座標上の点のためのクラステンプレート. :: template class Point3_ { public: typedef _Tp value_type; Point3_(); Point3_(_Tp _x, _Tp _y, _Tp _z); Point3_(const Point3_& pt); explicit Point3_(const Point_<_Tp>& pt); Point3_(const CvPoint3D32f& pt); Point3_(const Vec<_Tp, 3>& v); Point3_& operator = (const Point3_& pt); template operator Point3_<_Tp2>() const; operator CvPoint3D32f() const; operator Vec<_Tp, 3>() const; _Tp dot(const Point3_& pt) const; double ddot(const Point3_& pt) const; _Tp x, y, z; }; .. このクラスは,その座標値 :math:`x` , :math:`y` , :math:`z` によって指定される3次元の点を表現します. このクラスのインスタンスは,Cの構造体 ``CvPoint2D32f`` と交換可能です. ``Point_`` の場合と同様に,3次元点の座標を別の型に変換する事が可能で,さらにベクトル計算や比較演算もサポートされています. 以下の型の別名が利用可能です: :: typedef Point3_ Point3i; typedef Point3_ Point3f; typedef Point3_ Point3d; .. Size\_ ------ 画像や矩形のサイズを表現するためのクラステンプレート. :: template class Size_ { public: typedef _Tp value_type; Size_(); Size_(_Tp _width, _Tp _height); Size_(const Size_& sz); Size_(const CvSize& sz); Size_(const CvSize2D32f& sz); Size_(const Point_<_Tp>& pt); Size_& operator = (const Size_& sz); _Tp area() const; operator Size_() const; operator Size_() const; operator Size_() const; operator CvSize() const; operator CvSize2D32f() const; _Tp width, height; }; .. ``Size_`` クラスは,2つのメンバが ``x`` と ``y`` ではなく ``width`` と ``height`` という名前になっていることを除けば, ``Point_`` と同じです.この構造は,古い OpenCV の構造体 :ref:`CvSize` や :ref:`CvSize2D32f` に対して相互に変換可能です.また,このクラスでも ``Point_`` に対する計算や比較演算と同様のものが利用できます. OpenCVでは,型の別名を以下の用に定義しています: :: typedef Size_ Size2i; typedef Size2i Size; typedef Size_ Size2f; .. Rect\_ ------ 2次元の矩形を表現するためのクラステンプレート. :: template class Rect_ { public: typedef _Tp value_type; Rect_(); Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height); Rect_(const Rect_& r); Rect_(const CvRect& r); // (x, y) <- org, (width, height) <- sz Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz); // (x, y) <- min(pt1, pt2), (width, height) <- max(pt1, pt2) - (x, y) Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2); Rect_& operator = ( const Rect_& r ); // Point_<_Tp>(x, y) を返します Point_<_Tp> tl() const; // Point_<_Tp>(x+width, y+height) を返します Point_<_Tp> br() const; // Size_<_Tp>(width, height) を返します Size_<_Tp> size() const; // width*height を返します _Tp area() const; operator Rect_() const; operator Rect_() const; operator Rect_() const; operator CvRect() const; // x <= pt.x && pt.x < x + width && // y <= pt.y && pt.y < y + height ? true : false bool contains(const Point_<_Tp>& pt) const; _Tp x, y, width, height; }; .. 矩形は,左上コーナの座標(OpenCV ではデフォルトで, ``Rect_::x`` と ``Rect_::y`` を左上コーナーと解釈しますが,ユーザのアルゴリズムによっては ``x`` と ``y`` を左下のコーナから数えるかもしれません)と,矩形の幅と高さによって表現されます. OpenCV におけるもう1つの前提は,矩形の上側と左側の境界線は矩形に含まれますが,右側と下側の境界線はそうではないということです.例えば,以下のような場合, ``Rect_::contains`` メソッドは true を返します. .. math:: x \leq pt.x < x+width, y \leq pt.y < y+height OpenCV では,画像の :ref:`ROI` (ROIは ``Rect_`` によって指定されます)を処理するループは以下のように実装されます: :: for(int y = roi.y; y < roi.y + rect.height; y++) for(int x = roi.x; x < roi.x + rect.width; x++) { // ... } .. クラスのメンバに加えて,矩形に対する以下の処理が実装されています: * :math:`\texttt{rect} = \texttt{rect} \pm \texttt{point}` (あるオフセット分だけ矩形を移動する) * :math:`\texttt{rect} = \texttt{rect} \pm \texttt{size}` (ある量だけ矩形を縮小あるいは拡大する) * ``rect += point, rect -= point, rect += size, rect -= size`` (複合代入) * ``rect = rect1 & rect2`` (矩形の共通部分) * ``rect = rect1 | rect2`` ( ``rect2`` と ``rect3`` を含む最小面積の矩形) * ``rect &= rect1, rect |= rect1`` (上記に相当する複合代入) * ``rect == rect1, rect != rect1`` (矩形の比較) 例.ここでは,矩形での partial ordering がどのように行われるかを示します(rect1 :math:`\subseteq` rect2): :: template inline bool operator <= (const Rect_<_Tp>& r1, const Rect_<_Tp>& r2) { return (r1 & r2) == r1; } .. 利便性のため,以下のような型の別名が利用可能です: :: typedef Rect_ Rect; .. RotatedRect ----------- 回転を考慮した矩形. :: class RotatedRect { public: // コンストラクタ RotatedRect(); RotatedRect(const Point2f& _center, const Size2f& _size, float _angle); RotatedRect(const CvBox2D& box); // 回転した矩形を包含する,回転していない最小の矩形を返します. Rect boundingRect() const; // CvBox2D に対する後方互換性 operator CvBox2D() const; // 矩形の重心 Point2f center; // サイズ Size2f size; // degree で表現される回転角度 float angle; }; .. ``RotatedRect`` クラスは,古い :ref:`CvBox2D` を置き換え,それと完全な互換性を保ちます. TermCriteria ------------ 反復アルゴリズムの停止基準. :: class TermCriteria { public: enum { COUNT=1, MAX_ITER=COUNT, EPS=2 }; // コンストラクタ TermCriteria(); // type は, MAX_ITER, EPS, MAX_ITER+EPS の内の1つです. // type = MAX_ITER の場合は,反復回数だけが問題になります. // type = EPS の場合は,要求精度(イプシロン)だけが問題になります. // (もっとも,大抵のアルゴリズムでは反復回数に何らかの制限を設けますが) // type = MAX_ITER + EPS の場合は,反復回数が既定値に達するか, // あるいは,要求精度が達成された場合にアルゴリズムが停止します. TermCriteria(int _type, int _maxCount, double _epsilon); TermCriteria(const CvTermCriteria& criteria); operator CvTermCriteria() const; int type; int maxCount; double epsilon; }; .. ``TermCriteria`` は,古い :ref:`CvTermCriteria` を置き換え,それと完全な互換性を保ちます. Vec --- 短い数値ベクトルのためのクラステンプレート. :: template class Vec { public: typedef _Tp value_type; enum { depth = DataDepth<_Tp>::value, channels = cn, type = CV_MAKETYPE(depth, channels) }; // デフォルトコンストラクタ:全ての要素は0にセットされます. Vec(); // 最初の 10 要素までをパラメータにとるコンストラクタです. Vec(_Tp v0); Vec(_Tp v0, _Tp v1); Vec(_Tp v0, _Tp v1, _Tp v2); ... Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); Vec(const Vec<_Tp, cn>& v); // ベクトルの全要素を alpha にします. static Vec all(_Tp alpha); // 2 種類の内積 _Tp dot(const Vec& v) const; double ddot(const Vec& v) const; // 外積. cn == 3 の場合のみ有効です. Vec cross(const Vec& v) const; // 要素型の変換 template operator Vec() const; // CvScalar との相互変換( cn==4 の場合のみ有効です) operator CvScalar() const; // 要素アクセス _Tp operator [](int i) const; _Tp& operator[](int i); _Tp val[cn]; }; .. このクラスは,短い数値ベクトルまたはタプルの汎用表現です. ``Vec`` と ``Point_`` , ``Vec`` と ``Point3_`` , ``Vec`` と :ref:`CvScalar` の相互変換が可能です. ``Vec`` の要素には ``operator[]`` を用いてアクセスすることができます.期待されるベクトル演算もすべて実装されています: * ``v1 =`` :math:`v2 \pm v3` ``, v1 = v2 *`` :math:`\alpha` ``, v1 =`` :math:`\alpha` ``* v2`` (さらに,それぞれに相当する複合代入演算.これらの操作は,計算結果のベクトル要素に を適用することに注意してください) * ``v1 == v2, v1 != v2`` * ``double n = norm(v1); //`` :math:`L_2` -norm 利便性のため,以下の型の別名が導入されています: :: typedef Vec Vec2b; typedef Vec Vec3b; typedef Vec Vec4b; typedef Vec Vec2s; typedef Vec Vec3s; typedef Vec Vec4s; typedef Vec Vec2i; typedef Vec Vec3i; typedef Vec Vec4i; typedef Vec Vec2f; typedef Vec Vec3f; typedef Vec Vec4f; typedef Vec Vec6f; typedef Vec Vec2d; typedef Vec Vec3d; typedef Vec Vec4d; typedef Vec Vec6d; .. ``Vec`` クラスは,様々な数値オブジェクトを宣言するために利用可能です.例えば, ``Vec`` は,3x3 の倍精度行列を格納するために利用できます.また,マルチチャンネル配列を宣言,処理する際にも便利です.詳しくは, ``Mat_`` の説明を参照してください. Scalar\_ -------- 4-要素ベクトル. :: template class Scalar_ : public Vec<_Tp, 4> { public: Scalar_(); Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0); Scalar_(const CvScalar& s); Scalar_(_Tp v0); static Scalar_<_Tp> all(_Tp v0); operator CvScalar() const; template operator Scalar_() const; Scalar_<_Tp> mul(const Scalar_<_Tp>& t, double scale=1 ) const; template void convertTo(T2* buf, int channels, int unroll_to=0) const; }; typedef Scalar_ Scalar; .. クラステンプレート ``Scalar_`` および,その倍精度表現のインスタンス ``Scalar`` は,4要素のベクトルを表します.これらは ``Vec<_Tp, 4>`` から派生しているため,典型的な4-要素ベクトルとして利用でき,さらに ``CvScalar`` と相互に変換可能です. ``Scalar`` 型は,OpenCV でピクセル値を処理するために広く利用されており,以前のOpenCVで同様に利用されていた :ref:`CvScalar` を手軽に代替することができます. Range ----- シーケンス内の,連続した部分シーケンス(別名,スライス)を表す. :: class Range { public: Range(); Range(int _start, int _end); Range(const CvSlice& slice); int size() const; bool empty() const; static Range all(); operator CvSlice() const; int start, end; }; .. このクラスは,行列( :func:`Mat` )の行または列の区間を指定するため,またその他多くの目的で利用されます. ``Range(a,b)`` は基本的に, Matlab の ``a:b`` や Python の ``a..b`` などと等しいものです. Python での表現と同様に, ``start`` は範囲に含まれる左側の境界, ``end`` は範囲に含まれない右側の境界です.このような半開区間は,通常 :math:`[start,end)` と表記されます. スタティックメソッド ``Range::all()`` は,丁度 Matlab での " ``:`` " や Python(numpy) での " ``...`` " と同じように「シーケンス全体」あるいは「範囲全体」を意味する特別な変数を返します. ``Range`` を扱う OpenCV のすべてのメソッドおよび関数は,この特別な値 ``Range::all()`` をサポートします.しかしもちろん,ユーザ独自の処理では明示的にそれをチェックして対処する必要があります: :: void my_function(..., const Range& r, ....) { if(r == Range::all()) { // 全データを処理 } else { // [r.start, r.end) を処理 } } .. Ptr --- 参照カウント方式のスマートポインタクラステンプレート. :: template class Ptr { public: // デフォルトコンストラクタ Ptr(); // オブジェクトポインタをラップするコンストラクタ Ptr(_Tp* _obj); // デストラクタ: release() を呼び出します ~Ptr(); // コピーコンストラクタ. ptr の参照カウントをインクリメントします. Ptr(const Ptr& ptr); // 代入演算子.( release() によって)自分の参照カウントをデクリメントし, // ptr の参照カウンタをインクリメントします. Ptr& operator = (const Ptr& ptr); // 参照カウンタをインクリメントします. void addref(); // 参照カウンタをデクリメントします. 0 になった場合, // delete_obj() が呼ばれます. void release(); // ユーザ指定のカスタムオブジェクト削除処理. // デフォルトでは "delete obj" が呼ばれます. void delete_obj(); // obj == 0 の場合 true を返します. bool empty() const; // オブジェクトのフィールドとメソッドにアクセスする手段を提供します. _Tp* operator -> (); const _Tp* operator -> () const; // 内部のオブジェクトポインタを返します. // このメソッドのおかげで, _Tp* の代わりに // Ptr<_Tp> を利用できます. operator _Tp* (); operator const _Tp*() const; protected: // カプセル化されたオブジェクトポインタ _Tp* obj; // 参照カウンタ int* refcount; }; .. ``Ptr<_Tp>`` クラスは,対応する型へのポインタをラップするクラステンプレートです. これは,Boost ライブラリ( http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/shared_ptr.htm )や, `C++0x `_ 標準にある ``shared_ptr`` と同様のものです. このクラスを利用すると,以下のような事が可能になります: * C++ のクラスや C の構造体に対するデフォルトコンストラクタ,コピーコンストラクタ,代入演算子が実現できます.ファイルやウィンドウ,ミューテックス,ソケットなどのようなオブジェクトに対して,コピーコンストラクタや代入演算子を定義するのは難しいです.一方で,OpenCVの複雑な分類器のようなオブジェクトに対しては,コピーコンストラクタが存在せず,実装するのも簡単ではありません.また,OpenCV やユーザ自身のデータ構造体の中には C で書かれたものもあるでしょう.しかし,コピーコンストラクタやデフォルトコンストラクタは,プログラミングを非常に簡単にしてます.さらに,(例えば,STL のコンテナなどで)必要になる事も多いです.このような複雑なオブジェクト ``TObj`` へのポインタを ``Ptr`` という風にラッピングする事で,必要とするすべてのコンストラクタや割り当て演算子を自動的に得ることができます. * 上述の処理はデータサイズに関係なく非常に高速に,つまり "O(1)" の定数時間で行われます.実際, ``std::vector`` などの構造体はコピーコンストラクタと割り当て演算子を提供しますが,データ構造体が大きい場合この処理には無視できない時間がかかります.しかし,その構造体が ``Ptr<>`` でラッピングされていれば,データサイズとは関係なく小さいオーバーヘッドで済みます. * C の構造体でも自動的にデストラクションが可能になります.以下の ``FILE*`` を用いた例を参照してください. * 異種オブジェクトのコレクションが実現できます.標準の STL,他の多くの C++ や OpenCV のコンテナは,同じ型,同じサイズのオブジェクトしか格納することができません.異なる型のオブジェクトを同じコンテナに収める古典的な方法は,代わりに基底クラス ``base_class_t*`` のポインタを格納することですが,それは自動的なメモリ管理を諦めることになります.ここで再び,単純なポインタの代わりに ``Ptr()`` を利用することで,この問題を解決できます. ``Ptr`` クラスは,ラッピングされたオブジェクトをブラックボックスとして扱います.参照カウンタは個別に確保,管理されます.ポインタクラスが知っておく必要があるのは,オブジェクトの解放の方法だけです.それは, ``Ptr::delete_obj()`` メソッドにカプセル化され,参照カウンタが 0 になったときに呼ばれます.このメソッドはデフォルトで ``delete obj;`` を呼ぶので,オブジェクトが C++ のクラスインスタンスである場合は,追加のコーディングは必要ありません. しかし,オブジェクトが別の方法で解放される場合は,専用のメソッドを作る必要があります.例えば, ``FILE`` をラッピングしたい場合は, ``delete_obj`` を以下のような感じで実装します: :: template<> inline void Ptr::delete_obj() { fclose(obj); // さらにポインタをクリアする必要はありません. // その処理,この外側で行われます. } ... // 使い方 Ptr f(fopen("myfile.txt", "r")); if(f.empty()) throw ...; fprintf(f, ....); ... // Ptr のデストラクタによって, FILE は自動的に閉じられます. .. **注意** :参照のインクリメント/デクリメント処理は,不可分な処理として実装されているので,このクラスをマルチスレッドアプリケーションで利用しても通常は安全です.また,参照カウンタを利用する :func:`Mat` やその他の OpenCV のクラスも同様です. Mat --- OpenCV の C++ 行列クラス. :: class CV_EXPORTS Mat { public: // コンストラクタ Mat(); // 指定したサイズ,型の行列を作成します. // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) など) Mat(int _rows, int _cols, int _type); Mat(Size _size, int _type); // 行列を作成して,指定した値 _s で埋めます. Mat(int _rows, int _cols, int _type, const Scalar& _s); Mat(Size _size, int _type, const Scalar& _s); // コピーコンストラクタ Mat(const Mat& m); // ユーザが確保したデータを指す行列ヘッダのコンストラクタ Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP); Mat(Size _size, int _type, void* _data, size_t _step=AUTO_STEP); // より大きな行列の部分行列を作成します. Mat(const Mat& m, const Range& rowRange, const Range& colRange); Mat(const Mat& m, const Rect& roi); // 古い形式の CvMat を新しい行列に変換します.デフォルトではデータはコピーされません. Mat(const CvMat* m, bool copyData=false); // 古い形式の IplImage を新しい行列に変換します.デフォルトではデータはコピーされません. Mat(const IplImage* img, bool copyData=false); // データコピーの有無を選択して, std::vector から行列を作成します. template explicit Mat(const vector<_Tp>& vec, bool copyData=false); // 行列表現 をコンパイルするための補助コンストラクタ Mat(const MatExpr_Base& expr); // デストラクタ - release() を呼び出します. ~Mat(); // 代入演算子 Mat& operator = (const Mat& m); Mat& operator = (const MatExpr_Base& expr); operator MatExpr_() const; // 指定した行に対する新しい行列ヘッダを返します. Mat row(int y) const; // 指定した列に対する新しい行列ヘッダを返します. Mat col(int x) const; // 指定した複数行に対する新しい行列ヘッダを返します. Mat rowRange(int startrow, int endrow) const; Mat rowRange(const Range& r) const; // 指定した複数列に対する新しい行列ヘッダを返します. Mat colRange(int startcol, int endcol) const; Mat colRange(const Range& r) const; // 指定した対角成分に対する新しい行列ヘッダを返します. // (d=0 - 主対角成分, // >0 - 主対角成分の下側の対角成分, // <0 - 主対角成分の上側の対角成分 ) Mat diag(int d=0) const; // 対角成分が "d" の正方対角行列を作成します. static Mat diag(const Mat& d); // 行列の深いコピーを返します.つまり,データがコピーされます. Mat clone() const; // 行列の内容を "m" にコピーします. // これは, m.create(this->size(), this->type()) を呼び出します. void copyTo( Mat& m ) const; // 非 0 のマスク要素に対応する行列要素を "m" にコピーします. void copyTo( Mat& m, const Mat& mask ) const; // 行列をスケーリングして別のデータ型に変換します. cvConvertScale を参照してください. void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 ) const; void assignTo( Mat& m, int type=-1 ) const; // 全ての行列要素を s にします. Mat& operator = (const Scalar& s); // マスクに従って,行列要素を s にします. Mat& setTo(const Scalar& s, const Mat& mask=Mat()); // データは同じでチャンネル数や行数が異なる,別の行列ヘッダを作成します. // cvReshape を参照してください. Mat reshape(int _cn, int _rows=0) const; // 行列表現 を利用した転置行列 MatExpr_ >, Mat> t() const; // 行列表現 を利用した逆行列 MatExpr_ >, Mat> inv(int method=DECOMP_LU) const; MatExpr_ >, Mat> // 行列表現 を利用した要素毎の乗算 mul(const Mat& m, double scale=1) const; MatExpr_ >, Mat> mul(const MatExpr_ >, Mat>& m, double scale=1) const; MatExpr_ >, Mat> mul(const MatExpr_ >, Mat>& m, double scale=1) const; // 2 つの 3 次元ベクトルの外積 Mat cross(const Mat& m) const; // 内積 double dot(const Mat& m) const; // Matlab 形式の行列の初期化. static MatExpr_Initializer zeros(int rows, int cols, int type); static MatExpr_Initializer zeros(Size size, int type); static MatExpr_Initializer ones(int rows, int cols, int type); static MatExpr_Initializer ones(Size size, int type); static MatExpr_Initializer eye(int rows, int cols, int type); static MatExpr_Initializer eye(Size size, int type); // 行列データを新たに確保します.ただし,現在のデータが既に,指定されたサイズ,型である場合は除きます. // 必要ならば,以前のデータは参照されていない状態になります. void create(int _rows, int _cols, int _type); void create(Size _size, int _type); // 参照カウンタを増やします.メモリリークに注意して使用してください. void addref(); // 参照カウンタを減らします. // 参照カウンタが 0 になると,データは解放されます. void release(); // 親行列の内部に行列 ROI を作成します.以下を参照してください. void locateROI( Size& wholeSize, Point& ofs ) const; // 親行列内部の行列 ROI の移動やサイズ変更を行います. Mat& adjustROI( int dtop, int dbottom, int dleft, int dright ); // 矩形の部分行列を取り出します. // (これは, row, rowRange などを一般化したものです) Mat operator()( Range rowRange, Range colRange ) const; Mat operator()( const Rect& roi ) const; // ヘッダを CvMat に変換します.データはコピーされません. operator CvMat() const; // ヘッダを IplImage に変換します;データはコピーされません. operator IplImage() const; // 行列データが連続である(つまり,行間にギャップが存在しない) // 場合のみ, true を返します. // CV_IS_MAT_CONT(cvmat->type) と等価です. bool isContinuous() const; // CV_ELEM_SIZE(cvmat->type) と同様に, // バイト単位で表された要素サイズを返します. size_t elemSize() const; // returns the size of element channel in bytes. size_t elemSize1() const; // CV_MAT_TYPE(cvmat->type) と同様に,要素の型を返します. int type() const; // CV_MAT_DEPTH(cvmat->type) と同様に,要素のビット深度を返します. int depth() const; // CV_MAT_DEPTH(cvmat->type) と同様に,要素のチャンネル数を返します. int channels() const; // step/elemSize1() を返します. size_t step1() const; // 行列サイズを返します: // width == 列数, height == 行数 Size size() const; // 行列データが NULL の場合に true を返します. bool empty() const; // y 番目の行へのポインタを返します. uchar* ptr(int y=0); const uchar* ptr(int y=0) const; // 上述のメソッドのテンプレート版です. template _Tp* ptr(int y=0); template const _Tp* ptr(int y=0) const; // 読み書き,あるいは読み込みのみ,の要素アクセスを行うテンプレートメソッド // _Tp は,実際の要素型と一致しなければならないことに注意してください - // この関数は,処理中にいかなる型の変換も行いません. template _Tp& at(int y, int x); template _Tp& at(Point pt); template const _Tp& at(int y, int x) const; template const _Tp& at(Point pt) const; // 行列要素を横断するテンプレートメソッド // イテレータは,(もしあれば)行末のギャップをスキップします. template MatIterator_<_Tp> begin(); template MatIterator_<_Tp> end(); template MatConstIterator_<_Tp> begin() const; template MatConstIterator_<_Tp> end() const; enum { MAGIC_VAL=0x42FF0000, AUTO_STEP=0, CONTINUOUS_FLAG=CV_MAT_CONT_FLAG }; // 数種類のビットフィールドを含みます: // * マジックシグネチャ // * 連続性フラグ // * ビット深度 // * チャンネル数 int flags; // 行数,列数 int rows, cols; // バイト単位で表された,隣り合う行と行の距離.もしあれば,ギャップも含みます. size_t step; // データへのポインタ uchar* data; // 参照カウンタへのポインタ // 行列データがユーザが確保したものであれば,このポインタは NULL です. int* refcount; // locateROI と adjustROI の内部で利用される補助フィールド uchar* datastart; uchar* dataend; }; .. ``Mat`` クラスは,行列,画像,オプティカルフローマップなどとして振る舞う(そして,行列として参照される)2次元の数値配列を表現します.これは,前バージョンの OpenCV の :ref:`CvMat` 型と非常によく似ています.また, :ref:`CvMat` と同様にマルチチャンネルですが, :ref:`IplImage` と同様に :ref:`ROI` を完全にサポートしています. ``Mat`` オブジェクトを作成する方法はたくさんありますが,ここではポピュラーなものをいくつか示します: * ``create(nrows, ncols, type)`` メソッドや,それと同様のコンストラクタ ``Mat(nrows, ncols, type[, fill_value])`` を利用します. 指定されたサイズ,型の新しい行列が確保されます. ここで ``type`` は, :ref:`cvCreateMat` の場合と同じ意味を持ちます. 例えば, ``CV_8UC1`` は8ビットでシングルチャンネルの行列, ``CV_32FC2`` は2チャンネル(つまり複素数)浮動小数点型の行列を表します. :: // 1+3j で埋められた 7x7 の複素行列を作成します. cv::Mat M(7,7,CV_32FC2,Scalar(1,3)); // そして,M を 100x60 15 チャンネル 8 ビットの行列に変更します. // 行列の以前の内容は解放されます. M.create(100,60,CV_8UC(15)); .. この章のイントロダクションでも述べたように, ``create()`` は,現在の行列の次元や型が指定されたものと異なる場合のみ,新しい行列を確保します. * コンストラクタや代入演算子を利用します.この場合,右辺は行列か matrix expression になります.詳しくは以下を参照してください.同じくイントロダクションで述べたように,行列の代入はヘッダをコピーして参照カウンタを増加させるだけなので O(1) の処理です.必要なときには ``Mat::clone()`` メソッドを使って,行列の完全なコピー(つまり,深いコピー)を得ることができます. * 別の行列の一部分に対するヘッダを作成します.これは,行列の1行や1列,複数行や複数列,矩形領域(代数では小行列式と呼ばれます),対角要素などに対して可能です.新しいヘッダも同じデータを参照するので,この様な処理も O(1) です.この特徴を利用すると,例えば以下のように,行列の一部を実際に変更することができます. :: // 5 行目 を 3 倍して, 3 行目に足します. M.row(3) = M.row(3) + M.row(5)*3; // 7 列目を 1 列目にコピーします. // M.col(1) = M.col(7); // これは動作しません Mat M1 = M.col(1); M.col(7).copyTo(M1); // 新たに 320x240 の画像を作成します. cv::Mat img(Size(320,240),CV_8UC3); // ROI を選択します. cv::Mat roi(img, Rect(10,10,100,100)); // ROI を (0,255,0) ( RGB 空間での緑)で埋めます. // 320x240 の元の画像は変更されます. roi = Scalar(0,255,0); .. ``datastart`` および ``dataend`` メソッドが追加されたおかげで, ``locateROI()`` を用いてメインの *"コンテナ"* 行列内の部分行列の相対位置を計算することができます. :: Mat A = Mat::eye(10, 10, CV_32S); // 1 列目(範囲に含む)から 3 列目(含まない)までを A として抽出します Mat B = A(Range::all(), Range(1, 3)); // 5 行目(範囲に含む)から 9 行目(含まない)までを B として抽出します // つまり, C ~ A(Range(5, 9), Range(1, 3)) Mat C = B(Range(5, 9), Range::all()); Size size; Point ofs; C.locateROI(size, ofs); // サイズは (width=10,height=10) , ofs は (x=1, y=5) .. 行列全体の場合と同様に,深いコピーが必要ならば,抽出された部分行列の ``clone()`` メソッドを利用します. * ユーザが確保したデータに対するヘッダを作成します.これは,以下の場合に有用です: #. OpenCVを用いた「別の」データの処理 (例えば,DirectShow のフィルタや,gstreamer の処理モジュールなどを実装する場合) :: void process_video_frame(const unsigned char* pixels, int width, int height, int step) { cv::Mat img(height, width, CV_8UC3, pixels, step); cv::GaussianBlur(img, img, cv::Size(7,7), 1.5, 1.5); } .. #. 小さい行列の素早い初期化 および/または 超高速な要素アクセス :: double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}}; cv::Mat M = cv::Mat(3, 3, CV_64F, m).inv(); .. この「ユーザが確保したデータに対するヘッダを作成」で,一部で非常に頻繁に行われるケースが :ref:`CvMat` や :ref:`IplImage` から ``Mat`` への変換です.このために, ``CvMat`` や ``IplImage`` へのポインタをとる専用のコンストラクタと,データをコピーするか否かを指定するオプションフラグが存在します. また, ``Mat`` から ``CvMat`` や ``IplImage`` への後方変換は,キャスト演算子 ``Mat::operator CvMat() const`` と ``Mat::operator IplImage()`` によって提供されます.この演算子は,データをコピー *しません* . :: IplImage* img = cvLoadImage("greatwave.jpg", 1); Mat mtx(img); // IplImage* -> cv::Mat の変換 CvMat oldmat = mtx; // cv::Mat -> CvMat の変換 CV_Assert(oldmat.cols == img->width && oldmat.rows == img->height && oldmat.data.ptr == (uchar*)img->imageData && oldmat.step == img->widthStep); .. * MATLAB 形式 ``zeros(), ones(), eye()`` の行列初期化,つまり: :: // 倍精度の単位行列を作成して,それを M に足します. M += Mat::eye(M.rows, M.cols, CV_64F); .. * カンマ区切りの初期化子: :: // 3x3 の倍精度単位行列を作成します. Mat M = (Mat_(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); .. ここでは,まず(後で述べる) ``Mat_`` クラスのコンストラクタに適切な行列を与えて呼び出し,さらに ``<<`` 演算子に続いて,定数,変数,式などから成るカンマ区切りの値を与えます.また,コンパイルエラーを避けるために必要な括弧にも注意してください. 一度作成された行列は,参照カウント機構により自動的に管理されます(ただし,行列ヘッダがユーザが確保したデータに対して作成される場合を除きます.この場合,ユーザ自身がデータを処理しなければいけません). 行列データは,それを指し示すものがなくなった場合に解放されます.行列のデストラクタが呼ばれるよりも前に,その行列ヘッダによって指されるデータを解放したい場合は, ``Mat::release()`` を利用してください. 行列クラスに関して次に学ぶ重要事項は,要素へのアクセスです.ここでは,行列がどのようにデータを格納しているのかを述べます.要素は,行順に(行毎に)格納されています. ``Mat::data`` メンバは,この最初の行の最初の要素を指していおり, ``Mat::rows`` は行数を, ``Mat::cols`` は列数を表しています.さらに,実際に行列要素のアドレスを求めるために使われる ``Mat::step`` というメンバも存在します.ある行列は別の行列の一部であったり,アラインメントを揃えるために各行末に空白が存在したりするので,この ``Mat::step`` が必要になります. これらのパラメータが与えられると,行列要素 :math:`M_{ij}` のアドレスが以下のように計算されます: ``addr(`` :math:`M_{ij}` ``)=M.data + M.step*i + j*M.elemSize()`` もし行列要素の型が既知(例えば ``float`` )であるならば, ``at<>()`` メソッドを用いて以下のように書くことができます: ``addr(`` :math:`M_{ij}` ``)=&M.at(i,j)`` (ここで & は, ``at`` によって返される参照をポインタに変換します) 行列の行全体を処理する必要がある場合に最も効率的な方法は,最初の行のポインタを取得して,純粋な C 言語の演算子 ``[]`` を利用することです: :: // 値が正である要素の合計を求めます. // (ここで M は倍精度行列と仮定します) double sum=0; for(int i = 0; i < M.rows; i++) { const double* Mi = M.ptr(i); for(int j = 0; j < M.cols; j++) sum += std::max(Mi[j], 0.); } .. 上述のような処理は,実際は行列の形には依存せず,行列要素を1つ1つ処理する(または,行列同士の足し算のように,複数の行列の同じ場所にある要素を処理する)だけです.このような処理は要素ワイズと呼ばれ,すべての入出力行列が連続であるか,つまり,各行末にギャップが存在しないかどうかをチェックするのが妥当です.もしそうならば,長く続く1つの行であるかのように処理できます. :: // 値が正である要素の合計を求めます,最適化版 double sum=0; int cols = M.cols, rows = M.rows; if(M.isContinuous()) { cols *= rows; rows = 1; } for(int i = 0; i < rows; i++) { const double* Mi = M.ptr(i); for(int j = 0; j < cols; j++) sum += std::max(Mi[j], 0.); } .. 値が連続する行列の場合,外側のループは一度だけ実行されるのでオーバーヘッドが小さくなります.これは,特に小さい行列の場合に顕著です. 最後に,行と行の間のギャップをスキップする STL 形式の優れたイテレータの例を述べます: :: // 値が正である要素の合計を求める,イテレータ利用版 double sum=0; MatConstIterator_ it = M.begin(), it_end = M.end(); for(; it != it_end; ++it) sum += std::max(*it, 0.); .. 行列のイテレータは,ランダムアクセスイテレータなので, ``std::sort()`` を含むすべての STL アルゴリズムに適用できます. Matrix Expressions ------------------ これは,実装されている行列演算の一覧であり,組み合わせて任意の複雑な表現を行うこともできます(ここで *A* と *B* は行列を表し, *s* はスカラ( ``Scalar`` )を, *:math:`\alpha`* は実数値のスカラ( ``double`` )を表します). * 加算,減算,符号反転: :math:`\texttt{A}\pm \texttt{B},\;\texttt{A}\pm \texttt{s},\;\texttt{s}\pm \texttt{A},\;-\texttt{A}` * スケーリング: ``A*`` :math:`\alpha` ``, A/`` :math:`\alpha` * 要素毎の積,商: ``A.mul(B), A/B,`` :math:`\alpha` ``/A`` * 行列の積: ``A*B`` * 転置: ``A.t()`` :math:`\sim A^t` * 逆行列,擬似逆行列,連立方程式,最小二乗問題の解: ``A.inv([method])`` :math:`\sim A^{-1}` , ``A.inv([method])*B`` :math:`\sim X:\,AX=B` * 比較: :math:`\texttt{A}\gtreqqless \texttt{B},\;\texttt{A} \ne \texttt{B},\;\texttt{A}\gtreqqless \alpha,\; \texttt{A} \ne \alpha` . 比較結果は,8ビットのシングルチャンネルマスクで,(特定の要素,あるいは要素の組が条件を満たしていれば)255か,(そうでなければ)0にセットされます. * ビット単位の論理演算: ``A & B, A & s, A | B, A | s, A ^ B, A ^ s, ~ A`` * 要素単位の最小値,最大値: ``min(A, B), min(A,`` :math:`\alpha` ``), max(A, B), max(A,`` :math:`\alpha` ``)`` * 要素単位の絶対値: ``abs(A)`` * 外積,内積: ``A.cross(B), A.dot(B)`` * 行列かスカラを返す,行列,行列同士,スカラに対するあらゆる演算.例えば, :func:`norm` , :func:`mean` , :func:`sum` , :func:`countNonZero` , :func:`trace` , :func:`determinant` , :func:`repeat` など. * 行列の初期化子( ``eye(), zeros(), ones()`` ),行列のカンマ区切り初期化子,行列コンストラクタ,部分行列を抽出する演算子( :func:`Mat` の説明を参照してください). * 結果を適切な型に変換するための verb "Mat_()" コンストラクタ. また,カンマ区切りの初期化子,あるいはその他の演算は,曖昧さを回避するために明示的に ``Mat()`` や ``Mat_()`` コンストラクタを呼び出す必要があるかもしれない事に注意してください. 以下は, ``Mat`` のメソッドの正式な説明です. .. index:: Mat::Mat cv::Mat::Mat ------------ .. cfunction:: (1) Mat::Mat() .. cfunction:: (2) Mat::Mat(int rows, int cols, int type) .. cfunction:: (3) Mat::Mat(Size size, int type) .. cfunction:: (4) Mat::Mat(int rows, int cols, int type, const Scalar\& s) .. cfunction:: (5) Mat::Mat(Size size, int type, const Scalar\& s) .. cfunction:: (6) Mat::Mat(const Mat\& m) .. cfunction:: (7) Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP) .. cfunction:: (8) Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP) .. cfunction:: (9) Mat::Mat(const Mat\& m, const Range\& rowRange, const Range\& colRange) .. cfunction:: (10) Mat::Mat(const Mat\& m, const Rect\& roi) .. cfunction:: (11) Mat::Mat(const CvMat* m, bool copyData=false) .. cfunction:: (12) Mat::Mat(const IplImage* img, bool copyData=false) .. cfunction:: (13) template explicit Mat::Mat(const vector<_Tp>\& vec, bool copyData=false) .. cfunction:: (14) Mat::Mat(const MatExpr_Base\& expr) 様々な行列コンストラクタ. :param rows: 行列の行数 :param cols: 行列の列数 :param size: 行列のサイズ: ``Size(cols, rows)`` . ``Size()`` コンストラクタでは,行数と列数が逆順になっていることに注意してください :param type: 行列の型.1-4チャンネルの行列を作成するには ``CV_8UC1, ..., CV_64FC4`` を,マルチチャンネル(最大で ``CV_MAX_CN`` チャンネル)の行列を作成するには, ``CV_8UC(n), ..., CV_64FC(n)`` を利用してください :param s: 各行列要素を初期化するオプション値.すべての行列要素を特定の値にセットするには,コンストラクタの後で,代入演算子 ``Mat::operator=(const Scalar& value)`` を利用してください :param data: ユーザデータへのポインタ. ``data`` と ``step`` パラメータを引数にとる行列コンストラクタは,行列データ領域を確保しません.代わりに,指定のデータを指し示す行列ヘッダを初期化します.つまり,データのコピーは行われません.この処理は,非常に効率的で,OpenCVの関数を利用して外部データを処理することができます.外部データが自動的に解放されることはありませんので,ユーザが解放する必要があります :param step: The ``data`` buddy. このオプションパラメータでは,行列の各行が占めるバイト数を指定できます.この値は,各行の終端にパディングバイトが存在すれば,それも含みます.このパラメータがなければ( ``cv::AUTO_STEP`` にセットされて),パディングは存在しないとみなされ,実際の step は ``cols*elemSize()`` として計算されます. :ref:`Mat::elemSize` () を参照してください :param m: この行列は,作成された行列に(全体的,部分的に)割り当てられます.これらのコンストラクタによってデータがコピーされる事はありません.代わりに,データ ``m`` またはその部分行列,を指し示すヘッダが作成され,それに関連した参照カウンタがあれば,それがインクリメントされます.つまり,新しく作成された行列の内容を変更することで, ``m`` の対応する要素も変更することなります :param img: 古い形式である ``IplImage`` 画像構造体へのポインタ.デフォルトでは,元の画像と新しい行列とでデータが共有されますが, ``copyData`` がセットされている場合は,画像データの完全なコピーが作成されます :param vec: STL vector で,この要素は行列を構成します.この行列は,単一の列と,vector の要素と同じ数の行を持ちます.また,行列の型は,vector の要素の型と一致します.コンストラクタは,適切に宣言された :ref:`DataType` が存在する任意の型を扱うことが可能です.つまり,vector の要素は必ず,プリミティブな数値か,単一の型の数値タプルでなければいけません.もちろん,型が混在する構造体はサポートされていません.これに対応するコンストラクタは明示的であることに注意してください.つまり,STL vector が自動的に ``Mat`` インスタンスに変換されることはなく, ``Mat(vec)`` を明示的に記述しなければいけません.行列にデータをコピーする場合( ``copyData=true`` )を除いて,vector に新たに要素を追加するべきではありません.なぜなら,それによって vector データの再配置が発生する可能性があり,行列データポインタが無効になるからです :param copyData: STL vector の内部データ,古い形式の ``CvMat`` または ``IplImage`` を新しく作成される行列にコピーする(true)か,共有する(false)かを指定するフラグ.データがコピーされる場合,確保されたバッファは ``Mat`` の参照カウント機構を用いて管理されます.デーがが共有される場合,参照カウンタは NULL になり,ユーザは,作成された行列がデストラクトされない限り,データを解放するべきではありません :param rowRange: 扱われる ``m`` の行の範囲.すべての行を扱う場合は, ``Range::all()`` を利用してください :param colRange: 扱われる ``m`` の列の範囲.すべての列を扱う場合は, ``Range::all()`` を利用してください :param expr: Matrix expression. :ref:`Matrix Expressions` を参照してください これらは,行列を作成する様々なコンストラクタです. の注意書きにもあるように,多くの場合はデフォルトコンストラクタで十分であり,OpenCV の関数により適切な行列が確保されます.作成された行列は,さらに別の行列や matrix expression に代入することができ,その場合,古い内容に対しては参照外しが行われます.あるいは,行列をさらに :ref:`Mat::create` によって確保することもできます. .. index:: Mat::Mat cv::Mat::Mat ------------ ~ .. cfunction:: Mat::\textasciitilde Mat() index{cv::Mat::Mat}label{cppfunc.Mat::destructor} 行列のデストラクタ. 行列のデストラクタは :ref:`Mat::release` を呼び出します. .. index:: Mat::operator = cv::Mat::operator = ------------------- .. cfunction:: Mat\& Mat::operator = (const Mat\& m) .. cfunction:: Mat\& Mat::operator = (const MatExpr_Base\& expr) .. cfunction:: Mat\& operator = (const Scalar\& s) 行列の代入演算子. :param m: 代入される,右辺の行列. 行列の代入は O(1) の処理であり,つまりデータのコピーは行われません.その代わり,参照カウンタがあれば,それがインクリメントされます.また,新しいデータを代入する前に, :ref:`Mat::release` によって古いデータの参照外しが行われます :param expr: 代入される matrix expression オブジェクト.代入演算である1番目の形式とは逆に,この2番目の形式では,matrix expression と一致するサイズと型を持てば,既に確保された行列を再利用することが可能です.これは,実際の関数に展開された matrix expression によって自動的に扱われます.例えば, ``C=A+B`` は ``cv::add(A, B, C)`` に展開され,この :func:`add` は,自動的に ``C`` の再配置を行います :param s: 各行列要素に代入されるスカラ.行列サイズや型は変更されません これらは,すべて有効な代入演算子です.それぞれ大きく異なっているので,演算子の説明をきちんと読んでください. .. index:: Mat::operator MatExpr cv::Mat::operator MatExpr ------------------------- .. cfunction:: Mat::operator MatExpr_() const 行列から MatExpr へのキャスト演算子. このキャスト演算子は,明示的に呼ばれるべきものではありません. :ref:`Matrix Expressions` エンジンで,内部的に利用されます. .. index:: Mat::row cv::Mat::row ------------ .. cfunction:: Mat Mat::row(int i) const 行列の指定された行に対する行列ヘッダを作成します. :param i: 0基準の行インデックス このメソッドは,行列の指定行に対する新しいヘッダを作成し,それを返します.これは,行列のサイズにかかわらず O(1) の処理です.新しい行列の内部データは,元の行列と共有されます.ここでは,LU やその他たくさんのアルゴリズムで利用されている,古典的で基本的な行列処理の1つである axpy の例を紹介します. :: inline void matrix_axpy(Mat& A, int i, int j, double alpha) { A.row(i) += A.row(j)*alpha; } .. **重大な注意** .現在の実装では,以下のコードは期待通りには動作しません. :: Mat A; ... A.row(i) = A.row(j); // 動作しません .. 動作しない理由は, ``A.row(i)`` が一時的なヘッダを作成し,そこにさらに別のヘッダに代入されるからです.これらの演算子は O(1) ,つまりデータのコピーは行われないことを忘れないでください.従って,j 番目の行が i 番目の行にコピーされると期待して上述の代入を行っても,期待通りには動きません.それを行うには,単純な代入を expression を利用したものに代えるか, :ref:`Mat::copyTo` メソッドを利用するかしてください. :: Mat A; ... // 動作しますが,少し分かり難いです. A.row(i) = A.row(j) + 0; // すこし長いですが,おすすめの方法です. Mat Ai = A.row(i); M.row(j).copyTo(Ai); .. .. index:: Mat::col cv::Mat::col ------------ .. cfunction:: Mat Mat::col(int j) const 行列の指定された列に対する行列ヘッダを作成します. :param j: 0基準の行インデックス このメソッドは,行列の指定列に対する新しいヘッダを作成し,それを返します.これは,行列のサイズにかかわらず O(1) の処理です.新しい行列の内部データは,元の行列と共有されます. :ref:`Mat::row` の説明も参照してください. .. index:: Mat::rowRange cv::Mat::rowRange ----------------- .. cfunction:: Mat Mat::rowRange(int startrow, int endrow) const .. cfunction:: Mat Mat::rowRange(const Range\& r) const 行列の指定された範囲行に対する行列ヘッダを作成します. :param startrow: 範囲行の先頭インデックス.0基準 :param endrow: 範囲行の末尾インデックス.0基準 :param r: インデックスの先頭と末尾を共に含む :func:`Range` 構造体 このメソッドは,行列の指定範囲行に対する新しいヘッダを作成します.これは :func:`Mat::row` や :func:`Mat::col` と同様に,O(1) の処理です. .. index:: Mat::colRange cv::Mat::colRange ----------------- .. cfunction:: Mat Mat::colRange(int startcol, int endcol) const .. cfunction:: Mat Mat::colRange(const Range\& r) const 行列の指定された範囲列に対する行列ヘッダを作成します. :param startrow: 範囲行の先頭インデックス.0基準 :param endrow: 範囲行の末尾インデックス.0基準 :param r: インデックスの先頭と末尾を共に含む :func:`Range` 構造体 このメソッドは,行列の指定範囲行に対する新しいヘッダを作成します.これは :func:`Mat::row` や :func:`Mat::col` と同様に,O(1) の処理です. .. index:: Mat::diag cv::Mat::diag ------------- .. cfunction:: Mat Mat::diag(int d) const static Mat Mat::diag(const Mat\& matD) 行列から対角成分を抜き出します.または対角行列を作成します. :param d: 以下を意味する,対角成分のインデックス * **d=0** 主対角成分 * **d>0** 主対角成分の下側の対角成分.例えば ``d=1`` は,主対角成分の1つ下の対角成分を意味します * **d<0** 主対角成分の上側の対角成分.例えば ``d=1`` は,主対角成分の1つ上の対角成分を意味します :param matD: 1列の行列.ここから対角行列を構成します このメソッドは,指定された行列の対角成分に対する新しいヘッダを作成します.この新しい行列は,1列の行列として表現されます. :func:`Mat::row` や :func:`Mat::col` と同様に,これも O(1) の処理です. .. index:: Mat::clone cv::Mat::clone -------------- .. cfunction:: Mat Mat::clone() const 行列とその内部データの完全なコピーを作成します. このメソッドは,行列の完全なコピーを作成します.しかし,元の行列の ``step`` は考慮されません.この行列のコピーは,連続した ``cols*rows*elemSize()`` バイトの領域を占有します. .. index:: Mat::copyTo cv::Mat::copyTo --------------- .. cfunction:: void Mat::copyTo( Mat\& m ) const void Mat::copyTo( Mat\& m, const Mat\& mask ) const 行列を別の行列にコピーします. :param m: コピー先の行列.適切なサイズ,型ではない場合は,処理の前に再配置されます :param mask: 処理マスク.この非ゼロの要素が,コピーするべき行列の要素を表します このメソッドは,行列のデータを別の行列にコピーします.データをコピーする前に,このメソッドは以下を呼び出します. :: m.create(this->size(), this->type); .. これにより,必要ならばコピー先の行列が再配置されます. ``m.copyTo(m);`` が期待通りの動作を行います,つまり,行列は何も変化しません.しかし,この関数は,コピー元とコピー先の行列が部分的にオーバラップするような場合を扱うことはできません. 処理マスクが指定される場合,そして,上述の ``Mat::create`` 呼び出しが行列を再配置される場合,新しく確保された行列は,データコピーの前に0で初期化されます. .. index:: Mat::convertTo cv::Mat::convertTo ------------------ .. cfunction:: void Mat::convertTo( Mat\& m, int rtype, double alpha=1, double beta=0 ) const 行列のデータを別の型に変換します.オプションで,スケーリングか可能です. :param m: 出力行列.適切なサイズ,型ではない場合は,処理の前に再配置されます :param rtype: 出力行列に要求する型,(チャンネル数は元の行列と同じなので)正確には,要求するビット深度,です. ``rtype`` が負の場合は,出力行列は元の行列と同じ型になります :param alpha: オプションのスケールファクタ :param beta: オプションのデルタ.スケーリングされた値に足されます このメソッドは,元行列のピクセル値を目的のデータ型に変換します.オーバフローを回避するために,処理の最後に ``saturate_cast<>`` が適用されます. .. math:: m(x,y) = saturate \_ cast( \alpha (*this)(x,y) + \beta ) .. index:: Mat::assignTo cv::Mat::assignTo ----------------- .. cfunction:: void Mat::assignTo( Mat\& m, int type=-1 ) const convertTo の関数形式. :param m: 出力行列 :param type: 出力行列のビット深度(-1 の場合は元の行列と同じであることを意味します) これは, :ref:`Matrix Expressions` エンジンによって内部的に利用されるメソッドです. .. index:: Mat::setTo cv::Mat::setTo -------------- .. cfunction:: Mat\& Mat::setTo(const Scalar\& s, const Mat\& mask=Mat()) すべて,またはいくつかの行列の要素に指定された値をセットします. :param s: 代入されるスカラー値.これは,実際の行列の型に変換されます :param mask: ``*this`` と同じサイズの処理マスク これは, ``Mat::operator=(const Scalar& s)`` 演算子の詳細版です. .. index:: reshape cv::reshape ----------- .. cfunction:: Mat Mat::reshape(int cn, int rows=0) const データをコピーすることなく,行列の形状かチャンネル数,あるいはその両方を変更します :param cn: 新しいチャンネル数.このパラメータが0の場合,チャンネル数はそのままになります :param rows: 新しい行数.このパラメータが0の場合,行数はそのままになります このメソッドは, ``*this`` 要素に対する新しい行列ヘッダを作成します.新しい行列は,元の行列とは,サイズかチャンネル数,あるいはその両方が異なります.以下の条件内であれば,どのような組み合わせも可能です. #. 新しい行列に要素が追加されたり,逆に削除されたりしません.その結果, ``rows*cols*channels()`` は変換後も同じ値になります. #. データはコピーされません.つまり,これは O(1) の処理です.その結果,行数を変更したり,何か他の方法で要素の行インデックスを変更したりしても,行列のデータ領域は連続となります. ここでは,簡単な例を示します.STL vector に保存された3次元の点群があり,これを ``3xN`` の行列で表現したいとします.その処理は,次のようになります: :: std::vector vec; ... Mat pointMat = Mat(vec). // vector から Mat への変換. O(1) の処理 reshape(1). // Nx1 3-チャンネル行列から,Nx3 1-チャンネル行列を作成します. // これも O(1) の処理です. t(); // 最後に,Nx3 行列を転置します. // これは,全要素のデータコピーを含みます. .. .. index:: Mat::t() cv::Mat::t() ------------ .. cfunction:: MatExpr_ >, Mat> Mat::t() const 行列の転置を行います. このメソッドは, matrix expressions を用いて行列の転置を行います. つまりこのメソッドは,より複雑な matrix expression の一部として利用されたり,行列に代入されたりし得る,一時的な「転置行列」オブジェクトを返します. :: Mat A1 = A + Mat::eye(A.size(), A.type)*lambda; Mat C = A1.t()*A1; // (A + lambda*I)^t * (A + lamda*I) を計算します .. .. index:: Mat::inv cv::Mat::inv ------------ .. cfunction:: MatExpr_ >, Mat> Mat::inv(int method=DECOMP_LU) const 逆行列を求めます. :param method: 逆行列を求める方法.以下のうちの1つ * **DECOMP_LU** LU 分解.行列は,非特異行列でなければいけません * **DECOMP_CHOLESKY** コレスキー :math:`LL^T` 分解.利用できるのは,正定値対称行列のみです.巨大な行列に対しては,LU 分解の約2倍高速です * **DECOMP_SVD** 特異値分解.行列は,特異または非正方でも構いませんが,その場合擬似逆行列が計算されます このメソッドは,matrix expression を用いて逆行列を計算します.つまりこのメソッドは,より複雑な matrix expression の一部として利用されたり,行列に代入されたりし得る,一時的な「逆行列」オブジェクトを返します. .. index:: Mat::mul cv::Mat::mul ------------ .. cfunction:: MatExpr_<...MatOp_MulDiv_<>...> .. cfunction:: Mat::mul(const Mat\& m, double scale=1) const .. cfunction:: MatExpr_<...MatOp_MulDiv_<>...> .. cfunction:: Mat::mul(const MatExpr_<...MatOp_Scale_<>...>\& m, double scale=1) const .. cfunction:: MatExpr_<...MatOp_MulDiv_<>...> .. cfunction:: Mat::mul(const MatExpr_<...MatOp_DivRS_<>...>\& m, double scale=1) const 2つの行列の要素同士の掛け算,割り算を行います. :param m: ``*this`` と同じ型,同じサイズの別の行列,またはスケーリングされた行列,またはスカラーを行列で割った値(つまり,すべての要素が,別の行列の要素の逆数をスケーリングしたもの,となる行列) :param scale: オプションのスケールファクタ このメソッドは,要素同士の掛け算,または割り算,オプションとしてのスケーリングを表す一時的なオブジェクトを返します.これは,単純な「*」演算子に対応する行列同士の積とは異なることに注意してください. ここでは,このメソッドの3番目の形式が自動的に呼び出される例を示します: :: Mat C = A.mul(5/B); // divide(A, B, C, 5) と等価 .. .. index:: Mat::cross cv::Mat::cross -------------- .. cfunction:: Mat Mat::cross(const Mat\& m) const 2つの3要素ベクトル同士の外積を計算します. :param m: 外積計算の対象となる,もう片方のベクトル このメソッドは,3要素ベクトル同士の外積を計算します.この,ベクトルは同じ形状,同じサイズの3要素浮動小数点型ベクトルでなければいけません.計算結果は,入力ベクトルと同じ形状,同じ型の,別の3要素ベクトルとなります. .. index:: Mat::dot cv::Mat::dot ------------ .. cfunction:: double Mat::dot(const Mat\& m) const 2つのベクトル同士の内積を計算します. :param m: 内積計算の対象となる,もう片方のベクトル このメソッドは,2つの行列同士の内積を計算します.行列が1行または1列の行列でなかった場合,上から下,左から右の順番でスキャンを行い,それぞれを1次元ベクトルとして扱います.これらの2つベクトルは,同じサイズ,同じ型でなければいけません.行列が2つ以上のチャンネルを持つ場合,各チャンネルでの内積が足し合わされます. .. index:: Mat::zeros cv::Mat::zeros -------------- .. cfunction:: static MatExpr_Initializer Mat::zeros(int rows, int cols, int type) static MatExpr_Initializer Mat::zeros(Size size, int type) 指定されたサイズ,型の零行列を返します. :param rows: 行数 :param cols: 列数 :param size: 行列サイズ指定の別の表現: ``Size(cols, rows)`` :param type: 作成される行列の型 このメソッドは,Matlab 形式の零行列イニシャライザを返します.これは,定数行列を作成する簡単な書式で,行列を,関数のパラメータ,matrix expression の一部,行列イニシャライザとして利用する場合に用いることができます. :: Mat A; A = Mat::zeros(3, 3, CV_32F); .. 上述のサンプルでは, ``A`` が 3x3 浮動小数点型行列ではない場合にのみ,新しい行列が確保されます.それ以外の場合は,既に存在する行列 ``A`` が0で埋められます. .. index:: Mat::ones cv::Mat::ones ------------- .. cfunction:: static MatExpr_Initializer Mat::ones(int rows, int cols, int type) static MatExpr_Initializer Mat::ones(Size size, int type) 指定されたサイズ,型の,すべての要素が1である行列を返します. :param rows: 行数 :param cols: 列数 :param size: 行列サイズ指定の別の表現: ``Size(cols, rows)`` :param type: 作成される行列の型 このメソッドは :func:`Mat::zeros` と同様に,Matlab 形式の,1で埋められた行列イニシャライザを返します.このメソッドを利用する場合,以下の Matlab 的表現を用いると,行列を任意の値で初期化できることに注意してください: :: Mat A = Mat::ones(100, 100, CV_8U)*3; // 3 で埋められた 100x100 行列 .. 上述の処理は 1で埋められた 100x100 行列を作成して,それを3倍するわけではありません.スケールファクタ(この場合は3)を念頭に置き,行列イニシャライザを拡張する場合にはこれを利用してください. .. index:: Mat::eye cv::Mat::eye ------------ .. cfunction:: static MatExpr_Initializer Mat::eye(int rows, int cols, int type) static MatExpr_Initializer Mat::eye(Size size, int type) 指定されたサイズ,型の単位行列を返します. :param rows: 行数 :param cols: 列数 :param size: 行列サイズ指定の別の表現: ``Size(cols, rows)`` :param type: 作成される行列の型 このメソッドは :func:`Mat::zeros` と同様に,Matlab 形式の単位行列イニシャライザを返します.このメソッドを利用する場合,必要なスケールファクタを掛けることで,定数倍された単位行列で行列を初期化できることに注意してください. :: // 対角要素が 0.1 である 4x4 の対角行列を作成します. Mat A = Mat::eye(4, 4, CV_32F)*0.1; .. そして,これも非常に効率的に O(1) の処理を行います. .. index:: Mat::create cv::Mat::create --------------- .. cfunction:: void Mat::create(int rows, int cols, int type) void create(Size size, int type) 必要ならば,新しい行列データを確保します. :param rows: 行数 :param cols: 列数 :param size: 行列サイズ指定の別の表現: ``Size(cols, rows)`` :param type: 新しい行列の型 これは, ``Mat`` のキーメソッドの1つです.行列を作成する関数やメソッドで,OpenCV の新しい形式のものの多くは,各出力行列毎にこのメソッドを呼び出します.このメソッドのアルゴリズムは,次のようになります: #. 現在の行列サイズと型が,新しい行列と一致する場合,即座に return します. #. そうでない場合, :func:`Mat::release` を呼び出して,以前のデータに対して参照外しを行います. #. 新しいヘッダを初期化します. #. ``rows*cols*elemSize()`` バイトの新しいデータを確保します. #. 新しいデータに関連付けられた参照カウンタを作成し,その値を1にセットします. ロバストかつ効率的なメモリ管理を同時に行うこのような機構は,ユーザのタイプ量をかなり削減します.つまり通常は,出力行列を明示的に確保する必要はない,とうことです. .. index:: Mat::addref cv::Mat::addref --------------- .. cfunction:: void Mat::addref() 参照カウンタをインクリメントします. このメソッドは,行列データに関連付けられた参照カウンタをインクリメントします.行列ヘッダが外部データを指し示している場合( :func:`Mat::Mat` を参照)は,参照カウンタは NULL です.その場合,このメソッドは何もしません.メモリリークを避けるため,通常このメソッドは明示的に呼ばれるべきではありません.行列の代入演算子においては,内部的に呼び出されています.また,参照カウンタの増加は,それをサポートするプラットフォーム上では不可分な操作なので,異なるスレッドで同じ行列を非同期に処理しても安全です. .. index:: Mat::release cv::Mat::release ---------------- .. cfunction:: void Mat::release() 参照カウンタをデクリメントし,必要ならば行列を解放します. このメソッドは,行列データに関連付けられた参照カウンタをデクリメントします.参照カウンタが0になると,行列データは解放され,そのデータと参照カウンタのポインタは NULL にセットされます.行列ヘッダが外部データを指し示している場合( :func:`Mat::Mat` を参照)は,参照カウンタは NULL です.その場合,このメソッドは何もしません. 行列データを強制的に解放するために,このメソッドを手動で呼び出すことができます.しかし,デストラクタ,そしてデータポインタを変更するあらゆるメソッドは自動的にこのメソッドを呼び出すので,通常その必要はありません.参照カウンタの減少とそれが0に到達したかどうかのチェックは,それをサポートするプラットフォーム上では不可分な操作なので,異なるスレッドで同じ行列を非同期に処理しても安全です. .. index:: Mat::locateROI cv::Mat::locateROI ------------------ .. cfunction:: void Mat::locateROI( Size\& wholeSize, Point\& ofs ) const 親行列の内部の行列ヘッダを見つけます. :param wholeSize: 行列全体のサイズが代入される出力パラメータ. ``*this`` は,これの一部となります :param ofs: 出力パラメータ.行列全体の内側にある ``*this`` のオフセット値を表します :func:`Mat::row` , :func:`Mat::col` , :func:`Mat::rowRange` , :func:`Mat::colRange` などを利用して行列から部分行列を抽出した後,結果として得られる部分行列は元の大きな行列の一部を指し示すだけのものです.しかし,それぞれの部分行列は,( ``datastart`` と ``dataend`` フィールドによって表される)情報を持っています.これを用いることで,元の行列サイズと,抽出された部分行列が元の行列のどこに位置していたか,を復元できます.このメソッド ``locateROI`` は,まさにそれを行うものです. .. index:: Mat::adjustROI cv::Mat::adjustROI ------------------ .. cfunction:: Mat\& Mat::adjustROI( int dtop, int dbottom, int dleft, int dright ) 親行列内の部分行列のサイズと位置を調整します. :param dtop: 部分行列の上側の境界の,上への移動量 :param dbottom: 部分行列の下側の境界の,下への移動量 :param dleft: 部分行列の左側の境界の,左への移動量 :param dright: 部分行列の右側の境界の,右への移動量 このメソッドは, :func:`Mat::locateROI` を補足するものです.実際,これらの関数の典型的な用法は,親行列内での部分行列の位置を検出して移動させる,というものです.ROI の外側のピクセルを考慮しなければならないフィルタリング処理は,このような処理が必要になる典型的な場合です.メソッドのすべてのパラメータは正値であり,これは,ROI がすべての方向に指定され量だけ増加しなければいけない,ということを意味します.つまり,以下の処理の場合: :: A.adjustROI(2, 2, 2, 2); .. これは,各方向に4要素分行列サイズを増加させ,それを2要素分だけ左と上に移動させます.これによって, 5x5 のカーネルでフィルタリングを行うために必要なすべてのピクセルを引き込むことになります. adjustROI が親行列の境界を越えないようにするのはユーザの責任です.越えてしまった場合,この関数はエラーを発生させます. この関数は, :func:`filter2D` やモルフォロジー演算などの,OpenCV のフィルタリング関数の内部で利用されています. :func:`copyMakeBorder` も参照してください. .. index:: Mat::operator() cv::Mat::operator() ------------------- .. cfunction:: Mat Mat::operator()( Range rowRange, Range colRange ) const .. cfunction:: Mat Mat::operator()( const Rect\& roi ) const 矩形部分行列を抽出します. :param rowRange: 抽出された部分行列の最初と最後の行.最後の行は,範囲に含まれません.すべての行を選択するには, ``Range::all()`` を利用してください :param colRange: 抽出された部分行列の最初と最後の列.最後の列は,範囲に含まれません.すべての列を選択するには, ``Range::all()`` を利用してください :param roi: 抽出する部分行列を,矩形として指定したもの この演算子は, ``*this`` 内部の指定された部分行列に対する新しいヘッダを作成します.これらは, :func:`Mat::row` , :func:`Mat::col` , :func:`Mat::rowRange` そして :func:`Mat::colRange` の最も一般化された形式です.例えば, ``A(Range(0, 10), Range::all())`` は, ``A.rowRange(0, 10)`` と等価です.上述したものと同様に,この演算子も O(1) の処理であり,データはコピーされません. .. index:: Mat::operator CvMat cv::Mat::operator CvMat ----------------------- .. cfunction:: Mat::operator CvMat() const 行列に対する CvMat ヘッダを作成します. この演算子は,内部データをコピーせずに,行列に対する CvMat ヘッダを作成します.この処理では,参照カウンタは考慮されないので, ``CvMat`` ヘッダが利用されている間は,元の行列が解放されないようにしなければいけません.この演算子は,OpenCV の新旧の API を混合して利用している場合に役立ちます.例えば: :: Mat img(Size(320, 240), CV_8UC3); ... CvMat cvimg = img; my_old_cv_func( &cvimg, ...); .. ここで ``my_old_cv_func`` は,OpenCV 1.x のデータ構造を処理するための何らかの関数を表します. .. index:: Mat::operator IplImage cv::Mat::operator IplImage -------------------------- .. cfunction:: Mat::operator IplImage() const 行列に対する IplImage ヘッダを作成します. この演算子は,内部データをコピーせずに,行列に対する IplImage ヘッダを作成します. ``IplImage`` ヘッダが利用されている間は,元の行列が解放されないようにしなければいけません. ``Mat::operator CvMat`` と同様に,この演算子は,OpenCV の新旧の API を混合して利用している場合に役立ちます. .. index:: Mat::isContinuous cv::Mat::isContinuous --------------------- .. cfunction:: bool Mat::isContinuous() const 行列が連続であるか否かを調べます. このメソッドは,行列の要素が連続して格納されていれば,つまり,各行の最後にギャップが存在しなければ true を返し,そうでなければ false を返します.明らかに, ``1x1`` や ``1xN`` の行列は常に連続です. :func:`Mat::create` によって作成された行列は常に連続ですが, :func:`Mat::col` , :func:`Mat::diag` などによって部分行列が抜き出された場合,外部データに対して行列ヘッダが作成された場合など,そのような行列は連続とは限りません. この連続性フラグは, ``Mat::flags`` フィールド内の1ビットとして保存され,行列ヘッダを作成する際に自動的に計算されます.従って,連続性のチェックは非常に高速であり,理論的には,以下のような処理になります: :: // Mat::isContinuous() の別の実装 bool myCheckMatContinuity(const Mat& m) { //(m.flags & Mat::CONTINUOUS_FLAG) != 0; を返します return m.rows == 1 || m.step == m.cols*m.elemSize(); } .. このメソッドは,非常に多くの OpenCV の関数で利用されており,同様にユーザも自由に利用できるものです.(算術演算,論理演算,数学関数,アルファブレンディング,色空間変換などの)要素毎の処理は,画像の幾何的特徴に依存しないので,すべての入出力配列が連続であれば,この関数は,それらを非常に長い1行のベクトルとして扱うことができる,という事が重要な部分です.ここでは,アルファブレンディング関数の実装方法を示します. :: template void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) { const float alpha_scale = (float)std::numeric_limits::max(), inv_scale = 1.f/alpha_scale; CV_Assert( src1.type() == src2.type() && src1.type() == CV_MAKETYPE(DataType::depth, 4) && src1.size() == src2.size()); Size size = src1.size(); dst.create(size, src1.type()); // 配列の連続性チェックを行い, // 連続だった場合, // 配列を1次元ベクトルとして扱います. if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() ) { size.width *= size.height; size.height = 1; } size.width *= 4; for( int i = 0; i < size.height; i++ ) { // 配列が連続の場合, // 外側のループは1度しか実行されません. const T* ptr1 = src1.ptr(i); const T* ptr2 = src2.ptr(i); T* dptr = dst.ptr(i); for( int j = 0; j < size.width; j += 4 ) { float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale; dptr[j] = saturate_cast(ptr1[j]*alpha + ptr2[j]*beta); dptr[j+1] = saturate_cast(ptr1[j+1]*alpha + ptr2[j+1]*beta); dptr[j+2] = saturate_cast(ptr1[j+2]*alpha + ptr2[j+2]*beta); dptr[j+3] = saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale); } } } .. この技は非常に単純ですが,単純な要素処理を 10-20 パーセント高速化できます.特に,画像が比較的小さく,処理が非常に単純な場合に有効です. そして,この関数では OpenCV のもう一つの慣用的手法を用いていることに注意してください.つまり,出力配列が適切なサイズと型であるかをチェックする代わりに :func:`Mat::create` を呼び出しています.また,新たに確保された配列は常に連続ですが, :func:`create` が常に新しい行列を確保するとは限らないので,出力配列をチェックします. .. index:: Mat::elemSize cv::Mat::elemSize ----------------- .. cfunction:: size_t Mat::elemSize() const 行列の要素サイズをバイト単位で返します. このメソッドは,行列の要素サイズをバイト単位で返します.例えば,行列の型が ``CV_16SC3`` である場合,このメソッドは ``3*sizeof(short)`` または 6 を返します. .. index:: Mat::elemSize1 cv::Mat::elemSize1 ------------------ .. cfunction:: size_t Mat::elemSize1() const 行列要素のチャンネル毎のサイズをバイト単位で返します. このメソッドは,行列要素のチャンネル毎のサイズをバイト単位で返します.つまり,チャンネル数が無視されます.例えば,行列の型が ``CV_16SC3`` である場合,このメソッドは ``sizeof(short)`` または 2 を返します. .. index:: Mat::type cv::Mat::type ------------- .. cfunction:: int Mat::type() const 行列要素の型を返します. このメソッドは, ``CV_16SC3`` または 16-ビット符号あり整数,3チャンネル配列 などの ``CvMat`` の型システムと互換性のある行列要素の型を ID で返します. .. index:: Mat::depth cv::Mat::depth -------------- .. cfunction:: int Mat::depth() const 行列要素のビット深度を返します. このメソッドは,行列要素のビット深度 ID ,つまり,各チャンネル毎の型を返します.例えば,16-ビット符号あり整数,3チャンネル配列の場合,これは ``CV_16S`` を返します.行列の型の完全なリストは以下のとおりです: * ``CV_8U`` - 8-ビット符号なし整数 ( ``0..255`` ) * ``CV_8S`` - 8-ビット符号あり整数 ( ``-128..127`` ) * ``CV_16U`` - 16-ビット符号なし整数 ( ``0..65535`` ) * ``CV_16S`` - 16-ビット符号あり整数 ( ``-32768..32767`` ) * ``CV_32S`` - 32-ビット符号あり整数 ( ``-2147483648..2147483647`` ) * ``CV_32F`` - 32-ビット浮動小数点数 ( ``-FLT_MAX..FLT_MAX, INF, NAN`` ) * ``CV_64F`` - 64-ビット浮動小数点数 ( ``-DBL_MAX..DBL_MAX, INF, NAN`` ) .. index:: Mat::channels cv::Mat::channels ----------------- .. cfunction:: int Mat::channels() const 行列のチャンネル数を返します. このメソッドは,行列のチャンネル数を返します. .. index:: Mat::step1 cv::Mat::step1 -------------- .. cfunction:: size_t Mat::step1() const 正規化されたステップを返します. このメソッドは,行列のステップを :func:`Mat::elemSize1()` で割った値を返します.これは,行列の任意の要素に高速にアクセスするのに役立ちます. .. index:: Mat::size cv::Mat::size ------------- .. cfunction:: Size Mat::size() const 行列のサイズを返します. このメソッドは,行列のサイズを返します: ``Size(cols, rows)`` . .. index:: Mat::empty cv::Mat::empty -------------- .. cfunction:: bool Mat::empty() const 行列のデータが確保されていない場合に true を返します. このメソッドは,行列データが NULL ポインタである場合にのみ true を返します.このメソッドは,STL vector との親和性を向上させるために導入されました. .. index:: Mat::ptr cv::Mat::ptr ------------ .. cfunction:: uchar* Mat::ptr(int i=0) .. cfunction:: const uchar* Mat::ptr(int i=0) const .. cfunction:: template _Tp* Mat::ptr(int i=0) .. cfunction:: template const _Tp* Mat::ptr(int i=0) const 行列の指定された行へのポインタを返します. :param i: 0基準の行インデックス このメソッドは,行列の指定行を指す ``uchar*`` または型付きポインタを返します.これらのメソッドの使用法の知るために, :func:`Mat::isContinuous` () 項にあるサンプルを参照してください. .. index:: Mat::at cv::Mat::at ----------- .. cfunction:: template _Tp\& Mat::at(int i, int j) .. cfunction:: template _Tp\& Mat::at(Point pt) .. cfunction:: template const _Tp\& Mat::at(int i, int j) const .. cfunction:: template const _Tp\& Mat::at(Point pt) const 行列の指定された要素への参照を返します. :param i: 0基準の行インデックス :param j: 0基準の列インデックス :param pt: ``Point(j,i)`` という様に指定される要素位置 このテンプレートメソッドは,行列の指定要素への参照を返します.パフォーマンス向上のために,インデックスの範囲チェックは Debug モードでのみ行われます. ここでは例として, 様々な数値アルゴリズムに対する standard poor-conditioned test matrices を, ``Mat::at`` メソッドを用いて作成する方法を示します. :: Mat H(100, 100, CV_64F); for(int i = 0; i < H.rows; i++) for(int j = 0; j < H.cols; j++) H.at(i,j)=1./(i+j+1); .. .. index:: Mat::begin cv::Mat::begin -------------- .. cfunction:: template MatIterator_<_Tp> Mat::begin() template MatConstIterator_<_Tp> Mat::begin() const 行列の最初の要素を指した状態の,行列イテレータを返します. このメソッドは,読み込み専用,または読み書き可能なイテレータを返します.行列イテレータの利用法は, STL の双方向イテレータの利用法に非常によく似ています.ここでは,行列イテレータを用いて書かれたアルファブレンディング関数を紹介します: :: template void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst) { typedef Vec VT; const float alpha_scale = (float)std::numeric_limits::max(), inv_scale = 1.f/alpha_scale; CV_Assert( src1.type() == src2.type() && src1.type() == DataType::type && src1.size() == src2.size()); Size size = src1.size(); dst.create(size, src1.type()); MatConstIterator_ it1 = src1.begin(), it1_end = src1.end(); MatConstIterator_ it2 = src2.begin(); MatIterator_ dst_it = dst.begin(); for( ; it1 != it1_end; ++it1, ++it2, ++dst_it ) { VT pix1 = *it1, pix2 = *it2; float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale; *dst_it = VT(saturate_cast(pix1[0]*alpha + pix2[0]*beta), saturate_cast(pix1[1]*alpha + pix2[1]*beta), saturate_cast(pix1[2]*alpha + pix2[2]*beta), saturate_cast((1 - (1-alpha)*(1-beta))*alpha_scale)); } } .. .. index:: Mat::end cv::Mat::end ------------ .. cfunction:: template MatIterator_<_Tp> Mat::end() template MatConstIterator_<_Tp> Mat::end() const 行列の最後の要素の後ろを指した状態の,行列イテレータを返します. このメソッドは,最後の行列要素の後ろを指すようにセットされた,読み込み専用,または読み書き可能なイテレータを返します. Mat\_ ----- :func:`Mat` から派生する行列クラステンプレート. :: template class Mat_ : public Mat { public: typedef _Tp value_type; typedef typename DataType<_Tp>::channel_type channel_type; typedef MatIterator_<_Tp> iterator; typedef MatConstIterator_<_Tp> const_iterator; Mat_(); // Mat(_rows, _cols, DataType<_Tp>::type) と等価 Mat_(int _rows, int _cols); // 上記コンストラクタの別の表現 Mat_(int _rows, int _cols, const _Tp& value); explicit Mat_(Size _size); Mat_(Size _size, const _Tp& value); // コピー / 変換コンストラクタ. m が異なる型ならば,変換されます. Mat_(const Mat& m); // コピーコンストラクタ Mat_(const Mat_& m); // ユーザが確保したデータを持つ行列を作成します. // step は型に関係なくバイト単位(!!) Mat_(int _rows, int _cols, _Tp* _data, size_t _step=AUTO_STEP); // 小行列式の選択 Mat_(const Mat_& m, const Range& rowRange, const Range& colRange); Mat_(const Mat_& m, const Rect& roi); // 複雑な matrix expression をサポートするため Mat_(const MatExpr_Base& expr); // Vec や std::vector から 1 列だけの行列を作成します. template explicit Mat_(const Vec<_Tp, n>& vec); Mat_(const vector<_Tp>& vec, bool copyData=false); Mat_& operator = (const Mat& m); Mat_& operator = (const Mat_& m); // 全ての要素を s にセットします. Mat_& operator = (const _Tp& s); // 行末のギャップをスキップする優れたイテレータ iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; // Mat::create(_rows, _cols, DataType<_Tp>::type) と等価 void create(int _rows, int _cols); void create(Size _size); // 外積 Mat_ cross(const Mat_& m) const; // 複雑な matrix expression をサポートするため Mat_& operator = (const MatExpr_Base& expr); // データ型の変換 template operator Mat_() const; // Mat::row() などのオーバーライド Mat_ row(int y) const; Mat_ col(int x) const; Mat_ diag(int d=0) const; Mat_ clone() const; // 転置,逆行列,要素同士の掛け算 MatExpr_<...> t() const; MatExpr_<...> inv(int method=DECOMP_LU) const; MatExpr_<...> mul(const Mat_& m, double scale=1) const; MatExpr_<...> mul(const MatExpr_<...>& m, double scale=1) const; // Mat::elemSize() などのオーバーライド size_t elemSize() const; size_t elemSize1() const; int type() const; int depth() const; int channels() const; size_t step1() const; // step()/sizeof(_Tp) を返します. size_t stepT() const; // Mat::zeros() などのオーバーライド.もちろん,データ型の指定は省略されます. static MatExpr_Initializer zeros(int rows, int cols); static MatExpr_Initializer zeros(Size size); static MatExpr_Initializer ones(int rows, int cols); static MatExpr_Initializer ones(Size size); static MatExpr_Initializer eye(int rows, int cols); static MatExpr_Initializer eye(Size size); // その他のオーバーライドメソッド Mat_ reshape(int _rows) const; Mat_& adjustROI( int dtop, int dbottom, int dleft, int dright ); Mat_ operator()( const Range& rowRange, const Range& colRange ) const; Mat_ operator()( const Rect& roi ) const; // 行と要素へのアクセス演算子の,より便利な形式 _Tp* operator [](int y); const _Tp* operator [](int y) const; _Tp& operator ()(int row, int col); const _Tp& operator ()(int row, int col) const; _Tp& operator ()(Point pt); const _Tp& operator ()(Point pt) const; // 複雑な matrix expression をサポートするため operator MatExpr_() const; // ベクトルへの変換 operator vector<_Tp>() const; }; .. ``Mat_<_Tp>`` クラスは, ``Mat`` クラスに被せる「薄い」テンプレートラッパーです.追加のデータフィールドは一切なく,これも ``Mat`` も仮想メソッドをまったく持たないので,これら2つのクラスへの参照やポインタは,互いに自由に変換できます.しかし,そうする場合には注意が必要です.例えば: :: // 100x100 , 8 ビットの行列を作成する Mat M(100,100,CV_8U); // これは,コンパイル可能です.データ変換は行われません. Mat_& M1 = (Mat_&)M; // プログラムは,以下の文でクラッシュします. M1(99,99) = 1.f; .. 大抵の場合 ``Mat`` で十分ですが,要素アクセス演算子をたくさん利用し,コンパイル時に行列の型が分かっている場合は, ``Mat_`` の方がより便利です. ``Mat::at<_Tp>(int y, int x)`` および ``Mat_<_Tp>::operator ()(int y, int x)`` は完全に等価で同じ速度で動作しますが,後者の方が確実に短く書けることに注意してください: :: Mat_ M(20,20); for(int i = 0; i < M.rows; i++) for(int j = 0; j < M.cols; j++) M(i,j) = 1./(i+j+1); Mat E, V; eigen(M,E,V); cout << E.at(0,0)/E.at(M.rows-1,0); .. *マルチチャンネルの画像/行列の場合は ``Mat_`` をどうやって使うのでしょうか?* これは簡単 - ``Vec`` を ``Mat_`` のパラメータとして渡すだけです: :: // 320x240 のカラー画像を確保して,( RGB 空間の)緑色で埋めます. Mat_ img(240, 320, Vec3b(0,255,0)); // 対角線上に白い線を描きます. for(int i = 0; i < 100; i++) img(i,i)=Vec3b(255,255,255); // 各ピクセルの 2 番目(赤)のチャンネルをスクランブルします. for(int i = 0; i < img.rows; i++) for(int j = 0; j < img.cols; j++) img(i,j)[2] ^= (uchar)(i ^ j); .. MatND ----- n次元の密な配列. :: class MatND { public: // デフォルトコンストラクタ MatND(); // 指定されたサイズとデータ型の配列を作成します. MatND(int _ndims, const int* _sizes, int _type); // 配列を作成して,指定された値で埋めます. MatND(int _ndims, const int* _sizes, int _type, const Scalar& _s); // コピーコンストラクタ.ヘッダのみがコピーされます. MatND(const MatND& m); // 部分配列の選択.ヘッダのみがコピーされます. MatND(const MatND& m, const Range* ranges); // 古い形式の ND配列 を MatND に変換します.オプションでデータをコピーできます. MatND(const CvMatND* m, bool copyData=false); ~MatND(); MatND& operator = (const MatND& m); // 行列の完全なコピーを作成します(すべてのデータがコピーされます). MatND clone() const; // 部分配列の選択.ヘッダのみがコピーされます. MatND operator()(const Range* ranges) const; // データを他の行列にコピーします. // データをコピーする前に, // m.create(this->size(), this->type()) を呼びます. void copyTo( MatND& m ) const; // 選択された要素のみを別の行列にコピーします. void copyTo( MatND& m, const MatND& mask ) const; // データを指定されたデータ型に変換します. // 変換の前に m.create(this->size(), rtype) を呼びます. void convertTo( MatND& m, int rtype, double alpha=1, double beta=0 ) const; // 各配列要素に "s" を割り当てます. MatND& operator = (const Scalar& s); // 選択された配列要素に "s" を割り当てます. // ( mask==MatND() の場合はすべての要素) MatND& setTo(const Scalar& s, const MatND& mask=MatND()); // データをコピーせずに配列の形状を変えます. MatND reshape(int _newcn, int _newndims=0, const int* _newsz=0) const; // 現在の配列のサイズと型が,指定されたものと一致しない場合のみ, // データ用の新しいバッファを確保します. void create(int _ndims, const int* _sizes, int _type); // 参照カウンタを手動でインクリメントします(注意して使用してください!!!) void addref(); // 参照カウンタをデクリメントします. // カウンタが 0 になったときにデータが解放されます. void release(); // 行列を 2 次元の Mat あるいは 古い形式の CvMatND に変換します. // どちらの場合も,データはコピーされません. operator Mat() const; operator CvMatND() const; // 配列データが連続して格納されている場合に true を返します. bool isContinuous() const; // バイト単位で表された各要素サイズを返します. size_t elemSize() const; // バイト単位で表されたチャンネル毎の要素サイズを返します. size_t elemSize1() const; // OpenCV のデータ型 ID(CV_8UC1, ... CV_64FC4,...) を返します. int type() const; // ビット深度 (CV_8U ... CV_64F) を返します. int depth() const; // チャンネル数を返します. int channels() const; // step1() ~ step()/elemSize1() size_t step1(int i) const; // 要素へのポインタを返します( 1D, 2D, 3D, そして一般的な nD 版). uchar* ptr(int i0); const uchar* ptr(int i0) const; uchar* ptr(int i0, int i1); const uchar* ptr(int i0, int i1) const; uchar* ptr(int i0, int i1, int i2); const uchar* ptr(int i0, int i1, int i2) const; uchar* ptr(const int* idx); const uchar* ptr(const int* idx) const; // 要素アクセスのための便利なテンプレートメソッド. // _Tp は,実際の要素型と一致しなければならないことに注意してください - // この関数は,処理中にいかなる型の変換も行われません. template _Tp& at(int i0); template const _Tp& at(int i0) const; template _Tp& at(int i0, int i1); template const _Tp& at(int i0, int i1) const; template _Tp& at(int i0, int i1, int i2); template const _Tp& at(int i0, int i1, int i2) const; template _Tp& at(const int* idx); template const _Tp& at(const int* idx) const; enum { MAGIC_VAL=0x42FE0000, AUTO_STEP=-1, CONTINUOUS_FLAG=CV_MAT_CONT_FLAG, MAX_DIM=CV_MAX_DIM }; // データ型,連続性フラグ,シグネチャ(マジックナンバー)の組み合わせ int flags; // 配列の次元 int dims; // データの参照カウンタ int* refcount; // データへのポインタ uchar* data; // そして,データの実際の開始点と終了点 uchar* datastart; uchar* dataend; // 各次元の step と size ,最大で MAX_DIM int size[MAX_DIM]; size_t step[MAX_DIM]; }; .. ``MatND`` クラスは,シングルチャンネル,あるいはマルチチャンネルで, n 次元の,密な数値配列を表します.これは,多次元ヒストグラム(極端に疎ではない場合.疎な場合は ``SparseMat`` の方が適しています),ボクセルボリューム,スタックモーションフィールドなどを表現するのに便利です.行列 :math:`M` のデータレイアウトは, ``M.step[]`` の配列によって定義され,要素 :math:`(i_0,...,i_{M.dims-1})` (ここで :math:`0\leq i_k it = image.begin(), it_end = image.end(); for( ; it != it_end; ++it ) { const Vec3b& pix = *it; // セルの増加量 1.f の代わりに 1.f/(image.rows*image.cols) // を利用して,ヒストグラムを正規化することもできたかも. hist.at(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f; } } .. つぎに, ``MatND`` の要素を反復処理で横断する方法を示します: :: void normalizeColorHist(MatND& hist) { #if 1 // イテレータを初期化します(この形式は STL とは異なります). // 初期化後のイテレータは,これから横断する // スライス数 or 平面数を含みます. MatNDIterator it(hist); double s = 0; // 行列を反復処理で横断します.各反復において, // it.planes[*] (of type Mat) は,現在の平面の集合です. for(int p = 0; p < it.nplanes; p++, ++it) s += sum(it.planes[0])[0]; it = MatNDIterator(hist); s = 1./s; for(int p = 0; p < it.nplanes; p++, ++it) it.planes[0] *= s; #elif 1 // こちらは,MatND のビルトイン処理を利用した, // 上記のものより短い実装です. double s = sum(hist)[0]; hist.convertTo(hist, hist.type(), 1./s, 0); #else // そして,さらに短いバージョン. // (ヒストグラムの要素が非負であることを仮定しています) normalize(hist, hist, 1, 0, NORM_L1); #endif } .. 複数の行列が同じ形状(次元数とすべての次元サイズが等しい状態)である限り,同時に横断することができます.これは,この様な行列に対する 2 要素のあるいは n 要素の比較処理において役立ちます.そのためには,これらの行列を ``MatNDIterator`` に渡してください.すると,反復処理が行われる間, ``it.planes[0]`` , ``it.planes[1]`` , ... は,対応する行列のスライスを表します. MatND\_ ------- :func:`MatND` から派生した,n次元の密な配列のクラステンプレート. :: template class MatND_ : public MatND { public: typedef _Tp value_type; typedef typename DataType<_Tp>::channel_type channel_type; // 様々なコンストラクタ. MatND のものと同じですが,型の指定は省略されます. MatND_(); MatND_(int dims, const int* _sizes); MatND_(int dims, const int* _sizes, const _Tp& _s); MatND_(const MatND& m); MatND_(const MatND_& m); MatND_(const MatND_& m, const Range* ranges); MatND_(const CvMatND* m, bool copyData=false); MatND_& operator = (const MatND& m); MatND_& operator = (const MatND_& m); // 別の初期化関数 // Scalar の代わりに _Tp をとります. MatND_& operator = (const _Tp& s); // 特別なデストラクタは不要です. MatND のものを利用します. void create(int dims, const int* _sizes); template operator MatND_() const; MatND_ clone() const; MatND_ operator()(const Range* ranges) const; size_t elemSize() const; size_t elemSize1() const; int type() const; int depth() const; int channels() const; // step[i]/elemSize() size_t stepT(int i) const; size_t step1(int i) const; // MatND::at<_Tp> よりも短く書ける代替手段 _Tp& operator ()(const int* idx); const _Tp& operator ()(const int* idx) const; _Tp& operator ()(int idx0); const _Tp& operator ()(int idx0) const; _Tp& operator ()(int idx0, int idx1); const _Tp& operator ()(int idx0, int idx1) const; _Tp& operator ()(int idx0, int idx1, int idx2); const _Tp& operator ()(int idx0, int idx1, int idx2) const; _Tp& operator ()(int idx0, int idx1, int idx2); const _Tp& operator ()(int idx0, int idx1, int idx2) const; }; .. ``MatND_`` と ``MatND`` の関係は, ``Mat_`` と ``Mat`` の関係とほぼ同じです - つまり,多少便利な要素アクセス演算子を提供するだけで,基底クラスに対して仮想メソッドのメンバを追加したりはしません.従って, ``MatND_`` と ``MatND`` への参照/ポインタは,容易に相互変換できます.例えば: :: // 前述のヒストグラムを計算するループの別のバージョン ... CV_Assert(hist.type() == CV_32FC1); MatND_& _hist = (MatND_&)hist; for( ; it != it_end; ++it ) { const Vec3b& pix = *it; _hist(pix[0]*N/256, pix[1]*N/256, pix[2]*N/256) += 1.f; } ... .. SparseMat --------- n次元の疎な配列. :: class SparseMat { public: typedef SparseMatIterator iterator; typedef SparseMatConstIterator const_iterator; // 内部構造体 - 疎な行列のヘッダ struct Hdr { ... }; // 疎な行列のノード - ハッシュテーブルの要素 struct Node { size_t hashval; size_t next; int idx[CV_MAX_DIM]; }; ////////// コンストラクタとデストラクタ ////////// // デフォルトコンストラクタ SparseMat(); // 指定されたサイズと型の行列を作成します. SparseMat(int dims, const int* _sizes, int _type); // コピーコンストラクタ SparseMat(const SparseMat& m); // 2 次元の密な行列を,疎な形に変換します. // try1d が true かつ,行列が 1 列の場合 (Nx1) は, // 疎な行列も 1 次元になります. SparseMat(const Mat& m, bool try1d=false); // n 次元の密な行列を,疎な形に変換します. SparseMat(const MatND& m); // 古い形式の疎な行列を,新しい形式に変換します. // 変換後に "m" を安全に解放できるように, // すべてのデータがコピーされます. SparseMat(const CvSparseMat* m); // デストラクタ ~SparseMat(); ///////// 代入演算子 /////////// // これは O(1) の処理.データはコピーされません. SparseMat& operator = (const SparseMat& m); // ( try1d=false の場合のコンストラクタと等価) SparseMat& operator = (const Mat& m); SparseMat& operator = (const MatND& m); // 行列の完全なコピーを作成します. SparseMat clone() const; // 出力行列にすべてのデータをコピーします. // 必要ならば,出力行列は再割り当てされます. void copyTo( SparseMat& m ) const; // 1 次元あるいは 2 次元の疎な行列を, 2 次元の密な行列に変換します. // 疎な行列が 1 次元である場合,結果として得られる密な行列は, // 1 列の行列になります. void copyTo( Mat& m ) const; // 任意の疎な行列を密な行列に変換します. // 使用メモリに注意してください! void copyTo( MatND& m ) const; // 行列のすべての要素に,指定したスカラを掛けます. void convertTo( SparseMat& m, int rtype, double alpha=1 ) const; // 疎な行列を,密な行列に変換します.型の変換やスケーリングなども可能です. // rtype=-1 の場合,出力される密な行列の要素型は, // 元の疎な行列の要素型と同じになります. // それ以外の場合,ビット深度は rtype で指定された値に, // チャンネル数は,疎な行列のものと同じになります. void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 ) const; void convertTo( MatND& m, int rtype, double alpha=1, double beta=0 ) const; // 今は利用されません. void assignTo( SparseMat& m, int type=-1 ) const; // 疎な行列を再割り当てします.既に適切なサイズと型の行列である場合, // 単に clear() によってデータがクリアされます.そうでない場合, // 古い行列は( release() によって)解放され,新しい行列が確保されます. void create(int dims, const int* _sizes, int _type); // すべての行列要素を 0 にします.これは,ハッシュテーブルのクリアを意味します. void clear(); // ヘッダの参照カウンタを手動でインクリメントします. void addref(); // ヘッダの参照カウンタをデクリメントします.カウンタが 0 になると // ヘッダとすべての内部データが解放されます. void release(); // 疎な行列を古い形式の表現に変換します. // すべての要素がコピーされます. operator CvSparseMat*() const; // バイト単位で表された各要素サイズ // (要素のインデックスや SparseMat::Node 要素が存在するので, //  行列のノードサイズはもっと大きくなります) size_t elemSize() const; // elemSize()/channels() size_t elemSize1() const; // Mat や MatND のものと同じです. int type() const; int depth() const; int channels() const; // サイズの配列を返し,行列が確保されていない場合は 0 を返します. const int* size() const; // i 番目のサイズを返します(あるいは 0 ). int size(int i) const; // 行列の次元数を返します. int dims() const; // 0 ではない要素の個数を返します. size_t nzcount() const; // 要素のインデックスから,要素のハッシュ値を計算します: // 1D の場合 size_t hash(int i0) const; // 2D の場合 size_t hash(int i0, int i1) const; // 3D の場合 size_t hash(int i0, int i1, int i2) const; // n-D の場合 size_t hash(const int* idx) const; // 低レベルな要素アクセス関数: // 1D, 2D, 3D の場合の特別版と, n-D の場合の汎用版があります. // // 行列要素へのポインタを返します. // 要素が存在する( 0 ではない)場合,そのポインタが返されます. // 要素が存在せず createMissing=false の場合, NULL ポインタが返されます. // 要素が存在せず createMissing=true の場合,新しい要素が作成されて // 0 で初期化されます.そして,そのポインタが返されます. // hashval ポインタが NULL でない場合,要素のハッシュ値は計算されずに, // *hashval が代わりに利用されます. uchar* ptr(int i0, bool createMissing, size_t* hashval=0); uchar* ptr(int i0, int i1, bool createMissing, size_t* hashval=0); uchar* ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0); uchar* ptr(const int* idx, bool createMissing, size_t* hashval=0); // 高レベルな要素アクセス関数: // ref<_Tp>(i0,...[,hashval]) - *(_Tp*)ptr(i0,...true[,hashval]) と等価. // 常に要素への有効な参照を返します. // 要素が存在しない場合は,新たに作成します. // find<_Tp>(i0,...[,hashval]) - (_const Tp*)ptr(i0,...false[,hashval]) と等価. // 要素が存在すればそのポインタ,存在しなければ NULL を返します. // value<_Tp>(i0,...[,hashval]) -  // { const _Tp* p = find<_Tp>(i0,...[,hashval]); return p ? *p : _Tp(); } と等価. // つまり, 要素が存在しなければ 0 を返します. // _Tp は,実際の要素型と一致しなければならないことに注意してください - // この関数は,処理中にいかなる型の変換も行いません. // 1D の場合 template _Tp& ref(int i0, size_t* hashval=0); template _Tp value(int i0, size_t* hashval=0) const; template const _Tp* find(int i0, size_t* hashval=0) const; // 2D の場合 template _Tp& ref(int i0, int i1, size_t* hashval=0); template _Tp value(int i0, int i1, size_t* hashval=0) const; template const _Tp* find(int i0, int i1, size_t* hashval=0) const; // 3D の場合 template _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); template _Tp value(int i0, int i1, int i2, size_t* hashval=0) const; template const _Tp* find(int i0, int i1, int i2, size_t* hashval=0) const; // n-D の場合 template _Tp& ref(const int* idx, size_t* hashval=0); template _Tp value(const int* idx, size_t* hashval=0) const; template const _Tp* find(const int* idx, size_t* hashval=0) const; // 指定された行列要素を削除します. // 要素が存在しない場合は何もしません. void erase(int i0, int i1, size_t* hashval=0); void erase(int i0, int i1, int i2, size_t* hashval=0); void erase(const int* idx, size_t* hashval=0); // 行列のイテレータを返します. // これは,疎な行列の最初の要素を指すか, SparseMatIterator begin(); SparseMatConstIterator begin() const; // ... あるいは,最後の要素の後ろを指します. SparseMatIterator end(); SparseMatConstIterator end() const; // 上述のメソッドのテンプレート版 // _Tp は,実際の行列型と一致しなければいけません. template SparseMatIterator_<_Tp> begin(); template SparseMatConstIterator_<_Tp> begin() const; template SparseMatIterator_<_Tp> end(); template SparseMatConstIterator_<_Tp> end() const; // 疎な行列のノードに格納されている値を返します. template _Tp& value(Node* n); template const _Tp& value(const Node* n) const; ////////////// 内部で利用されるメソッド /////////////// ... // 疎な行列のヘッダへのポインタ Hdr* hdr; }; .. ``SparseMat`` クラスは,多次元の疎な数値配列を表します.この疎な配列は, :func:`Mat` や :func:`MatND` が格納できるあらゆる型の要素を格納できます.「疎な」というのは,0 ではない要素だけが格納されていることを意味します(しかし,疎な行列を処理した結果,その行列の要素が実際には 0 になる可能性はあります.そのような要素を検出して, ``SparseMat::erase`` を用いて削除するかどうかはユーザ次第です).0 ではない要素は,ハッシュテーブルに格納されます.また,(要素が存在する・しないに関係なく)平均探索時間が O(1) となるように,多くの値が埋められるとハッシュテーブルは拡張されます.要素には以下のメソッドを利用してアクセスできます: #. クエリ操作( ``SparseMat::ptr`` と,高レベル関数 ``SparseMat::ref`` , ``SparseMat::value`` , ``SparseMat::find`` ),つまり: :: const int dims = 5; int size[] = {10, 10, 10, 10, 10}; SparseMat sparse_mat(dims, size, CV_32F); for(int i = 0; i < 1000; i++) { int idx[dims]; for(int k = 0; k < dims; k++) idx[k] = rand() sparse_mat.ref(idx) += 1.f; } .. #. 疎な行列のイテレータ. :func:`Mat` のイテレータと同様に,また :func:`MatND` のイテレータとは異なり,疎な行列のイテレータは STL 形式であり,この反復ループは C++ ユーザにとって馴染みあるものでしょう: :: // 疎な浮動小数点型行列の要素と, // その要素の合計を表示します. SparseMatConstIterator_ it = sparse_mat.begin(), it_end = sparse_mat.end(); double s = 0; int dims = sparse_mat.dims(); for(; it != it_end; ++it) { // 要素のインデックスと要素の値を表示します. const Node* n = it.node(); printf("(") for(int i = 0; i < dims; i++) printf(" printf(": s += *it; } printf("Element sum is .. このループを走らせると,要素の表示順がいかなる論理的順序(辞書順,など)にも従わないことに気付くでしょう.これらは,ハッシュテーブルに格納された順番に,つまり,半乱数的に並んでいます.そこで,ノードへのポインタを集めて,適切な順序にソートしようとするかもしれません.しかしそうすると,後から行列に要素を加えたときに,バッファが再割り当てされて,ノードへのポインタが無効になってしまう可能性があることに注意しなければいけません. #. 2つ以上の疎な行列を同時に処理する必要がある場合の,上述の2つのメソッドの組み合わせ.例えば,次のようにすれば,2つの疎な浮動小数点型行列同士の正規化されていない相関を求めることができます: :: double cross_corr(const SparseMat& a, const SparseMat& b) { const SparseMat *_a = &a, *_b = &b; // b の要素数が a よりも少ない場合, // b を反復横断した方が高速です. if(_a->nzcount() > _b->nzcount()) std::swap(_a, _b); SparseMatConstIterator_ it = _a->begin(), it_end = _a->end(); double ccorr = 0; for(; it != it_end; ++it) { // 最初の行列から,次の要素を取り出します. float avalue = *it; const Node* anode = it.node(); // 2 番目の行列で同じインデックスをもつ要素を見つけようとします. // ハッシュ値は要素のインデックスにのみ依存するので, // ノードに格納されたハッシュ値を再利用します. float bvalue = _b->value(anode->idx,&anode->hashval); ccorr += avalue*bvalue; } return ccorr; } .. SparseMat\_ ----------- :func:`SparseMat` から派生した,疎なn次元配列のクラステンプレート. :: template class SparseMat_ : public SparseMat { public: typedef SparseMatIterator_<_Tp> iterator; typedef SparseMatConstIterator_<_Tp> const_iterator; // コンストラクタ; // データ型が = DataType<_Tp>::type となる行列を作成します. SparseMat_(); SparseMat_(int dims, const int* _sizes); SparseMat_(const SparseMat& m); SparseMat_(const SparseMat_& m); SparseMat_(const Mat& m); SparseMat_(const MatND& m); SparseMat_(const CvSparseMat* m); // 代入演算子.必要ならばデータ型を変換します. SparseMat_& operator = (const SparseMat& m); SparseMat_& operator = (const SparseMat_& m); SparseMat_& operator = (const Mat& m); SparseMat_& operator = (const MatND& m); // 基底クラスの対応するメソッドと同じです. SparseMat_ clone() const; void create(int dims, const int* _sizes); operator CvSparseMat*() const; // データ型のチェックが加えられたオーバーライドメソッド int type() const; int depth() const; int channels() const; // より便利な要素アクセス演算子. // ref() はそのまま(ただし, <_Tp> の指定は必要ありません) // operator () は SparseMat::value<_Tp> と等価です. _Tp& ref(int i0, size_t* hashval=0); _Tp operator()(int i0, size_t* hashval=0) const; _Tp& ref(int i0, int i1, size_t* hashval=0); _Tp operator()(int i0, int i1, size_t* hashval=0) const; _Tp& ref(int i0, int i1, int i2, size_t* hashval=0); _Tp operator()(int i0, int i1, int i2, size_t* hashval=0) const; _Tp& ref(const int* idx, size_t* hashval=0); _Tp operator()(const int* idx, size_t* hashval=0) const; // イテレータ SparseMatIterator_<_Tp> begin(); SparseMatConstIterator_<_Tp> begin() const; SparseMatIterator_<_Tp> end(); SparseMatConstIterator_<_Tp> end() const; }; .. ``SparseMat_`` は, ``Mat_`` や ``MatND_`` と同じように作成された, :func:`SparseMat` に被せる薄いラッパーです. これによって,いくつかの処理が次のように簡単に書けるようになります. :: int sz[] = {10, 20, 30}; SparseMat_ M(3, sz); ... M.ref(1, 2, 3) = M(4, 5, 6) + M(7, 8, 9); ..