OpenCV.jsでImageDataの画像処理

画像処理をするWebアプリケーションを開発する際に、OpenCV.jsを使ってImageDataを操作する方法を紹介します

images/cards/lena.webp

目次

1. ImageData オブジェクトの取得または作成

まずは、Canvas 上から ImageData オブジェクトを取得または作成します。

const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const context = canvas.getContext("2d");
// 座標(10, 10)の位置に、widthが200、heightが100のイメージを描画
context.drawImage(image, 10, 10, 200, 100);
// 座標(10, 10)の位置から、widthが200、heightが100のイメージを取得
const imageData1 = context.getImageData(0, 0, 200, 100);
// widthが200、heightが100のイメージを作成
const imageData2 = context.createImageData(200, 100);

2. ImageData から cv.Mat に変換

ImageData.data には、Uint8ClampedArray 型のバイナリ配列が格納されています。 その配列は、RGBA カラー(32bit)になっていますので、OpenCV 用に RGB カラーに変換します。

const src = cv.matFromImageData(imageData.data);
cv.cvtColor(src, src, cv.COLOR_RGBA2RGB);

3. OpenCV.js で画像処理

cv.Mat になってしまえば、あとは自由に OpenCV の画像処理が出来ます。 cv.Mat のプロパティには、以下があります。

  • rows: 画像の高さ
  • cols: 画像の幅
  • step: 画像一行あたりのバイト数
  • data: 画像データのポインタ
  • channels(): チャンネル数
  • type(): 画像の種類
  • depth(): 1ピクセルあたりのビット数

OpenCV.js での注意事項は、自分で new した cv.Mat は必ず delete すること!

// 線を引く
let start = new cv.Point(0, 0);
let end = new cv.Point(200, 200);
cv.line(src, start, end, new cv.Scalar(0, 0, 255), 2, cv.LINE_AA, 0);

// 四角を描く
let p1 = new cv.Point(10, 10);
let p2 = new cv.Point(200, 200);
cv.rectangle(src, p1, p2, cv.Scalar(0, 0, 255), cv.FILLED);

// 丸を描く
let p1 = new cv.Point(100, 100);
cv.circle(dst, p1, 80, cv.Scalar(0, 0, 255), cv.FILLED);

4. cv.Mat から ImageData に変換

ImageData に戻すために、OpenCV で扱ったデータの型から RGBA カラー(32bit)に変換します。

function imageDataFromMat(mat) {
  // converts the mat type to cv.CV_8U
  const img = new cv.Mat();
  const depth = mat.type() % 8;
  const scale =
    depth <= cv.CV_8S ? 1.0 : depth <= cv.CV_32S ? 1.0 / 256.0 : 255.0;
  const shift = depth === cv.CV_8S || depth === cv.CV_16S ? 128.0 : 0.0;
  mat.convertTo(img, cv.CV_8U, scale, shift);
  mat.delete();

  // converts the img type to cv.CV_8UC4
  switch (img.type()) {
    case cv.CV_8UC1:
      cv.cvtColor(img, img, cv.COLOR_GRAY2RGBA);
      break;
    case cv.CV_8UC3:
      cv.cvtColor(img, img, cv.COLOR_RGB2RGBA);
      break;
    case cv.CV_8UC4:
      break;
    default:
      throw new Error(
        "Bad number of channels (Source image must have 1, 3 or 4 channels)"
      );
  }
  const clampedArray = new ImageData(
    new Uint8ClampedArray(img.data),
    img.cols,
    img.rows
  );
  img.delete();
  return clampedArray;
}

5. 画像処理した結果を Canvas に描画

const img_converted = imageDataFromMat(mat);
//座標(50, 50)の位置に、取得したImageDataオブジェクトを描画
context.putImageData(img_converted, 50, 50);

参考

関連記事