OpenCV.jsで加工した画像をjimpで出力するとエラーが起きる
課題
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.7
、OpenCV.js:4.6.0
、jimp: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