他のプロバイダーからの移行ですか? Check out our migration guide
差分 PNG 出力形式は、遅延や帯域を大幅に削減することができ、特にモバイルアプリなどの遅延や帯域に厳しい条件下で有効です。
画像の処理結果を生成するには、まず元の画像のピクセルをクライアントに読み込んで、その後元の画像に差分 PNG を適用することが必要です。
例:
この例のように髪の毛が多く含まれる場合(差分 PNG 形式にとっては最悪の例)であっても、大幅な削減 73% を実現できます
差分 PNG は通常の PNG ファイルであり、PNG を読むことができるどんなソフトウェアでも読むことができます。 通常の PNG 結果との唯一の違いは、ピクセル値自体にあります。 背景は透明の黒 0x00000000
で符号化され、前景は透明の白 0x00FFFFFF
で符号化されます。 部分的に透明なピクセルは、実際の色の値となります。
ピクセルタイプ | オリジナル | 通常の PNG | 差分 PNG | 出力ソース |
---|---|---|---|---|
前景 |
0xFFrrggbb
|
0xFFrrggbb
|
0x00FFFFFF
|
オリジナル |
背景 |
0xFFrrggbb
|
0x00000000
|
0x00000000
|
差分 PNG |
エッジ |
0xFFrrggbb
|
0x80rrggbb
|
0x80rrggbb
|
差分 PNG |
これは差分 PNG ピクセル値を複合するときに、透明の白 0x00FFFFFF
を検出したとき、元の PNG から実際のピクセル値を引き出す必要があることを意味しています。 他のピクセルは、通常の PNG 形式と同じ値となっています。
以下は、差分 PNG 形式を復号化する場合の TypeScript コードの例です。
export function decodeDeltaPngInPlace(originalPixels: Uint8Array, deltaPngPixels: Uint8Array): Uint8Array { const N = originalPixels.length / 4; // Array of RGBA values, div 4 to get number of pixels for (let i = 0; i < N; i++) { const i4 = i * 4; const alpha = deltaPngPixels[i4 + 3]; // JavaScript is RGBA, +3 to get alpha if (alpha == 0) { const r = deltaPngPixels[i4]; // JavaScript is RGBA, +0 to get red if (r == 0xFF) { // Transparent white => foreground => take values from original deltaPngPixels[i4] = originalPixels[i4]; deltaPngPixels[i4 + 1] = originalPixels[i4 + 1]; deltaPngPixels[i4 + 2] = originalPixels[i4 + 2]; deltaPngPixels[i4 + 3] = originalPixels[i4 + 3]; } // else transparent black => background => keep values } // else partially transparent => keep values } return deltaPngPixels; }
JavaScript で画像やピクセルを操作する場合のさらなる詳細については、Mozilla 開発者ネットワークでキャンバスとピクセル操作チュートリアル という優れた解説を参照してください。
画像読み込みライブラリは、完全に透明なピクセルであってもピクセル値を保存できなければなりません。これはライブラリの通常動作です。
ただし、Python やよく知られている OpenCV ライブラリなどを使用する場合、cv2.IMREAD_UNCHANGED
フラグを使用して次のように画像を読み込まなければなりません。cv2.imread(path, cv2.IMREAD_UNCHANGED)
。 そうしないと、OpenCV が完全に透明なピクセルの実際のピクセル値を上書きしてしまいます。
残念なことに、このフラグを使用する場合、OpenCV は画像に保存されている回転情報を適用しません。 したがって、このような場合でも画像の正しい方向を適用できるよう、X-Input-Orientation
ヘッダーが返されます。
以下は、方向を適用するときの Python+OpenCV コードの例です。
def apply_exif_rotation(im: np.ndarray, orientation: int) -> np.ndarray: # https://note.nkmk.me/en/python-opencv-numpy-rotate-flip/ if 1 < orientation <= 8: if 2 == orientation: # TOP-RIGHT, flip left-right, [1, 1] -> [-1, 1] im = cv2.flip(im, 1) elif 3 == orientation: # BOTTOM-RIGHT, rotate 180 im = cv2.rotate(im, cv2.ROTATE_180) elif 4 == orientation: # BOTTOM-LEFT, flip up-down, [1, 1] -> [1, -1] im = cv2.flip(im, 0) elif 5 == orientation: # LEFT-TOP, Rotate 90 and flip left-right im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE) im = cv2.flip(im, 1) elif 6 == orientation: # RIGHT-TOP, Rotate 90 im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE) elif 7 == orientation: # RIGHT-BOTTOM, im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE) im = cv2.flip(im, 0) else: # 8 == orientation: # LEFT-BOTTOM, Rotate 270 im = cv2.rotate(im, cv2.ROTATE_90_COUNTERCLOCKWISE) return im