📷
画像処理100本ノックに挑戦|ヒストグラム操作(022/100)
これはなに?
画像処理100本ノックを、TypeScriptとlibvipsで挑戦してみる記事の22本目です。
前回
実装
お題
ヒストグラムの平均値をm0=128、標準偏差をs0=52になるように操作せよ。
これはヒストグラムのダイナミックレンジを変更するのではなく、ヒストグラムを平坦に変更する操作である。
平均値m、標準偏差s、のヒストグラムを平均値m0, 標準偏差s0に変更するには、次式によって変換する。
Coding
import sharp from 'sharp';
export async function histogramManipulation(
inputPath: string,
outputPath: string,
targetMean: number = 128,
targetStdDev: number = 52
): Promise<void> {
try {
const image = await sharp(inputPath)
.raw()
.toBuffer({ resolveWithObject: true });
const { data, info } = image;
const { width, height, channels } = info;
// 現在の平均値と標準偏差を計算
let sum = 0;
let sumSquared = 0;
const n = data.length;
for (let i = 0; i < n; i++) {
sum += data[i];
sumSquared += data[i] * data[i];
}
const mean = sum / n;
const variance = (sumSquared / n) - (mean * mean);
const stdDev = Math.sqrt(variance);
// 新しい画像データ用のバッファを作成
const normalizedData = Buffer.alloc(data.length);
// ヒストグラム操作を実行
const scale = targetStdDev / stdDev;
for (let i = 0; i < data.length; i++) {
const normalized = scale * (data[i] - mean) + targetMean;
normalizedData[i] = Math.min(255, Math.max(0, Math.round(normalized)));
}
// 結果を保存
await sharp(normalizedData, {
raw: {
width,
height,
channels
}
})
.toFile(outputPath);
// 結果の表示
console.log('ヒストグラム操作の結果:');
console.log('元の統計値:', {
平均値: mean.toFixed(2),
標準偏差: stdDev.toFixed(2)
});
// 操作後の統計値を計算
let newSum = 0;
let newSumSquared = 0;
for (let i = 0; i < normalizedData.length; i++) {
newSum += normalizedData[i];
newSumSquared += normalizedData[i] * normalizedData[i];
}
const newMean = newSum / n;
const newVariance = (newSumSquared / n) - (newMean * newMean);
const newStdDev = Math.sqrt(newVariance);
console.log('操作後の統計値:', {
平均値: newMean.toFixed(2),
標準偏差: newStdDev.toFixed(2)
});
} catch (error) {
console.error('画像処理中にエラーが発生しました:', error);
throw error;
}
}
結果
入力 | 出力 |
---|---|
![]() |
![]() |
Discussion