Есть ли хорошая библиотека или алгоритм, который помогает быстро преобразовать плоское изображение в чересстрочное (C ++)? - PullRequest
1 голос
/ 01 сентября 2011

Я занимаюсь разработкой приложения для воспроизведения видео, которое будет отображать видео файлы, содержащие необработанные данные плоского изображения, кадр за кадром. Данные, которые он содержит, являются 8-битными RGB (в настоящий момент нет альфа-канала). Аппаратное обеспечение здесь принимает только данные чередующегося изображения. В результате мне нужно преобразовать данные плоского изображения в данные чередующегося изображения. Что я сделал, так это записал или записал на плоские данные. Однако при обработке HD-контента это займет много времени, поскольку данные являются необработанными данными. Я попытался реализовать с двумя потоками, один для обработки другого для отображения. Оказывается, что отображение обработанных чередующихся данных действительно быстро, но поток обработки не в состоянии угнаться за ним. Следовательно, частота кадров сильно зависит от скорости обработки.

У меня есть идея предварительно обработать все и сохранить его в памяти (количество кадров этих видеоклипов относительно невелико). При необходимости я просто отображаю обработанные данные в памяти. На самом деле я протестировал этот подход, он довольно быстрый (60 кадров в секунду). Однако, это кажется неоптимальным, потому что у меня будет либо очень медленный первый запуск, либо мне придется подождать некоторое время, прежде чем начнется воспроизведение. Более того, когда размер файла становится большим, это невозможно сделать из-за ограничений памяти.

Так что я ищу любую библиотеку обработки изображений или алгоритм, который быстро выполняет планарное чередование. Я попробовал gil от boost, но производительность не совсем хорошая.

Ответы [ 5 ]

2 голосов
/ 02 сентября 2011

(Добавление кода рядом с моими комментариями выше)
Это, скомпилированное с g ++ 4.2.1 с -O2 на 2,4 ГГц Intel Core 2 Duo, работает на 2000 кадров за 10 секунд.

int const kWidth = 1920;
int const kHeight = 1080;
for (std::size_t i = 0; i != kWidth*kHeight; ++i) {
    interleavedp[i*3+0] = planarp[i+0*kWidth*kHeight];
    interleavedp[i*3+1] = planarp[i+1*kWidth*kHeight];
    interleavedp[i*3+2] = planarp[i+2*kWidth*kHeight];
}

Обратите внимание, что написание этого текста позволяет компилятору лучше оптимизировать.Разбиение его на строки (или 12-байтовые блоки) только замедляет процесс.

1 голос
/ 10 апреля 2019

Мне пришлось решить ту же проблему, но у меня есть добавленное ограничение, которое мне нужно было выполнить преобразование «на месте» (т. Е. Мне пришлось оставить данные изображения в том же буфере).На изображении ниже я демонстрирую, как необходимо переместить пиксели с плоского на чередованное представление:

enter image description here

Итак, мы видим, что можем изменить изображение "на месте "с последовательностью перестановок.Вот моя реализация C ++, которая работает за линейное время.Параметр шаблона T - это тип канала изображения (например, uint8_t для каналов байтового размера).

#include <vector>
#include <cstdint>
#include <algorithm>

template <typename T>
void planarToInterleaved(int numPixels, int numChannels, T pixels[]) {
    const int size = numPixels * numChannels;
    std::vector<bool> visited(size);
    std::fill(visited.begin(), visited.end(), false);

    auto nextUnvisited = [&](int index) -> int {
        int i;
        for (i = index; i < size && visited[i]; i++)
            ;
        return i;
    };

    auto interleavedIndex = [=](int planarIndex) -> int {
        const int i = planarIndex % numPixels;
        const int k = planarIndex / numPixels;
        return numChannels*i + k;
    };

    int J = 0;
    int Jnext = 0;
    while ( (J = nextUnvisited(Jnext++)) < size ) {
        visited[J] = true;
        const int Jstart = J;
        T tmp = pixels[J];
        while ( true ) {
            const int I = interleavedIndex(J);
            if ( I == J ) break; // 1-node cycle
            std::swap(pixels[I],tmp);
            if ( I == Jstart ) break;
            J = I;
            visited[J] = true;
        }
    }
}

Здесь я преобразую изображение RxH RGB, хранящееся в буфере image (которое содержит W* H * 3 значения) от плоского до чередующегося:

planarToInterleaved(W*H, 3, image);

В любом случае, это было забавно выяснить.

1 голос
/ 28 октября 2015

Существует Библиотека Simd .У него много алгоритмов преобразования изображений.Он поддерживает преобразование между следующими форматами изображений: NV12, YUV420P, YUV422P, YUV444P, BGR-24, BGRA-32, HSL-24, HSV-24, Gray-8, Bayer и некоторыми другими.Алгоритмы оптимизированы с использованием различных расширений ЦП SIMD.В частности, библиотека поддерживает следующие расширения ЦП: SSE, SSE2, SSSE3, SSE4.1, SSE4.2, AVX и AVX2 для x86 / x64, VMX (Altivec) и VSX (Power7) для PowerPC.

1 голос
/ 01 сентября 2011

Должно быть довольно просто написать эту функцию с использованием векторных встроенных функций.Я не знаю, какой процессор, компилятор или формат упакованного пикселя вы используете, поэтому приведу пример реализации с использованием встроенных функций GCC и MMX для x86.Также должно быть легко перевести этот код в код ARM NEON, PowerPC Altivec или x86 / x64 SSE.

Это должно преобразовать планарный RGB в 32-битную упакованную RGBA, хотя ARGB фактически более распространен.Если вам нужен 24-битный RGB, вам нужно будет немного креативнее.«Руководство разработчика программного обеспечения» для вашего процессора станет вашим лучшим другом при написании этого небольшого фрагмента кода, и вам также необходимо прочитать документацию для вашего компилятора.

SIMD очень хорошо справится с этим, вы можетерасскажите, насколько короткий код ниже.Обратите внимание, что приведенный ниже код на самом деле C99, а не C ++, поскольку C99 предоставляет доступ к ключевому слову restrict, которое может уменьшить количество загрузок и сгенерированных хранилищ.

Также обратите внимание, что этот код имеет строгие требования выравнивания.1008 *

#include <stddef.h>

#if defined(USE_MMX)

typedef char v8qi __attribute__ ((vector_size(8)));
void pack_planes3(void *dest, const void *src[3], size_t n)
{
    v8qi *restrict dp = dest, x, y, zero = { 0, 0, 0, 0, 0, 0, 0, 0 };
    const v8qi *restrict sp1 = src[0];
    const v8qi *restrict sp2 = src[1];
    const v8qi *restrict sp3 = src[2];
    size_t i;
    for (i = 0; i < n; i += 8) {
        x = __builtin_ia32_punpckhbw(*sp1, *sp3);
        y = __builtin_ia32_punpckhbw(*sp2, zero);
        dp[0] = __builtin_ia32_punpckhbw(x, y);
        dp[1] = __builtin_ia32_punpcklbw(x, y);
        x = __builtin_ia32_punpcklbw(*sp1, *sp3);
        y = __builtin_ia32_punpcklbw(*sp2, zero);
        dp[2] = __builtin_ia32_punpckhbw(x, y);
        dp[3] = __builtin_ia32_punpcklbw(x, y);
        sp1++;
        sp2++;
        sp3++;
        dp += 4;
    }
}

#else

/* Scalar implementation goes here */

#endif
1 голос
/ 01 сентября 2011

libswscale (часть ffmpeg) может сделать это, насколько я знаю, хороший учебник можно найти здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...