Использование swscale для создания изображений

У меня есть входное изображение A и результирующее изображение B размером 800x600, сохраненное в формате YUV420, и мне нужно масштабировать изображение A до размера 100x100 и поместить его в результирующее изображение B в некоторой точке (x=100, y=100). Чтобы уменьшить использование памяти и процессора, я поместил результат swscale прямо в окончательное изображение B.

Вот фрагменты кода (довольно просто):

//here we a creating sws context for scaling into 100x100
sws_ctx = sws_getCachedContext(sws_ctx, frame.hdr.width, frame.hdr.height, AV_PIX_FMT_YUV420P,
                               100, 100, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr);

Далее мы создаем соответствующий срез и шаги, описывающие изображение A

    int src_y_plane_sz = frame.hdr.width * frame.hdr.height;
    int src_uv_plane_sz = src_y_plane_sz / 2;
    std::int32_t src_stride[] = {
        frame.hdr.width,
        frame.hdr.width / 2,
        frame.hdr.width / 2,
        0};

    const uint8_t* const src_slice[] = {
        &frame.raw_frame[0],
        &frame.raw_frame[0] + src_y_plane_sz,
        &frame.raw_frame[0] + src_y_plane_sz + src_uv_plane_sz,
        nullptr};

Теперь делаем то же самое для целевого изображения B

    std::int32_t dst_stride[] = {
        current_frame.hdr.width,
        current_frame.hdr.width /2,
        current_frame.hdr.width /2,
        0
    };

    std::int32_t y_plane_sz = current_frame.hdr.width * current_frame.hdr.height;
    std::int32_t uv_plane_sz = y_plane_sz / 2;

    //calculate offset in slices for x=100, y=100 position
    std::int32_t y_offset = current_frame.hdr.width * 100 + 100;

    uint8_t* const dst_slice[] = {
        &current_frame.raw_frame[0] + y_offset,
        &current_frame.raw_frame[0] + y_plane_sz + y_offset / 2,
        &current_frame.raw_frame[0] + y_plane_sz + uv_plane_sz + y_offset / 2,
        nullptr};

В конце концов - звонит swscale

    int ret = sws_scale(sws_ctx, src_slice, src_stride, 0, frame.hdr.height,
                        dst_slice, dst_stride);

После использования тестовой последовательности у меня возник недопустимый результат со следующими проблемами:

  1. Компонент Y получил некоторую линию заполнения
  2. Ультрафиолетовые компоненты оказались не на своем месте - они немного ниже оригинальных Y-компонентов.

Артефакты

У кого-нибудь были такие же проблемы с функцией swscale? Я довольно новичок в этой коллекции библиотек FFmpeg, поэтому я открыт для любых мнений о том, как правильно выполнить эту задачу.

Версия FFmpeg используется 3.3

2 ответа

Решение

Спасибо @VTT за указание на возможную проблему - я исправил расчет указателей срезов назначения следующим образом:

    int dest_x = 200, dest_y = 70;

    //into 100x100 position
    std::int32_t y_offset = current_frame.hdr.width * dest_y + dest_x;
    std::int32_t u_offset = ( current_frame.hdr.width * dest_y )  / 4 + dest_x /2;
    std::int32_t v_offset = u_offset + y_plane_sz / 4;

    uint8_t* const dst_slice[] = {
        &current_frame.raw_frame[0] + y_offset,
        &current_frame.raw_frame[0] + y_plane_sz + u_offset,
        &current_frame.raw_frame[0] + y_plane_sz + v_offset,
        nullptr};

И вторая проблема с "линейным артефактом" решается с использованием коэффициента масштабированных размеров размеров на 8.

Еще одно дополнение для правильных вычислений положения для указателей срезов назначения - эти координаты y должны быть перенастроены в соответствии с текущей плоскостью Y, указывающей, потому что на каждые два шага Y имеется только один шаг U или V. Например (см. Переменную Adjust_uv_y):

std::int32_t adjusted_uv_y = dest_y % 2 == 0 ? dest_y : dest_y - 1;
std::int32_t y_offset = current_frame.hdr.width * dest_y + dest_x;
std::int32_t u_offset = ( current_frame.hdr.width * adjusted_uv_y )  / 4 + dest_x /2;
std::int32_t v_offset = u_offset + y_plane_sz / 4;

YUV420 Формат масштабирует ширину и высоту изображения в два раза. То есть каждая хроматическая плоскость в 4 раза меньше плоскости яркости:

int src_uv_plane_sz = src_y_plane_sz / 4;

Также я не уверен, что рассчитанные значения шага верны. Обычно шаг!= Ширина.

Другие вопросы по тегам