================= cv::Matの基本処理 ================= .. highlight:: cpp cv::Matの概要 ============= OpenCV 1.x には,基本的に C言語 および Python のインタフェースが用意されていましたが,OpenCV 2.0 以降では,新たに C++ インタフェースが追加されました. OpenCV 1.x では,画像を管理する構造体として ``IplImage`` が,その他の行列を管理する構造体として ``CvMat`` が用いられました. しかし,OpenCV 2.x の C++ インタフェースでは, ``cv::Mat`` クラスを利用して,これらを統一的に扱います. ``cv::Mat`` クラスは,実際のデータへのポインタと,様々なプロパティ(幅,高さ,ビット深度など)を保持します. +------------------------+-----------------------+-----------------------+-----------------------+ | | 1.0/1.1pre | 2.0/2.1 | 2.2/2.3 | +------------------------+------------+----------+------------+----------+------------+----------+ | Interface | C | C++ | C | C++ | C | C++ | +========================+============+==========+============+==========+============+==========+ | Image | IplImage | | IplImage | Mat | IplImage | Mat | +------------------------+------------+----------+------------+----------+------------+----------+ | Matrix | CvMat | | CvMat | Mat | CvMat | Mat | +------------------------+------------+----------+------------+----------+------------+----------+ | N-dimentional Matrix | CvMatND | | CvMatND | MatND | CvMatND | Mat | +------------------------+------------+----------+------------+----------+------------+----------+ | Sparse Matrix | CvSparseMat| | CvSparseMat| SparseMat| CvSparseMat| SparseMat| +------------------------+------------+----------+------------+----------+------------+----------+ マルチチャンネルと多次元配列 ---------------------------- OpenCV 2.2 以降,2次元配列用の ``cv::Mat`` クラスと,多次元配列用の ``cv::MatND`` クラスが統一されました.つまり,現在は, ``cv::Mat`` で任意次元の配列を表現します. しかし,今まで ``cv::Mat`` を引数に取っていた関数が,すべて「多次元(>=3次元)」の Mat に対応しているとは限らないので注意が必要です. OpenCVの構造には,次元とチャンネルの概念があります. * チャンネル(channel):要素の次元 * 次元(dimension):複数の要素からなる配列の次元 マルチチャンネル多次元配列の例(Dim=2 Ch=2, Dim=2 Ch=1, Dim=2 Ch=3, Dim=3 Ch=4): .. image:: image/mat17c2.png :scale: 80% .. image:: image/space.png :scale: 50% .. image:: image/mat57c1.png :scale: 80% .. image:: image/space.png :scale: 50% .. image:: image/mat57c3.png :scale: 80% .. image:: image/space.png :scale: 50% .. image:: image/mat57xc4.png :scale: 80% 上記の例のように, :math:`1\times x` や :math:`x\times 1` の配列は,1次元ベクトルではなく,1行の2次元行列と見なされることに注意してください.つまり,Mat の次元は,必ず2以上になります. 次元数の最大値は ``CV_MAX_DIM`` (OpenCV 2.2/2.3 では =32)で定義されます. また,4チャンネル以上の Mat を作成する場合は,要素の型として ``CV_8UC(n)``, ..., ``CV_64FC(n)`` を指定してください. 作成された Mat の,チャンネル数や次元数,その他のプロパティを得る方法は, :ref:`mat_various_properties` を参照してください. .. image:: image/mat57gray.png :scale: 80% .. image:: image/space.png .. image:: image/mat57color.png :scale: 80% Mat で画像を表現する場合,各チャンネルは「色平面」と呼ばれることもあります.画像の1つのチャンネルが1つの色(あるいは輝度や彩度など)を表すためです.カラー画像では,RGB表現が良く利用されますが,OpenCVのチャンネルの順番は「BGR」になっていることに注意してください. 1チャンネルを表現するためのビット数と,{U|S|F}の数値表現ID,チャンネル数を合わせて, ``CV_{U|S|F}C`` の形式で ``CV_8UC1`` (符号なし8ビット整数型,1チャンネル)や ``CV_32FC2`` (32ビット浮動小数点型,2チャンネル)のような一意な識別子を与える事ができます.これが Mat のデータ型となります. これらは ``types_c.h`` ヘッダで次の様に定義されています. :: #define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT)) ... #define CV_8UC1 CV_MAKETYPE(CV_8U,1) #define CV_8UC2 CV_MAKETYPE(CV_8U,2) ... #define CV_64FC1 CV_MAKETYPE(CV_64F,1) #define CV_64FC2 CV_MAKETYPE(CV_64F,2) ... 上述のように,CV_8U や CV_32F などの ``CV_`` という表記と, CV_8UC1 や CV_32FC1 などの1チャンネルの表記 ``CV_C1`` は一致するので,コンストラクタなどで,1チャンネルの型を指定する場合は, ``CV_8UC1`` , ``CV_8U`` のどちらでも構いません. 以下に,マルチチャンネル多次元配列の作成の例を示します. .. literalinclude:: code/cpp/sample_multi_dim_ch.cpp 実行結果: .. literalinclude:: output/cpp/sample_multi_dim_ch.txt 浅いコピーと深いコピー ---------------------- cv::Matを代入演算子でコピーすると,浅いコピー(shallow copy)が行われ,コピー元とコピー先とcv::Matでデータが共有されます.この場合,最も大きいメンバであるデータ自身が実際にはコピーされないので,cv::Matのコピー動作はサイズに依存せず高速に行われます.それぞれのcv::Matのdataポインタは,同じメモリアドレスを指すので,コピー元cv::Matのデータを書き換えると,コピー先cv::Matのデータも変わることになります. コピー元とコピー先を別データとしてコピーするには,clone()メソッドを利用して,深いコピー(deep copy)を行います. clone()メソッド内部では,新たなcv::Matが作成され,そこにcopyTo()メソッドでデータ全体がコピーされます. .. image:: image/shallow_copy.png :scale: 80% .. image:: image/space.png .. image:: image/deep_copy.png :scale: 80% .. literalinclude:: code/cpp/sample_mat_copy.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_copy.txt 連続データと不連続データ ------------------------ 通常,cv::Matのデータはメモリ上の連続した領域に割り当てられます. しかし,IplImageからのキャストを行った場合や,ROIを利用した場合など,このデータが不連続になる場合があります. ポインタを利用してデータにアクセスする場合,データの連続・不連続の問題を意識することが重要です. cv::Matのデータがメモリ上で連続しているかを調べるには, :ref:`mat_various_properties` を参照してください. .. image:: image/continuous_discontinuous.png :scale: 80% cv::Mat の内容を std::cout に出力する ======================================== デフォルトの出力 ---------------- OpenCV 2.2 から << 演算子で,std::cout への出力が可能になりました. MatExpr,Point_<_Tp>, Point3_<_Tp> や,マルチチャンネル配列なども出力可能です. .. literalinclude:: code/cpp/sample_cout.cpp 実行結果: .. literalinclude:: output/cpp/sample_cout.txt 出力フォーマットの指定 ---------------------- std::cout への出力は,デフォルトスタイル以外にも,python,numpy,csv,c言語の配列スタイルの出力フォーマットを選択できます. フォーマットの選択には, ``cv::format`` 関数を利用します. .. literalinclude:: code/cpp/sample_cout_format.cpp 実行結果: .. literalinclude:: output/cpp/sample_cout_format.txt cv::Matにおけるメモリの自動管理 ================================= まず,OpenCVの以前のバージョンと異なり,ほとんどの関数において,関数呼び出しの前に出力用の領域を確保しておく必要はありません. 多くの関数は,関数内で適切なサイズ,型,ビット深度の ``cv::Mat`` を確保します. また, ``cv::Mat`` は,そのデータ領域がどこからも参照されなくなると,自動的にメモリを解放します. これは,データを参照する cv::Mat ヘッダの個数をカウントし,これが0になった場合にデータを解放する,参照カウント(Reference Count)方式によって実現されます. また,ユーザが自分自身でデータ領域を割り当てた場合は,参照カウンタは null のままで,参照カウントは動作しません. .. literalinclude:: code/cpp/sample_mem_auto.cpp 参照カウント ------------- 参照カウンタの値は,データ領域と1対1で対応し,実データの先頭または末尾に置かれます. この値を見るには,refcount メンバを利用します. .. literalinclude:: code/cpp/sample_refcount.cpp 実行結果: .. literalinclude:: output/cpp/sample_refcount.txt cv::Matを初期化する =================== カンマ区切り初期化子 -------------------- このカンマ区切り初期化子には,定数だけではなく,変数,MatExpr などを利用できます. .. literalinclude:: code/cpp/sample_comma_init.cpp 実行結果: .. literalinclude:: output/cpp/sample_comma_init.txt .. #MatCommaInitializer\_ クラスのインスタンスが生成され .. # .. # template MatCommaInitializer_<_Tp>& operator , (T2 v); MATLABスタイルの行列初期化関数 ------------------------------ 次のような,MATLABスタイルの行列初期化関数が存在します. * cv::Mat::zeros() :行列要素を0で埋めて初期化します. * cv::Mat::ones() :行列要素を1で埋めて初期化します. * cv::Mat::eye() :単位行列で初期化します. ここで, ``cv::Mat::zeros()`` を2次元画像に対して用いた場合,黒色で画像を初期化することができます. .. literalinclude:: code/cpp/sample_matlab_init.cpp 実行結果: .. literalinclude:: output/cpp/sample_matlab_init.txt 配列の値で初期化する -------------------- .. literalinclude:: code/cpp/sample_array_init.cpp 実行結果: .. literalinclude:: output/cpp/sample_array_init.txt 定数で初期化する ---------------- .. literalinclude:: code/cpp/sample_const_init.cpp 実行結果: .. literalinclude:: output/cpp/sample_const_init.txt 乱数で初期化する ----------------- .. literalinclude:: code/cpp/sample_random_mat.cpp 実行結果: .. literalinclude:: output/cpp/sample_random_mat.txt マルチチャンネル配列を初期化する -------------------------------- マルチチャンネル配列を初期化したい場合,初期化されたシングルチャンネル配列を ``reshape`` で変形します. .. literalinclude:: code/cpp/sample_multi_ch_init.cpp 実行結果: .. literalinclude:: output/cpp/sample_multi_ch_init.txt cv::Matの行数・列数・次元数を調べる =================================== :ref:`mat_various_properties` を参照してください. cv::Matのビット深度を調べる =========================== :ref:`mat_various_properties` を参照してください. cv::Matのデータがメモリ上で連続しているかを調べる ================================================= :ref:`mat_various_properties` を参照してください. cv::Matが部分配列かを調べる =========================== :ref:`mat_various_properties` を参照してください. .. _mat_various_properties: cv::Matの様々なプロパティ ========================= シングルチャンネル,2次元配列 ----------------------------- .. literalinclude:: code/cpp/sample_mat_properties1.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_properties1.txt マルチチャンネル,2次元配列の部分配列 ------------------------------------- .. literalinclude:: code/cpp/sample_mat_properties2.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_properties2.txt マルチチャンネル,5次元配列 ----------------------------- .. literalinclude:: code/cpp/sample_mat_properties3.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_properties3.txt cv::Matの型(ビット深度)を変換する =================================== .. literalinclude:: code/cpp/sample_mat_convertTo.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_convertTo.txt cv::Matをリサイズする ===================== STL vector のように,Matのサイズを変更することができます.ただし,変更できるのは行単位でのみです. つまり,3x3の行列を2x3の行列に縮小したり,4x3の行列に拡大したりすることができます. .. literalinclude:: code/cpp/sample_mat_resize.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_resize.txt cv::Matを変形する ================= 前述のサイズ変更とは別に,Matの全体のサイズ(全次元サイズとチャンネルの積)を変えずに,その形状だけを変更することができます. この変形は,「1チャンネル3x3の行列」を「1チャンネル1x9の行列(行ベクトル)」に変形するような「行数の変形操作」と, 「3チャンネル1x3の行列」を「1チャンネル1x9の行列」に変形するような「チャンネル数の変形操作」から成り立ちます. これらを行うメソッド ``reshape`` では,変形後のチャンネル数と行数を指定します. このメソッドは,マルチチャンネル行列を初期化したり,座標点群のベクトルを Mat 形式にしたりする際に非常に役立ちます. .. literalinclude:: code/cpp/sample_mat_reshape.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_reshape.txt ちなみに,多次元 Mat に対する ``reshape`` は,下記の様にまだ実装されていません. matrix.cpp :: Mat Mat::reshape(int, int, const int*) const { CV_Error(CV_StsNotImplemented, ""); // TBD return Mat(); } cv::Matの特定の行/列を取り出す ============================== 行列の特定の行/列,または特定の範囲の複数行/列,を取り出すことができます. .. literalinclude:: code/cpp/sample_mat_range.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_range.txt cv::Matの部分行列を取り出す ============================ 行列の部分行列,を取り出すことができます. .. literalinclude:: code/cpp/sample_mat_roi.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_roi.txt .. _mat_merge_mix_seperate: cv::Mat のチャンネルの合成と分離 ================================= 例えば,個別の行列として存在する実数部と虚数部を1つの複素行列に合成する場合や,画像のR,G,Bのチャンネルを個別に処理したい場合などを考えると, チャンネルの合成や分離といった操作が必要になります. OpenCVでは,複数チャンネルを合成する関数として ``cv::merge`` と ``cv::mixChannels`` が用意されています. ``cv::merge`` は,複数の1チャンネル cv::Mat を,1つの cv::Mat に合成する関数です. 一方で ``cv::mixChannels`` は,入力としてマルチチャンネルの cv::Mat を与えることができ,出力の cv::Mat も複数指定することができます. 以下では,それぞれの関数の使い方を示します. 単純な合成:merge ----------------- .. literalinclude:: code/cpp/sample_mat_merge.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_merge.txt 複雑な合成:mixChannels ------------------------ ``cv::mixChannels`` では,入出力の cv::Mat の他に,どの「入力チャンネル」から,どの「出力チャンネル」に値がコピーされるかを示す配列を渡す必要があります. 例えば,入力が3チャンネル ``cv::Mat`` 1つ(src),出力が2チャンネル ``cv::Mat`` 2つ(dst1, dst2)である場合に, :: // src[0] => dst1[0] // src[1] => dst1[1] // src[1] => dst2[1] // src[2] => dst2[0] というようにチャンネルをコピーするとします.この場合,入力チャンネルのインデックスは,そのまま ``0〜2`` が利用されますが, 出力先が複数あるので,これらのインデックスは, ``dst1[0]`` が ``0`` , ``dst1[1]`` が ``1`` , ``dst2[0]`` が ``2`` , ``dst2[1]`` が ``3`` という様にインクリメントされます. つまり, :: // {入力1,出力1, 入力2,出力2, ..., 入力n,出力n} {0,0, 1,1, 1,3, 2,2} というペアを指定します. .. literalinclude:: code/cpp/sample_mat_mixchannels.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_mixchannels.txt 分離 ----- 合成とは逆に,マルチチャンネルの ``cv::Mat`` を,各チャンネル毎に分離して,1チャンネルの ``cv::Mat`` をもとのチャンネル数分だけ作ることもできます. .. literalinclude:: code/cpp/sample_mat_split.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_split.txt cv::Matの列/行毎の合計,平均値,最小,最大値を求める ===================================================== 例えば,1行が1つの特徴ベクトルである行列(行数は特徴ベクトルの個数)があるとして,それらのベクトルの平均を求めるにはどうすれば良いでしょうか. 1行1行足し合わせて個数で割ることも出来ますが, ``reduce`` メソッドを用いると,行列の行毎(または列毎)の,合計値や平均値などを簡単に計算することができます. .. literalinclude:: code/cpp/sample_reduce.cpp 実行結果: .. literalinclude:: output/cpp/sample_reduce.txt cv::Matの親行列に対する部分行列の位置を求める =============================================== .. literalinclude:: code/cpp/sample_mat_locateroi.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_locateroi.txt cv::Matの部分行列の範囲を拡大する ==================================== .. literalinclude:: code/cpp/sample_mat_adjustroi.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_adjustroi.txt cv::Matの要素をシャッフルする ============================= 実際には,「行数x列数x反復ファクタ」回だけ要素の入れ替えが行われます. シングルチャンネル行列のシャッフル ---------------------------------- .. literalinclude:: code/cpp/sample_mat_shuffle.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_shuffle.txt マルチチャンネル行列のシャッフル -------------------------------- マルチチャンネルの場合でも,1要素単位(=複数チャンネルから成る要素自身は保たれたまま)で入れ替えが行われます. つまり,1つのMat要素を校構成する複数のチャンネルの値は,シャッフル後も保たれたままになります. RGB画像の場合で考えると,1画素を構成するRGB値の値と順序は保たれるので,シャッフルで画素の位置は変わりますが,色は変わりません. .. literalinclude:: code/cpp/sample_mat_shuffle_multi.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_shuffle_multi.txt 2つのcv::Matをスワップする ========================== .. literalinclude:: code/cpp/sample_swap.cpp 実行結果: .. literalinclude:: output/cpp/sample_swap.txt cv::Matの行/列の要素をソートする ================================= ソートした値を出力 -------------------- .. literalinclude:: code/cpp/sample_sort.cpp 実行結果: .. literalinclude:: output/cpp/sample_sort.txt ソートしたインデックスを出力 ------------------------------ 同値の場合,元の順序が保存される保証はありません. .. literalinclude:: code/cpp/sample_sortidx.cpp 実行結果: .. literalinclude:: output/cpp/sample_sortidx.txt cv::Matの行列要素を,イテレータを介して扱う ============================================ .. literalinclude:: code/cpp/sample_mat_iterator.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat_iterator.txt .. _use_mat_T: cv::Mat_<_Tp>を使う ===================== ``cv::Mat_<_Tp>`` は, ``cv::Mat`` を継承したクラステンプレートです. ``cv::Mat_<_Tp>`` にも ``cv::Mat`` にも,virtual なメソッドは存在せず,データフィールドも同一なので,互いのポインタを自由に変換することができます. とは言っても,互いの data メンバの型が変換されるわけではないので,以下のような場合に注意が必要です. :: // 100x100 , 8 ビットの行列を作成 Mat M(100,100,CV_8U); // これは,コンパイル可能です.データ変換は行われません. Mat_& M1 = (Mat_&)M; // uchar型配列の最後の要素にfloatを書きこむことで,範囲外のアクセスが発生します. M1(99,99) = 1.f; また,コンパイル時に型が既知であるならば,cv::Mat_<_Tp> を利用すると,様々な処理を短く書くことができます. .. literalinclude:: code/cpp/sample_mat__mat.cpp 実行結果: .. literalinclude:: output/cpp/sample_mat__mat.txt .. _use_matx: cv::Matxを使う ============== このクラスは,コンパイル時に型とサイズが既知である小さい行列を表します.より柔軟な型が必要な場合は cv::Mat を利用してください. 行列の形状によっては,値を指定して初期化可能なコンストラクタが用意されています. :: template class Matx { public: typedef T value_type; enum { depth = DataDepth::value, channels = m*n, type = CV_MAKETYPE(depth, channels) }; // ... Tp val[m*n]; }; typedef Matx Matx12f; typedef Matx Matx12d; // ... typedef Matx Matx16f; typedef Matx Matx16d; typedef Matx Matx21f; typedef Matx Matx21d; //... typedef Matx Matx61f; typedef Matx Matx61d; typedef Matx Matx22f; typedef Matx Matx22d; //... typedef Matx Matx66f; typedef Matx Matx66d; 行列 M の要素には, ``cv::Mat_<_Tp>`` の場合と同様に M(i,j) という表記でアクセス可能です. また,一般的な行列演算の大部分が利用可能です. .. literalinclude:: code/cpp/sample_matx.cpp 実行結果: .. literalinclude:: output/cpp/sample_matx.txt cv::Vecを使う ============= ``Vec`` は, ``Matx`` の特別なケースと言えます. ``Matx`` は行列を表現しますが, ``Vec`` はベクトルを1行の行列として表現します. :: template class Vec : public Matx { public: typedef T value_type; enum { depth = DataDepth::value, channels = cn, type = CV_MAKETYPE(depth, channels) }; //... }; typedef Vec Vec2b; typedef Vec Vec3b; //... typedef Vec Vec6d; ``Vec`` と ``Point_`` は相互に変換可能で, ``Vec`` と ``Point3_`` も同様です.また, ``Vec`` は ``CvScalar`` や ``Scalar`` に変換することができます. ``Vec`` の要素にアクセスするには, ``operator[]`` を利用します. ``Vec`` クラスは通常,マルチチャンネル配列のピクセル型を記述するために利用されます. .. literalinclude:: code/cpp/sample_vec.cpp 実行結果: .. image:: image/sample_vec.png :scale: 80% .. _mat_iplimage_convert: cv::MatとIplImageの相互変換 =========================== .. literalinclude:: code/cpp/sample_iplimage_mat.cpp cv::MatとCvMatの相互変換 =========================== .. literalinclude:: code/cpp/sample_cvmat_mat.cpp cv::MatとSTL vectorの相互変換 ================================ .. literalinclude:: code/cpp/sample_vector_mat.cpp cv::Matとcv::Matxの相互変換 =========================== ``Matx`` から ``Mat`` への変換コンストラクタは,データをコピーするフラグのデフォルト値が ``true`` であり, ``CvMat`` や ``vector`` などとは異なることに注意してください. :ref:`use_matx` も参照してください. .. literalinclude:: code/cpp/sample_matx_mat.cpp cv::Matとcv::SparseMatの相互変換 ================================= 要素の大部分が0である疎な行列を効率的に保持するために,OpenCVには ``SparseMat`` クラスが用意されています. ``Mat`` から ``SparseMat`` に変換すると,値が0ではない要素のみがハッシュテーブルに保存されます. ``SparseMat`` から ``Mat`` に変換する場合,最初に0で埋められた ``Mat`` のインスタンスが作成され,そこに ``SparseMat`` のノードに保存された値が上書きされます. .. literalinclude:: code/cpp/sample_sparsemat_mat.cpp 実行結果: .. literalinclude:: output/cpp/sample_sparsemat_mat.txt cv::MatとEigen::Matrixの相互変換 ================================= cv::MatからEigen::Matrixへの変換 .. literalinclude:: code/cpp/sample_cv2eigen.cpp 実行結果: .. literalinclude:: output/cpp/sample_cv2eigen.txt Eigen::Matrixからcv::Matへの変換 .. literalinclude:: code/cpp/sample_eigen2cv.cpp 実行結果: .. literalinclude:: output/cpp/sample_eigen2cv.txt cv::Mat データの行アライメントを調整する ======================================== OpenCV によって割り当てられるメモりは,必ず16バイト境界にアライメントが調整されます.ここで,注意が必要なのは,アライメントが調整されるのはメモリ割り当て時のアドレスに対して,という点です.画像の行毎のメモリアドレスの話ではありません. IplImage を利用した場合は,さらに,確保された画像の行毎のアラインメントも調整されます. この,アライメントが調整されるバイト数は, IplImage->align によって決まり,デフォルト値は 4 [byte] と定義されています. modules/core/include/opencv2/core/internal.hpp:: /* default image row align (in bytes) */ #define CV_DEFAULT_IMAGE_ROW_ALIGN 4 しかし,cv::Mat によって確保されるデータは,デフォルトで連続データとなります.つまり,行毎のアライメント調整は行われません. この値についても,以下の様に 1 [byte] で定義されていますが,このマクロは(OpenCV2.2〜2.3では)実際には利用されていないので,この値を変更しても意味はありません. modules/core/include/opencv2/core/internal.hpp:: /* matrices are continuous by default */ #define CV_DEFAULT_MAT_ROW_ALIGN 1 データを行毎にコピーしてアライメントを調整する ---------------------------------------------- :ref:`mat_iplimage_convert` でも述べた様に,Mat を IplImage にキャストする場合はデータが共有されるので,結果的に行毎のアライメントが調整されていない IplImage データが作成されます. このような場合にアライメントを調整する最も単純な方法は,ユーザがデータを1行ずつコピーすることです. .. image:: image/align_copy.png :scale: 80% .. literalinclude:: code/cpp/sample_alignment_copy.cpp 実行結果: .. literalinclude:: output/cpp/sample_alignment_copy.txt ROIを利用してアライメントを調整する ----------------------------------- 上記の様にデータを1行ずつコピーする方法は,単純ですがあまり見た目の良いものではありません. また,IplImageではなく ``cv::Mat`` データ自体の行毎のアライメントを調整したい場合もあるでしょう. その場合,IplImage の場合と同様に1行ずつデータをコピーすることもできますが,コピー先にROIを適用すると ``copyTo`` メソッドを利用できます. .. image:: image/align_roi.png :scale: 80% .. literalinclude:: code/cpp/sample_alignment_roi.cpp 実行結果: .. literalinclude:: output/cpp/sample_alignment_roi.txt また,画像サイズと型が既知であれば,この様な ROI を設定した Mat に直接画像を読み込むこともできます. :: cv::Mat roi_img = cv::imread("image_name.png", 1); この方法では,複数チャンネルから成る1要素単位でしか親行列のサイズを変更できないため,上記の図ように無駄な領域が増えています. また,ROIを設定できるのは2次元の行列のみ,という制約もあります. .. _align_allocator: カスタムアロケータを利用してアライメントを調整する -------------------------------------------------- cv::Mat では,任意の方法で領域を割り当てることができるカスタムアロケータを指定することができます. ここでは,最初から行アライメントが調整された領域を割り当てるアロケータの例を示します. .. literalinclude:: code/cpp/sample_alignment_allocator.cpp 実行結果: .. literalinclude:: output/cpp/sample_alignment_allocator.txt ここで示した例は2次元の行列に対するものですが,多次元に対応したアロケータを指定することも可能です. cv::Matでカスタムアロケータを利用する ===================================== :ref:`align_allocator` を参照してください.