目標
- 画像のプロパティへのアクセス方法を学ぶ
- Matの構築方法を学ぶ
- Matのコピー方法を学ぶ
- Matの型の変換方法を学ぶ
- MatVectorの使い方を学ぶ
- ピクセル値へのアクセス方法とその変更方法を学ぶ
- 関心領域 (ROI) の設定方法を学ぶ
- 画像の分割と結合の方法を学ぶ
画像プロパティへのアクセス
画像のプロパティには、行数、列数とサイズ、ビット深度、チャンネル、画像データの型が含まれる。
let src = cv.imread("canvasInput");
console.log('image width: ' + src.cols + '\n' +
'image height: ' + src.rows + '\n' +
'image size: ' + src.size().width + '*' + src.size().height + '\n' +
'image depth: ' + src.depth() + '\n' +
'image channels ' + src.channels() + '\n' +
'image type: ' + src.type() + '\n');
- 覚え書き
- OpenCV.jsコードの多くのエラーは無効なデータ型が原因となるため、デバッグ時にsrc.type()は非常に重要である。
Matの構築方法
4つの基本的なコンストラクタがある:
// 1. default constructor
let mat = new cv.Mat();
// 2. two-dimensional arrays by size and type
let mat = new cv.Mat(size, type);
// 3. two-dimensional arrays by rows, cols, and type
let mat = new cv.Mat(rows, cols, type);
// 4. two-dimensional arrays by rows, cols, and type with initialization value
let mat = new cv.Mat(rows, cols, type, new cv.Scalar());
3つの静的関数がある:
// 1. Create a Mat which is full of zeros
let mat = cv.Mat.zeros(rows, cols, type);
// 2. Create a Mat which is full of ones
let mat = cv.Mat.ones(rows, cols, type);
// 3. Create a Mat which is an identity matrix
let mat = cv.Mat.eye(rows, cols, type);
2つのファクトリ関数がある:
// 1. Use JS array to construct a mat.
// For example: let mat = cv.matFromArray(2, 2, cv.CV_8UC1, [1, 2, 3, 4]);
let mat = cv.matFromArray(rows, cols, type, array);
// 2. Use imgData to construct a mat
let ctx = canvas.getContext("2d");
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let mat = cv.matFromImageData(imgData);
- 覚え書き
- cv.Mat を使わなくなったら削除するのを忘れないこと。
Matのコピー方法
Matをコピーする方法は2つある:
// 1. Clone
let dst = src.clone();
// 2. CopyTo(only entries indicated in the mask are copied)
src.copyTo(dst, mask);
Matの型の変換方法
次の関数を使う: convertTo(m, rtype, alpha = 1, beta = 0)
- 引数
-
| m | 出力行列。操作前に適切なサイズや型を持っていない場合は再確保される。 |
| rtype | 出力行列に求める型、あるいは正確にはビット深度。チャンネル数は入力と同じであるためである。rtypeが負の場合、出力行列は入力と同じ型になる。 |
| alpha | 省略可能なスケール係数。 |
| beta | スケーリングされた値に加算する省略可能なデルタ。 |
src.convertTo(dst, rtype);
MatVectorの使い方
let mat = new cv.Mat();
// Initialise a MatVector
let matVec = new cv.MatVector();
// Push a Mat back into MatVector
matVec.push_back(mat);
// Get a Mat fom MatVector
let cnt = matVec.get(0);
mat.delete(); matVec.delete(); cnt.delete();
- 覚え書き
- cv.Mat、cv.MatVector、cnt(MatVectorから取得したMat)を使わなくなったら削除するのを忘れないこと。
ピクセル値へのアクセスと変更
まず、次の型の対応関係を知っておくべきである:
| データプロパティ | C++ 型 | JavaScript 型付き配列 | Mat 型 |
| data | uchar | Uint8Array | CV_8U |
| data8S | char | Int8Array | CV_8S |
| data16U | ushort | Uint16Array | CV_16U |
| data16S | short | Int16Array | CV_16S |
| data32S | int | Int32Array | CV_32S |
| data32F | float | Float32Array | CV_32F |
| data64F | double | Float64Array | CV_64F |
1. data
let row = 3, col = 4;
let src = cv.imread("canvasInput");
if (src.isContinuous()) {
let R = src.data[row * src.cols * src.channels() + col * src.channels()];
let G = src.data[row * src.cols * src.channels() + col * src.channels() + 1];
let B = src.data[row * src.cols * src.channels() + col * src.channels() + 2];
let A = src.data[row * src.cols * src.channels() + col * src.channels() + 3];
}
- 覚え書き
- データの操作は連続したMatに対してのみ有効である。まずisContinuous()を使って確認すべきである。
2. at
| Mat 型 | At 操作 |
| CV_8U | ucharAt |
| CV_8S | charAt |
| CV_16U | ushortAt |
| CV_16S | shortAt |
| CV_32S | intAt |
| CV_32F | floatAt |
| CV_64F | doubleAt |
let row = 3, col = 4;
let src = cv.imread("canvasInput");
let R = src.ucharAt(row, col * src.channels());
let G = src.ucharAt(row, col * src.channels() + 1);
let B = src.ucharAt(row, col * src.channels() + 2);
let A = src.ucharAt(row, col * src.channels() + 3);
- 覚え書き
- atによる操作はシングルチャンネルへのアクセス専用であり、値を変更することはできない。
3. ptr
| Mat 型 | Ptr 操作 | JavaScript 型付き配列 |
| CV_8U | ucharPtr | Uint8Array |
| CV_8S | charPtr | Int8Array |
| CV_16U | ushortPtr | Uint16Array |
| CV_16S | shortPtr | Int16Array |
| CV_32S | intPtr | Int32Array |
| CV_32F | floatPtr | Float32Array |
| CV_64F | doublePtr | Float64Array |
let row = 3, col = 4;
let src = cv.imread("canvasInput");
let pixel = src.ucharPtr(row, col);
let R = pixel[0];
let G = pixel[1];
let B = pixel[2];
let A = pixel[3];
mat.ucharPtr(k) はmatのk行目を取得する。mat.ucharPtr(i, j) はmatのi行目j列目を取得する。
画像のROI
時には、画像の特定の領域を扱う必要がある。画像内での目の検出では、まず画像全体に対して顔検出を行い、顔が得られたら、その顔の領域だけを選択して、画像全体を探索する代わりにその中で目を探索する。これにより精度(目は常に顔の上にあるため)と性能(小さな領域を探索するため)が向上する
次の関数を使う: roi (rect)
- 引数
-
| rect | 矩形の関心領域 (Region of Interest)。 |
試してみよう
画像チャンネルの分割と結合
画像のR,G,Bチャンネルを個別に処理する必要が生じることがある。その場合はRGB画像をシングルプレーンに分割する必要がある。あるいは逆に、個々のチャンネルをRGB画像に結合する必要がある場合もある。
let src = cv.imread("canvasInput");
let rgbaPlanes = new cv.MatVector();
// Split the Mat
cv.split(src, rgbaPlanes);
// Get R channel
let R = rgbaPlanes.get(0);
// Merge all channels
cv.merge(rgbaPlanes, src);
src.delete(); rgbaPlanes.delete(); R.delete();
- 覚え書き
- cv.Mat、cv.MatVector、R(MatVectorから取得したMat)は、使用しなくなったら削除することを忘れないこと。
画像に境界を付ける (パディング)
写真フレームのような境界を画像の周囲に作りたい場合は、cv.copyMakeBorder() 関数を使用できる。ただしこの関数は畳み込み演算やゼロパディングなど、より多くの用途を持つ。この関数は以下の引数を取る。
- src - 入力画像
- top, bottom, left, right - 対応する方向のピクセル数で表した境界の幅
- borderType - Flag defining what kind of border to be added. It can be following types:
- cv.BORDER_CONSTANT - Adds a constant colored border. The value should be given as next argument.
- value - 境界の種類が cv.BORDER_CONSTANT の場合の境界の色
試してみよう