世界を動かす技術を、日本語で。

RGB値は255で正規化すべきか、それとも256で正規化すべきか?

2026年6月2日原文(30fps.net)

概要

  • 画像処理における 整数と浮動小数点変換 の2つの手法を比較
  • 255除算(標準)256除算(代替) の違いと利点・欠点を解説
  • 極値の扱い・量子化誤差・実用上の影響について説明
  • どちらの手法を選ぶべきかの 結論と推奨 を提示
  • 量子化理論 や他の参考意見も簡潔に紹介

画像処理プログラムにおける整数-浮動小数点変換の2方式

  • 画像を 浮動小数点(float) に変換し、処理後に 8ビット整数 へ戻す処理が一般的

  • 主な変換方式は以下の2つ:

    • 標準方式(255除算)
      • 変換式:pixels = img / 255.0
      • 保存時:output = np.trunc(result * 255 + 0.5)
      • 特徴:0→0.0、255→1.0に正確にマッピング
    • 代替方式(256除算)
      • 変換式:pixels = (img + 0.5) / 256.0
      • 保存時:output = np.trunc(result * 256)
      • 特徴:0→0.00195…、255→0.998…にマッピングされ、両端値がピッタリ0や1にならない
  • どちらも最終的に clamp(0~255に丸め) して8ビット化

標準方式(255除算)の特徴と批判

  • 0と255が0.0と1.0 に正確に対応し、GPUや多くの標準実装で採用
  • 変換後の値は[0,1]範囲を 少しはみ出す
    • 例:0→0.0、255→1.0だが、実際のbin幅は両端が半分
  • 極値(0,255)が出現しづらい
    • 一様乱数でヒストグラムを取ると、0と255の出現頻度が他より半分
  • しかし、 元画像の再量子化(uint8→float→uint8) は損失なし
  • 極端値のbin幅が狭い問題は、 実用上あまり影響しない ケースが多い

代替方式(256除算)の特徴と利点・欠点

  • 各float値が整数のちょうど中間 に配置される
  • 例:128/256=0.5で、値の間隔が均等
  • dithering(ディザリング) やノイズ加算時に端値の特別扱いが不要
  • ただし、 0や255がfloatの0.0や1.0に一致しない ため、黒判定などが煩雑
  • 元画像が標準方式で保存されている場合、 精度を取り戻せない

量子化方式としての2手法の整理

  • mid-riser(標準:255除算)
    • 量子化式:k = trunc(x L)、復元:y_k = (k+0.5)/L
    • L=255の場合
  • mid-tread(代替:256除算)
    • 量子化式:k = trunc(x L + 0.5)、復元:y_k = k/L
    • L=256の場合
  • mid-riser は0→0.0、 mid-tread は0→0.00195…と、 0の扱い が異なる

量子化誤差と実用上の意味

  • 255除算は理論上、量子化誤差がわずかに大きい
    • 平均絶対誤差:1/1020(255除算)、1/1024(256除算)
  • しかし、 実際の画像がどう保存されたか に依存し、理論値ほどの差は出ない
  • 他人が作成した画像 を処理する場合、標準方式(255除算)が安全
  • 自分で保存・読込を完結 させる場合のみ、256除算のメリットを活かせる可能性

結論と推奨

  • 他人から提供された画像や標準的なワークフローでは255除算を推奨
  • 浮動小数点値の正確さや量子化誤差を極限まで気にする場合のみ256除算を検討
  • ただし、 保存・読込の両方を自分で管理できる場合 に限定
  • 方式を混在させると誤差やバグの原因 になるため、統一が重要

参考意見・関連資料

  • Jonathan Blowの2002年記事:mid-riser/mid-tread量子化の図解
  • Andrew Keslerの2015年記事:256除算のディザリング利点を主張
  • Wikipedia「Quantization(量子化)」:mid-riser/mid-treadの定義と数式
  • StackOverflowの議論:量子化誤差の理論値比較

まとめ

  • 画像処理で整数⇔float変換時は255除算が基本
  • 理論的には256除算も一考の価値あり
  • 現場の標準や互換性を最優先 に選択するのが無難

Hackerたちの意見

定規があって、12インチまであるなら、13の目盛りの数ではなく、長さLで正規化すべきだよ。

そうだけど、>> 8はめっちゃ速いよね。

自分はバカだ。0って最初から始まるんじゃないの?

そのアナロジーにはちょっと混乱してる。 “定規”って、0から255までの256ポイントがある255インチの定規なの?それとも256インチの定規で256個の1インチのセグメントがあるってこと?L = 256×1になるの?

でも、数字がポイントを表しているのではなく、ポイント間の間隔を表しているって誰が言ったの?

正しい方法はスライドルールを使うことだよ。

+0.5の解決策を支持するよ。まず、端っこの半分のサイズの間隔が好きじゃないし、次に、255ベースの表現は通常SDR(HDRじゃない)画像だからね。RGBの値は、何らかの適応状態に対する輝度を表していて、日中のシーンでの「ゼロ」は「ゼロ輝度」じゃないんだ。最も明るいポイントの約0.001倍の明るさで、何百万もの光子があるから、ゼロとは程遠いよ。ある意味、私たちの目は滑らかなスケールでコントラストを感じていて、システムには絶対的なゼロは存在しないんだ。例えば、放送システムは歴史的にSDRの輝度範囲として16-235を使っていたよ。「ゼロが必要だ」と言う議論には偏りがあると思うけど、ほとんどのことにゼロは必要ないと思う。

両方の解決策は0.5を足すけど、違いはそのプロセスのどこで起こるかだね。

同意するよ。それに、0.0と1.0はディザ信号には実際には存在しないから、バイトは256で割る前に[0.5, 255.5]にマッピングされるべきだと思う。これで符号付き整数の非対称性も解決するし、符号付きバイトは128で割る前に[-127.5, 127.5]にマッピングされるんだ。音声DSPの人たちがもうやってるか気になるな。

ある意味、私たちの目は滑らかなスケールでコントラストを感じている。光の量をチェックして瞳孔を調整するための視覚センターがあるんだ。それは意図的に反応するようになってる。 > システムには絶対的なゼロは存在しない。もしかしたらあるかもね。「盲目」って呼ぶんじゃないかな。 > 放送システムは歴史的にSDRの輝度範囲として16-235を使っていた。主に完全にアナログシステムだったから、これらはすべて信号電圧に変換されるんだ。冗談でNTSCは「同じ色が二度と出ない」って呼ばれてたよ。すでに妥協されたシステムにさらに妥協を重ねたからね。

面白いアイデアだけど、なんか世界が揺れてる気がする。処理プログラムでは、昔は黒(0.0)と白(1.0)だったのが、今はとても暗いグレーととても明るいグレーになっちゃった。

画像処理やVFXのレンダリングにかなりの経験がある者として言わせてもらうと、色空間変換(古いSDR用のsRGB「リニア」rec709や、新しいフォーマット用のより広いガマットなど)はこの後に行われるから、ダイナミックレンジの「圧縮」は読み込み後に起こることを忘れてるかも。画像処理や合成の多くのワークフローでは、0がゼロを意味するという前提がある(正しくないことが多いけど)。だから、8ビットの場合、0uが0.0fに、255が1.0fにマッピングされるっていう仮定がよくある。0の値がちょっとだけ0.0を超えると、どこかのコードが0.0のハードしきい値を使って他の操作をマスクしてるからアーティファクトが出ちゃうんだよね。逆に、1.0のアルファの場合も、255の値がもう1.0fじゃなくなると、非常にわずかに透けて見えるオブジェクトができちゃう(これは特定の状況やピクセルをじっくり見る時にしか見えないことが多い)。(254が+0.5で1.0fになる時も同じことが起こるよ。)

Hacker Newsで議論の続きを見る