Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save heguro/4318c00739f28e34d2a32492441acc5c to your computer and use it in GitHub Desktop.

Select an option

Save heguro/4318c00739f28e34d2a32492441acc5c to your computer and use it in GitHub Desktop.
ブラウザごとのJPEG読み込み挙動の違い (20260704 macOS 26.5.1, fable5記)

ブラウザごとのJPEG読み込み挙動の違い(今回の実測 + 既知情報の整理)

実測結果(stage1デコードプローブ: createImageBitmap → canvas → getImageData、OpenCV不使用)

IMG_4620の各バリアント(同一写真、ICCプロファイル3種)を3エンジンで測定した結果です。

入力のICCプロファイル Edge/Chromium WebKit (Safari系) Firefox
Display P3 (iOS Safariアップロード / HEIC) sRGBへ正しく変換(PILのICC→sRGB変換とほぼ一致、差 mean 0.021/max 1) 同じくPIL-sRGB風だがEdgeとはバイト非同一(±2 LSB) ICC完全無視=cv2.imreadとバイト一致
Apple Wide Color Sharing Profile (macOS写真.app「互換性優先」書き出し) ほぼ生値(変換がほぼかからない: 対cv2 mean 0.21-0.23/max 1。PIL-sRGBとは mean 2.1-2.4/max 9-10も離れる) ほぼ生値(±1-2) 生値=cv2一致
sRGB (写真.app sRGB書き出し) 生値=cv2とバイト一致 それでも±1-2 LSBズレる 生値=cv2とバイト一致
HEIC デコード不可 デコード可(macOSのみ) デコード不可

各エンジンの挙動の解釈

Firefox: この経路(createImageBitmap→canvas)ではICCプロファイルを一切適用せず、JPEGの生の画素値をそのまま返します。全JPEGで cv2.imread とバイト一致でした。背景として、Firefoxのカラーマネジメント(qcms)はICC v4プロファイルを実質v2として読む既知の制限があり、カラマネの適用範囲も歴史的に限定的です。「Firefoxの結果はmsedgeに近かった」という観察は、msedgeもApple Wide/sRGB画像では生値に近いため整合します。

Edge/Chromium: プロファイル種別で挙動が分かれるのが最大の特徴です。Display P3は正しくsRGBへカラマネされる(=生値から最大8 LSV程度動く)のに、Apple Wide Color Sharing Profileはほぼ生値のまま=プロファイルが実質適用されない。つまりChromium内でも「どの変換ルートに乗るかがプロファイル依存」で、バリアントごとに違う画素が出ます。デコーダはlibjpeg-turbo系で、生値モードではcv2(同じくlibjpeg-turbo系)とバイト一致します。

WebKit: Apple ImageIOフレームワークでデコードするため、ICCが絡まないsRGB画像ですら他エンジンと±1-2 LSBずれます(IDCTやクロマアップサンプリングの実装差。JPEGの4:2:0/4:2:2は輝度より低解像度の色差をどう補間するかがデコーダ任意で、box filter vs triangle filterなどで差が出る)。P3はカラマネされるがEdgeとは非同一。HEICだけはmacOSのシステムデコーダでネイティブに読めます。

正規化オプションの効き方(実測)

  • createImageBitmap(blob, {colorSpaceConversion: "none"}): EdgeとFirefoxは全JPEGプロファイル種でcv2.imreadとバイト一致になる(=Pythonパリティが取れる)。WebKitはこのオプションを部分的に無視し、独自デコードのまま。
  • canvas側の getContext("2d", {colorSpace: "srgb"}) 明示: 正規化効果なし(Edgeではdefaultと同一、WebKitでも一般的な正規化点にならない)。

今回のケースへの含意

  • 差の絶対量は「平均数LSB、最大10 LSB弱」程度で、画像としては目視で分からないレベルです。それが結果の大差になったのは、この写真群で1段目featureマッチが8〜10 inlierのぎりぎり合格となり、ホモグラフィが入力の微小差に過敏だったため(judge ROIで最大59pxのズレに増幅)。
  • なおEXIF orientationや画像寸法の扱いはエンジン間で差がありませんでした(全JPEGでorientation=1、4032x3024維持。WebKitの大画像サブサンプリングも今回の12MPでは非発生)。
  • 補足: 実Safariには「高度なフィンガープリント保護」(プライベートブラウズ等で有効)によるcanvasノイズ注入があり、これが有効だと同一画像でもgetImageDataが毎回変わります。playwright経由のWebKitでは無関係ですが、実機Safariでの不安定報告が出たらまず疑うポイントです。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment