🖼️

OpenCV.jsで加工した画像をjimpで出力するとエラーが起きる

2022/08/20に公開

課題

node.js上でOpenCV.jsを動かしている。
OpenCV.jsで加工した画像をjimpで出力する。
その際に下記のエラーが出る。

abort(TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received null). Build with -s ASSERTIONS=1 for more info.
(Use `node --trace-uncaught ...` to show where the exception was thrown)

環境

node.js:18.7OpenCV.js:4.6.0jimp:0.16.1

原因

jimpのインスタンスを作る時に、rgbaの4チャネル以外のチャネル数のbufferを渡していたことが原因です。
jimpのインスタンスは、rgbaの4チャネル以外のbufferを受け取れません。

You can also initialize a new Jimp image with a raw image buffer:

new Jimp({ data: buffer, width: 1280, height: 768 }, (err, image) => {
// this image is 1280 x 768, pixels are loaded from the given buffer.
});

This can be useful for interoperating with other image processing libraries. buffer is expected to be four-channel (rgba) image data.

エラーを起こしていたコード

OpenCV.jsを触っている人なら分かると思いますが、チュートリアルのコードを組み合わせただけの簡単なコードです。

const Jimp = require('jimp');

async function onRuntimeInitialized() {
    let jimpSrc = await Jimp.read('./input.jpg');
    let src = cv.matFromImageData(jimpSrc.bitmap);
    let dst = new cv.Mat();
    let low = new cv.Mat(src.rows, src.cols, src.type(), [0, 0, 0, 0]);
    let high = new cv.Mat(src.rows, src.cols, src.type(), [150, 150, 150, 255]);
    cv.inRange(src, low, high, dst);// ここでグレースケールの1チャネルのデータになる

    new Jimp({
        width: dst.cols,
        height: dst.rows,
        data: Buffer.from(dst.data)// ここがエラーの原因
    }).write('./output.png');
    src.delete();
    dst.delete();
    low.delete();
    high.delete();
}

Module = {
    onRuntimeInitialized
};
cv = require('./opencv.js');

rgba4チャネルのデータからgrayscaleのデータに変わっていることは、console.log(src.data)console.log(dst.data)の中身を比較してみれば分かると思います。

解決策

grayscaleのデータをrgba4チャネルのデータに置き換えてあげれば、解決します。

ちゃんと動くコード

cv.cvtColor(dst, dst, cv.COLOR_GRAY2RGBA, 0);を1行追加するだけですね!

const Jimp = require('jimp');

async function onRuntimeInitialized() {
    let jimpSrc = await Jimp.read('./input.jpg');
    let src = cv.matFromImageData(jimpSrc.bitmap);
    let dst = new cv.Mat();
    let low = new cv.Mat(src.rows, src.cols, src.type(), [0, 0, 0, 0]);
    let high = new cv.Mat(src.rows, src.cols, src.type(), [150, 150, 150, 255]);
    cv.inRange(src, low, high, dst);// ここでグレースケールの1チャネルのデータになる

    cv.cvtColor(dst, dst, cv.COLOR_GRAY2RGBA, 0);// ここでRGBAに変換する

    new Jimp({
        width: dst.cols,
        height: dst.rows,
        data: Buffer.from(dst.data)// いける!
    }).write('./output.png');
    src.delete();
    dst.delete();
    low.delete();
    high.delete();
}

Module = {
    onRuntimeInitialized
};
cv = require('./opencv.js');

最後に

ほぼチュートリアルの通りで躓いたので、他のOpenCV.js初心者に広く役立つかなと思って記事にしました。

jimpを使わないことで解決する方法もあると思います!

Discussion