前回の記事では、reg-cliを使った画像差分比較について紹介しました。
E2Eテストの始め方 番外編 - reg-cliで差分比較 -
そして今回は、TestCafeで撮影した複数枚のスクリーンショット画像を連結するためのsharpについて書いていきたいと思います。
sharp とは
某電機メーカーが強すぎてググラビリティが残念なライブラリ。。。
同じようなライブラリとしてImageMagickやGraphicsMagickも有名ですが、ドキュメントによるとそれらより4倍~5倍速いそうです。
読み込みはJPEG,PNG,WebP,TIFF,GIF,SVGをサポート。
様々なサイズのJPEG,PNG,WebPに変換可能で、リサイズ以外に回転、抽出、合成、ガンマ補正、圧縮などの操作も可能となっています。
今回sharpを選んだ理由としてImageMagickらはしばらくメンテナンスされていない(sharpは執筆時点で10h前と活発に動いてる)ことと、処理の速さでsharpを採用しました。
install
$ npm install sharp
usage
const sharp = require('sharp');
【ソース全文】
const fs = require('fs')
const config = require('../config')
const { imgPath, imgPc, imgSp } = config;
//出力先フォルダの作成
const makeDir = () => {
fs.mkdir('screenshots/convert', { recursive: true }, (err) => {
if (err) throw err;
});
}
//sharp
const convert = async (imagePaths, imageName) => {
const imageAttrs = [];
// 連結する画像の情報取得
const promises = [];
const imagePromise = path =>
new Promise(async resolve => {
const image = await sharp(path);
let width = 0,
height = 0;
await image
.metadata()
.then(meta => ([width, height] = [meta.width, meta.height]));
const buf = await image.toBuffer();
resolve({ width, height, buf });
});
imagePaths.forEach(path => promises.push(imagePromise(path)));
await Promise.all(promises).then(values => {
values.forEach(value => imageAttrs.push(value));
});
// outputする画像の設定
const outputImgWidth = imageAttrs.reduce((acc, cur) => acc + cur.width, 0);
const outputImgHeight = Math.max(...imageAttrs.map(v => v.height));
let totalLeft = 0;
const compositeParams = imageAttrs.map(image => {
const left = totalLeft;
totalLeft += image.width;
return {
input: image.buf,
//合成場所
gravity: "northwest",
left: left,
top: 0
};
});
// 連結処理
sharp({
create: {
width: outputImgWidth,
height: outputImgHeight,
channels: 4,
background: { r: 255, g: 255, b: 255, alpha: 0 }
}
})
//合成
.composite(compositeParams)
//圧縮
.png({
quality: 80,
compressionLevel: 9
})
.toFile(`screenshots/convert/${imageName}.png`,(err, info)=>{
if(err){ throw err }
console.log(info)
});
}
(async () => {
await
makeDir()
convert(imgPc, 'pc')
convert(imgSp, 'sp')
})()
解説
まず、出力先のフォルダを作成します。
フォルダがないと「どこに保存するのか分からないよ!」と怒られてしまうので、sharpを実行する前にフォルダを用意してあげます。
//出力先フォルダの作成
const makeDir = () => {
fs.mkdir('screenshots/convert/corporate', { recursive: true }, (err) => {
if (err) throw err;
});
}
画像の情報取得や連結処理などのソースはこちらを参考にさせていただきました。
NodeJSの画像処理ライブラリ「sharp」を使って画像を連結する
私は、連結したい画像がたくさんあるためconfig.js
に画像パスの配列を記述し呼び出して使っています。
第一引数imagePaths
に呼び出した画像パスの配列imgPc
とimgSp
を指定し、
第二引数のimageNameには出力される画像のファイル名を指定します。
(デフォルトのピクセル制限(268402689)を超えてしまうので複数枚に分けて出力しています)
const config = require('../config')
const { imgPath, imgPc, imgSp } = config;
const convert = async (imagePaths, imageName) => {
.toFile(`screenshots/convert/corporate/${imageName}.png`,(err, info)=>{
});
}
(async () => {
await
convert(imgPc, 'pc')
convert(imgSp, 'sp')
})()
const imgDir = `screenshots/${year}${month}${date}/`
exports.imgPc = [
`${imgDir}/top_pc.png`,
`${imgDir}/company_pc.png`,
`${imgDir}/company_access_pc.png`,
];
exports.imgSp = [
`${imgDir}/top.png`,
`${imgDir}/company.png`,
`${imgDir}/company_access.png`,
];
圧縮
生成された画像の圧縮も設定可能です。
jpeg
.jpeg({
quality: 80
})
options.quality
:整数1〜100,デフォルト80
options.compressionLevel
:zlib圧縮レベル、0〜9(デフォルト9)
png
.png({
quality: 100,
compressionLevel: 9
})
options.quality
:デフォルト100
options.compressionLevel
:zlib圧縮レベル、0〜9(デフォルト9
)
webp
.webp({
quality: 80,
lossless: true
})
options.quality
:整数1〜100,デフォルト80
options.lossless
:可逆圧縮モード,デフォルトfalse
このほか、gifやtiff,heifも対応可能でオプションで細かい設定もできるので詳細は公式をご覧ください。
https://sharp.pixelplumbing.com/api-output
実行
$ node e2e/convert.js
完成!
横並びにがっちゃんこして出力してくれました!
各ページフルサイズのスクリーンショットを連結させると出力サイズがかなり大きくなるので圧縮率を大きくしたり、連結枚数を少なくして何枚かに分けるなど工夫が必要ですが良い感じにできたのではないかと思います
参考
NodeJSの画像処理ライブラリ「sharp」を使って画像を連結する