📷

画像処理100本ノックに挑戦|ヒストグラム平坦化(023/100)

2025/01/25に公開

これはなに?

画像処理100本ノックを、TypeScriptとlibvipsで挑戦してみる記事の23本目です。

前回

https://zenn.dev/nyagato_00/articles/2be700edd634d7

実装

お題

ヒストグラム平坦化を実装せよ。

ヒストグラム平坦化とはヒストグラムを平坦に変更する操作であり、上記の平均値や標準偏差などを必要とせず、ヒストグラム値を均衡にする操作である。

これは次式で定義される。 ただし、S ... 画素値の総数、Zmax ... 画素値の最大値、h(z) ... 濃度zの度数

https://github.com/minido/Gasyori100knock-1/tree/master/Question_21_30#q23-ヒストグラム平坦化

Coding

import sharp from 'sharp';

export async function histogramEqualization(
  inputPath: string, 
  outputPath: string,
  maxValue: number = 255
): Promise<void> {
  try {
    const image = await sharp(inputPath)
      .raw()
      .toBuffer({ resolveWithObject: true });

    const { data, info } = image;
    const { width, height, channels } = info;
    const totalPixels = width * height * channels;

    // 度数分布を計算
    const histogram = new Array(256).fill(0);
    for (let i = 0; i < data.length; i++) {
      histogram[data[i]]++;
    }

    // 累積度数分布を計算
    const cumulativeHist = new Array(256).fill(0);
    let sum = 0;
    for (let i = 0; i < 256; i++) {
      sum += histogram[i];
      cumulativeHist[i] = sum;
    }

    // 平坦化を実行
    const equalizedData = Buffer.alloc(data.length);
    for (let i = 0; i < data.length; i++) {
      const pixelValue = data[i];
      const newValue = Math.round((maxValue / totalPixels) * cumulativeHist[pixelValue]);
      equalizedData[i] = Math.min(255, Math.max(0, newValue));
    }

    // 結果を保存
    await sharp(equalizedData, {
      raw: {
        width,
        height,
        channels
      }
    })
    .toFile(outputPath);

    // 結果の統計を表示
    console.log('平坦化処理が完了しました');

  } catch (error) {
    console.error('画像処理中にエラーが発生しました:', error);
    throw error;
  }
}

結果

入力 出力

Discussion