locator/quality.js

66 lines
1.8 KiB
JavaScript

const { PNG } = require('pngjs');
const fs = require('fs');
// Fixed from https://www.npmjs.com/package/png-quality
async function loadPngFile(pathOrBuffer) {
// Load buffer of path
if (!(pathOrBuffer instanceof Buffer)) {
pathOrBuffer = await new Promise((resolve, reject) => {
fs.readFile(pathOrBuffer, (err, data) => {
if (err) return reject(err)
resolve(data)
});
})
}
// Load PNG from buffer
return await new Promise((resolve, reject) => {
const png = new PNG()
png.parse(pathOrBuffer, err => {
if (err) return reject(err)
resolve(png)
})
})
}
async function mse(png1, png2) {
const pngs = [png1, png2];
for (let i in pngs) {
if (!(pngs[i] instanceof PNG)) pngs[i] = await loadPngFile(pngs[i])
}
if (pngs[0].width !== pngs[1].width || pngs[0].height !== pngs[1].height) {
throw new Error('Width or height does not equal')
}
if (pngs[0].data.length !== pngs[1].data.length) {
throw new Error('Data buffer length does not equal')
}
const square = (a) => a * a,
channelIndex = [0, 1, 2],
channelMax = 255 * 255,
area = pngs[0].width * pngs[1].height
let mse = 0
for (let i = 0; i < pngs[0].data.length; i += 4) {
const rgbas = pngs.map(png => png.data.slice(i, i + 4))
const rgbs = rgbas.map(rgba => channelIndex.map(i => rgba[i] * rgba[3]))
channelIndex.forEach(i => mse += square(rgbs[0][i] - rgbs[1][i]))
}
return mse / 3.0 / (channelMax * channelMax) / area
}
async function psnr(png1, png2) {
const m = await mse(png1, png2)
return 10 * Math.log10(1 / m)
}
module.exports = {
loadPngFile,
mse,
psnr,
};