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での不安定報告が出たらまず疑うポイントです。