Выходной формат Delta PNG

Получить ключ API

Мигрируете из другого провайдера? Check out our migration guide

Выходной формат Delta PNG может дать значительную экономию в плане задержки и пропускной способности и особенно полезен в ситуациях, когда задержка и пропускная способность имеют особое значение, например в мобильных приложениях.

Для этого требуется, чтобы пиксели исходного изображения были загружены на клиентский сайт, а затем к исходному изображению был применен Delta PNG для получения итогового изображения.

Например:

Оригинал

778 × 639 px

Обычный PNG

409 048 байтов

Delta PNG

110 904 байтов
Экономия 73%.

Даже в этом примере, где внимание сосредоточено на волосах (что является наихудшим сценарием для формата Delta PNG), экономия значительна: 73%

Расшифровка Delta PNG

Delta PNG — это обычный PNG-файл, который можно прочитать в любой программной библиотеке, поддерживающей чтение PNG. Единственная разница по сравнению с обычным результатом PNG заключается в самих значениях пикселей. Фон кодируется как прозрачный черный 0x00000000, а передний план — как прозрачный белый 0x00FFFFFF. Частично прозрачные пиксели имеют свои фактические значения цвета.

Тип пикселя Оригинал Обычный PNG Delta PNG Источник вывода
Передний план 0xFFrrggbb 0xFFrrggbb 0x00FFFFFF Оригинал
Фон 0xFFrrggbb 0x00000000 0x00000000 Delta PNG
Край 0xFFrrggbb 0x80rrggbb 0x80rrggbb Delta PNG

Это означает, что при расшифровке значений пикселей Delta PNG вам необходимо извлечь фактическое значение пикселя из оригинала, если вы встречаете прозрачный белый 0x00FFFFFF. Остальные пиксели имеют те же значения, что и в обычном формате PNG.

Вот пример кода TypeScript для расшифровки формата Delta PNG:

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