📷

画像処理100本ノックに挑戦|ガンマ補正(024/100)

2025/01/26に公開

これはなに?

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

前回

https://zenn.dev/nyagato_00/articles/60493cb7873f11

実装

お題

imori_gamma.jpgに対してガンマ補正(c=1, g=2.2)を実行せよ。

ガンマ補正とは、カメラなどの媒体の経由によって画素値が非線形的に変換された場合の補正である。 ディスプレイなどで画像をそのまま表示すると画面が暗くなってしまうため、RGBの値を予め大きくすることで、ディスプレイの特性を排除した画像表示を行うことがガンマ補正の目的である。

非線形変換は次式で起こるとされる。 ただしxは[0,1]に正規化されている。 c ... 定数、g ... ガンマ特性(通常は2.2)

https://github.com/minido/Gasyori100knock-1/tree/master/Question_21_30#q24-ガンマ補正

Coding

import sharp from 'sharp';

export async function gammaCorrection(
  inputPath: string, 
  outputPath: string,
  c: number = 1,
  gamma: number = 2.2
): Promise<void> {
  try {
    const image = await sharp(inputPath)
      .raw()
      .toBuffer({ resolveWithObject: true });

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

    const correctedData = Buffer.alloc(data.length);

    for (let i = 0; i < data.length; i++) {
      // [0,1]に正規化
      const normalized = data[i] / 255.0;
      // ガンマ補正を適用
      const corrected = Math.pow(normalized / c, 1 / gamma);
      // 255をかけて元のレンジに戻す
      correctedData[i] = Math.min(255, Math.max(0, Math.round(corrected * 255)));
    }

    await sharp(correctedData, {
      raw: {
        width,
        height,
        channels
      }
    })
    .toFile(outputPath);

    console.log('ガンマ補正が完了しました');
  } catch (error) {
    console.error('画像処理中にエラーが発生しました:', error);
    throw error;
  }
}

結果

入力 出力

Discussion