差分 PNG 出力形式

API キーを取得

他のプロバイダーからの移行ですか? Check out our migration guide

差分 PNG 出力形式は、遅延や帯域を大幅に削減することができ、特にモバイルアプリなどの遅延や帯域に厳しい条件下で有効です。

画像の処理結果を生成するには、まず元の画像のピクセルをクライアントに読み込んで、その後元の画像に差分 PNG を適用することが必要です。

例:

オリジナル

778 × 639 ピクセル

通常の PNG

409,048 バイト

差分 PNG

110,904 バイト
73% の削減。

この例のように髪の毛が多く含まれる場合(差分 PNG 形式にとっては最悪の例)であっても、大幅な削減 73% を実現できます

差分 PNG の復号化

差分 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
API キーを取得