| Дата последнего изменения : | 14.08.2009 07:39 |
Рейтинг |
|
Цель данной статьи – познакомить читателя с алгоритмами сжатия и декодирования видеоизображений, а также дать описание двух спецификаций сжатия видеозаписей и рассмотреть вопросы сжатия видеоизображений при помощи библиотеки Intel® Integrated Performance Primitives (Intel® IPP).
Кодеры и декодеры видеозаписей и изображений, которые в терминологии программного обеспечения носят название кодеков, предназначены для сжатия мультимедийных данных с целью их хранения или передачи. Необработанные изображения имеют достаточно большой размер, а современные технологии практически не позволяют работать с несжатым цифровым видео. Более того, нет необходимости работать с мультимедийными данными в несжатом формате (кроме как для захвата и отображения), к тому же, с учетом возможностей современных процессоров, такие операции были бы неэффективными. Считывание с диска и распаковка сжатой видеозаписи происходит гораздо быстрее, чем несжатой.
В основе большинства алгоритмов сжатия лежит принцип избыточности и предсказуемости данных, что позволяет сократить объем информации, необходимый для кодирования видеосигнала. Среди наиболее распространенных методов можно выделить кодирование длин серий (run-length, RLE), в ходе которого происходит преобразование серий данных в цепочки с указанием количества повторов. Еще одним распространенным способом является неравномерное кодирование (variable-length), при котором данные фиксированной длины кодируются в виде данных переменной длины, в зависимости от частоты их использования. В качестве примера неравномерного кодирования можно привести алгоритм сжатия Хаффмана и арифметическое кодирование. Оба способа принято относить к методам сжатия без потери данных.
Другой способ кодирования основывается на принципе ограниченного восприятия. Несомненно, для некоторых типов данных, таких как текст или исполняемые двоичные файлы, сжатие должно проходить без потерь. Метод сжатия, при котором «a» иногда меняется на «A» будет недопустим. Независимое кодирование Хаффмана является полностью обратимым. Однако существуют методы сжатия мультимедийной информации, при которых ее полное обратное преобразование будет не только затруднительным, но и практически невозможным. Такие методы называют сжатием с потерями. Это означает, что данные до и после сжатия могут отличаться друг от друга. Однако в большинстве случаев потери являются незначительными или не очевидными для зрителя. Как и при кодировании звукозаписей, алгоритм сжатия служит для преобразования данных в области, из которых информация может быть исключена без ощутимого воздействия на материал.
Для сжатия большинства мультимедийных данных обычно используются методы кодирования, основанные на преобразовании. При использовании таких методов позиционная информация преобразовывается в частотную или позиционно-частотную информацию. Преимуществом сжатия является то, что для представления важной информации требуется меньшее количество данных. Соответственно, для представления более важной информации используется большее количество бит, а менее важной – меньшее количество бит. Модель восприятия диктует степень важности информации, но, как правило, информация с более высокой частотностью считается менее важной.
На рисунке 1 показана структура схемы кодирования и декодирования, основанная на преобразовании.
Рисунок 1. Упрощенная схема кодирования изображений с преобразованием
В алгоритмах сжатия видеоизображений обычно используется второй источник избыточности и повторяемости видеокадров. Кодек кодирует необработанные видеокадры или разницу между последовательными кадрами, часто компенсирующую движение.
В библиотеке Intel IPP сжатие видеозаписей происходит аналогично сжатию файлов формата JPEG с несколькими вариантами. Библиотека Intel IPP содержит компоненты кодеков и модели, образующие неполные кодеки для нескольких алгоритмов сжатия. В частности, библиотека включает:
Далее мы расскажем о каждом из этих элементов для двух алгоритмов сжатия MPEG-2 и H.264, а также приведем описание и примеры UMC. Все описания указанных выше элементов будут рассмотрены на примерах из моделей кодеков.
В данном разделе приводится описание видеочасти стандарта MPEG-2.
Стандарт MPEG-2 предназначен для высококачественных видеозаписей с высокой полосой пропускания. Это наиболее известный стандарт, поскольку он используется для кодирования видеозаписей в форматах DVD и HDTV. Для достижения приемлемого уровня кодирования требуются значительные вычислительные ресурсы, но процесс может выполняться в реальном времени на системах с современными процессорами. Декодирование потока MPEG-2 осуществляется довольно просто и может выполняться практически любым процессором либо бытовыми DVD-проигрывателями.
Проигрыватели формата MPEG-2 должны также уметь воспроизводить видео стандарта MPEG-1. Стандарт сжатия MPEG-1 во многом аналогичен MPEG-2, но отличается потоком битов с меньшим разрешением и компенсацией движения. Этот стандарт используется для сжатия видеоизображения формата VCD.
MPEG-2 является сложным форматом с множеством настроек. Он включает семь профилей коэффициентов сжатия и наборы функций, четыре уровня настройки разрешения, скорости потока и частоты кадров, а также три типа кадров. Код потока достаточно сложный и требует применения нескольких таблиц. Но, несмотря на то, что в его основе заложены сложные для расчета элементы сжатия и распаковки, стандарт с концептуальной точки зрения четко сформулирован. Элементы данного стандарта будут рассмотрены в настоящем разделе.
Компоненты MPEG-2 очень похожи на JPEG компоненты: процесс кодирования в MPEG-2 основан на дискретном косинусном преобразовании (DCT, Discrete Cosine Transform) и использует алгоритм кодирования Хаффмана с квантованными коэффициентами DCT. Однако, форматы потока данных отличаются, как и все таблицы. В отличие от JPEG, при кодировании в MPEG-2 используется ограниченный, но очень большой набор частот кадров и размеров. Но самым существенным отличием является принцип использования избыточности между кадрами.
Кадры в MPEG бывают трех видов: I-кадры (intra-кадры или опорные кадры), P-кадры (прогнозируемые) и B-кадры (двунаправленные). Последовательности типов кадров могут быть различными, но определяющей характеристикой является алгоритм предсказания. Intra-кадры независимы от других, что делает удобным их использование в качестве опорных. Фактически, они являются самостоятельными сжатыми изображениями. Для сравнения, P-кадры предсказываются на основе предшествующего P- или I-кадра, а B-кадры предсказываются по предшествующему или следующему P- или I-кадру. Однако отдельные блоки в таких кадрах могут относиться либо не относиться к Intra-кадрам.
Формат MPEG построен на иерархии блоков, макроблоков, срезов и кадров. Размер блоков составляет 8х8 пикселей на один канал. Макроблоки представляет собой совокупность блоков размером 16х16 пикселей, и занимают все три канала. В зависимости от субдискретизации макроблок может состоять из 6, 8 или 12 блоков. Например, в макроблоке YCbCr 4:2:0 – четыре блока Y, один Cb и один Cr.
Далее будет описаны основные блоки кодека MPEG-2 в порядке кодирования. На рисунке 2 представлены взаимосвязи между этими блоками.
Ключом к эффективности кодирования видеосигнала является использование предыдущих или последующих кадров для прогнозирования значения каждого пикселя. При сжатии изображений, в качестве опорного значения для каждого пикселя может использоваться только блок в другой части изображения, но при сжатии видеоизображения может потребоваться использование изображения того же объекта. Вместо сжатия пикселей, обладающих высоким уровнем энтропии, при кодировании видео может происходить сжатие различий между сходными пикселями, которые имеют намного более низкий уровень энтропии.
Однако объекты и фон видеоизображения не всегда неподвижны. Для создания по-настоящему эффективной связи с другими видеокадрами, кодек должен учитывать движение между кадрами. Эта задача решается посредством оценки и компенсации движения. Наряду с видео данными, каждому блоку также соответствовать векторы движения, которые определяют степень смещения определенного кадра по отношению к опорному изображению. Прежде чем принимать во внимание разницу между текущим и опорным кадром, кодек смещает опорный кадр на соответствующую величину. Расчет векторов движения называется оценкой движения, а адаптация движения называется компенсацией движения.
Подобная компенсация движения является важной и ресурсоемкой операцией при сжатии видео. Фактически, существенная разница между стандартами MPEG-1 и MPEG-2 заключается в переходе от точности уровня пикселя к точности уровня полупикселя. Такое изменение позволяет добиться значительного качества при заданном потоке данных, но и делает процесс кодирования в MPEG-2 более длительным.
Как и сжатие в JPEG, сжатие в MPEG основывается на дискретном косинусном преобразовании. Кодек рассчитывает значение DCT для каждого блока размером 8x8 пикселей или разницу данных для каждого кадра. Информацию о частотности проще упорядочить по степени важности для зрителя и квантовать; при этом используются неизменяемые области каждого кадра.
Рисунок 2. Высокоуровневые блоки кодирования и декодирования в MPEG-2
Для различных типов блоков в MPEG квантование выполняется по-разному. Существуют различные матрицы значений коэффициентов для Intra- и не Intra-макроблоков, а также данных о цвете и насыщенности. Для всех матриц также используется шкала. Каждый макроблок может изменяться по шкале и матрице квантования.
Для Intra-блоков, коэффициент DC или нулевой частоты квантуется путем отбрасывания младших бит (от 0 до 3), то есть путем смещения вправо на величину от нуля до трех бит. Коэффициенты AC присваиваются шагам квантования согласно общей шкале и матрице. Квантование происходит в линейном режиме.
Для не Intra-блоков, DC-составляющая содержит менее важную информацию и с большей вероятностью стремится к нулю. Таким образом, составляющие DC и AC квантуются одинаково по не Intra-матрице квантования и шкале.
Чтобы меньший уровень энтропии видео данных приводил к уменьшению скорости передачи данных в потоке, данные необходимо перекодировать с использованием меньшего количества бит. В стандарте MPEG, так же как и JPEG, данная операция называется схемой кодирования переменных длин по алгоритму Хаффмана. Каждый отрезок данных представляется кодом, длина которого обратно связана с его частотой. По причине сложности кодирования в MPEG-2, существуют десятки таблиц кодов для коэффициентов, типов блоков и другой информации.
Для Intra-блоков коэффициент DC не кодируется напрямую. Вместо этого использует разница между ним и предсказывающим блоком. Предсказывающий блок может быть значением DC (при наличии) и Intra-значением или постоянным средним значением, во втором случае.
Для упорядочивания коэффициентов DCT используется две матрицы сканирования. Для одной формируется зигзагообразный шаблон, близкий к диагональной симметрии для не чередующихся блоков, а другая образует измененный зигзагообразный профиль для чередующихся блоков. Матрицы располагают коэффициенты в порядке повышения частоты, пытаясь сделать длины серий данных максимальными.
Кодер выполняет кодирование данных потока для этой матрицы. Каждая пара уровня потока представляет число последовательных точек определенного уровня. Для наиболее распространенных пар, коды помещаются в таблицу Хаффмана. Менее частные коды, например цепочки длиной более 31, заключается в управляющую последовательность с 6-битной серией и 12-битным уровнем.
В библиотеке Intel IPP содержится эффективная модель кодера и декодера для формата MPEG-2. Поскольку существуют различные варианты, это всего лишь модель, а не утвержденный кодек.
В каждом из функциональных компонентов кодека записаны сотни вызовов функций Intel IPP. Основная часть кода в модели служит для интерпретации потока битов и манипуляции с данными, но большая часть времени уходит на декодирование пикселей. В связи с этим практически все вызовы Intel IPP используются для блоков декодирования пикселей. В частности, основные высокоуровневые функции принадлежат классу
MPEG2VideoDecoderBase: DecodeSlice_FrameI_420 DecodeSlice_FramePB_420 DecodeSlice_FieldPB_420 DecodeSlice_FrameI_422 DecodeSlice_FramePB_422 DecodeSlice_FieldPB_422
Эти функции декодируют структуру изображения, а затем переходят к выполнению функции декодирования отдельных блоков, такую как ippiDecodeIntra8x8IDCT_MPEG2_1u8u. На рисунке 2 показаны основные отрывки кода этих двух функций.
Status MPEG2VideoDecoderBase::DecodeSlice_FrameI_420(
IppVideoContext *video)
{
...
DECODE_VLC(macroblock_type, video->bs, vlcMBType[0]);
if (load_dct_type) {
GET_1BIT(video->bs, dct_type);
}
if (macroblock_type & IPPVC_MB_QUANT)
{
DECODE_QUANTIZER_SCALE(video->bs,
video->cur_q_scale);
}
if (PictureHeader.concealment_motion_vectors)
{
if (PictureHeader.picture_structure !=
IPPVC_FRAME_PICTURE) {
SKIP_BITS(video->bs, 1);
}
mv_decode(0, 0, video);
SKIP_BITS(video->bs, 1);
}
RECONSTRUCT_INTRA_MB_420(video->bs, dct_type);
}
}//DecodeSlice_FrameI_420
#define RECONSTRUCT_INTRA_MB_420(BITSTREAM, DCT_TYPE) \
RECONSTRUCT_INTRA_MB(BITSTREAM, 6, DCT_TYPE)
#define RECONSTRUCT_INTRA_MB(BITSTREAM, NUM_BLK, DCT_TYPE) \
{ \ ...
for (blk = 0; blk < NUM_BLK; blk++) { \
sts = ippiDecodeIntra8x8IDCT_MPEG2_1u8u( ... ); \
} \
}
Status MPEG2VideoDecoderBase::DecodeSlice_FramePB_420(
IppVideoContext *video)
{
...
if (video->prediction_type == IPPVC_MC_DP) {
mc_dualprime_frame_420(video);
} else {
mc_frame_forward_420(video);
if (video->macroblock_motion_backward) {
mc_frame_backward_add_420(video);
}
}
} else {
if (video->macroblock_motion_backward) {
mc_frame_backward_420(video);
} else {
RESET_PMV(video->PMV)
mc_frame_forward0_420(video);
}
}
if (macroblock_type & IPPVC_MB_PATTERN) {
RECONSTRUCT_INTER_MB_420(video->bs, dct_type);
}
}
return UMC_OK;
}//DecodeSlice_FramePB_420
void MPEG2VideoDecoderBase::mc_frame_forward0_422(
IppVideoContext *video)
{
MC_FORWARD0(16, frame_buffer.Y_comp_pitch,
frame_buffer.U_comp_pitch);
}
#define MC_FORWARD0(H, PITCH_L, PITCH_C) \
...
ippiCopy16x16_8u_C1R(ref_Y_data + offset_l, PITCH_L, \
cur_Y_data + offset_l, PITCH_L); \
ippiCopy8x##H##_8u_C1R(ref_U_data + offset_c, PITCH_C, \
cur_U_data + offset_c, PITCH_C); \
ippiCopy8x##H##_8u_C1R(ref_V_data + offset_c, PITCH_C, \
cur_V_data + offset_c, PITCH_C);
#define RECONSTRUCT_INTER_MB_420(BITSTREAM, DCT_TYPE) \
RECONSTRUCT_INTER_MB(BITSTREAM, 6, DCT_TYPE)
#define RECONSTRUCT_INTER_MB(BITSTREAM, NUM_BLK, DCT_TYPE) \
...
for (blk = 0; blk < NUM_BLK; blk++) { \
...
sts = ippiDecodeInter8x8IDCTAdd_MPEG2_1u8u(...);
Рисунок 2. Структура декодирования Intra-макроблока в MPEG-2
При декодировании две группы функций Intel IPP образуют большую часть конвейера декодирования. Между ними встраивается часть декодера MPEG-2, по крайней мере, для Intra-блоков.
Первая группа – это ippiReconstructDCTBlock_MPEG2 для не Intra-блоков, а вторая – ippiReconstructDCTBlockIntra_MPEG2 для Intra-блоков. Указанные функции выполняют декодирование данных алгоритма Хаффмана, заново упорядочивают и деквантуют их. Источником выступает закодированный по методу Хаффмана поток битов, указывающий на верхнюю часть блока, а объектом – блок размером 8x8 последовательных коэффициентов DCT.
В алгоритме декодирования Хаффмана используются отдельные таблицы для кодов AC и DC в формате, отвечающим структуре спецификации Intel IPP. Аргумент матрицы сканирования определяет используемый зигзагообразный шаблон. Функции также принимают два аргумента для квантования, матрицу и коэффициент масштабирования. Каждый элемент умножается на соответствующий элемент в матрице квантования, и затем на общий коэффициент масштабирования.
Функция ReconstructDCTBlockIntra также принимает два аргумента для обработки коэффициента DC: опорное значение и смещение. Функция выполняет сложение опорного значения, которое часто берется из последнего блока, с коэффициентом DC. Коэффициент DC смещается аргументом смещения, который может иметь значение от нуля до трех бит, как уже было сказано ранее.
Вторая основная функция – обратное DCT. Две наиболее полезные функции DCT – это ippiDCT8x8InvLSClip_16s8u_C1R для Intra-блоков и ippiDCT8x8Inv_16s_C1R для не Intra-блоков. Кроме того, могут использоваться варианты без смещения уровня и обрезания. Первая функция выполняет обратное DCT с блоком 8x8 и затем преобразовывает данные в Ipp8u со смещением уровня. В результате получаются пиксели. Вторая функция применяет обратное DCT и оставляет результат в Ipp16s – получаются значения расхождения. Декодер теперь должен сложить полученные значения расхождения с опорным блоком с компенсацией движения.
На рисунке 4 показаны группы функций, выполняющие декодирование внутреннего макроблока 4:2:0. На вход подается поток битов и несколько предварительно рассчитанных таблиц. Результатом DCT являются данные о пикселях непосредственно в плоскости изображения. Четыре блока Y-данных помещаются в массив размером 2x2 в соответствующем изображении, а блоки U и V помещаются в аналогичное положение в плоскостях U и V. Результат отображается непосредственно на соответствующем экране. Кроме того, плоскости U и V можно восстановить для получения изображения YCbCr 4:4:4, или преобразовать все три плоскости при помощи других функций Intel IPP в RGB для просмотра.
ippiReconstructDCTBlockIntra_MPEG2_32s(
&video->bitstream_current_data,
&video->bitstream_bit_ptr,
pContext->vlcTables.ippTableB5a,
pContext->Table_RL,
scan_1[pContext->PictureHeader.alternate_scan],
q_scale[pContext->PictureHeader.q_scale_type]
[pContext->quantizer_scale],
video->curr_intra_quantizer_matrix,
&pContext->slice.dct_dc_y_past,
pContext->curr_intra_dc_multi,
pContext->block.idct, &dummy);
ippiReconstructDCTBlockIntra_MPEG2_32s(
…
pContext->block.idct+64, &dummy);
…
// Repeat two more times for other Y blocks
ippiReconstructDCTBlockIntra_MPEG2_32s(…)
…
VIDEO_FRAME_BUFFER* frame =
&video->frame_buffer.frame_p_c_n
[video->frame_buffer.curr_index];
// Inverse DCT and place in 16x16 block of image
ippiDCT8x8InvLSClip_16s8u_C1R(
pContext->block.idct,
frame->Y_comp_data + pContext->offset_l,
pitch_Y, 0, 0, 255);
ippiDCT8x8InvLSClip_16s8u_C1R(
pContext->block.idct,
frame->Y_comp_data + pContext->offset_l + 8,
pitch_Y, 0, 0, 255);
ippiDCT8x8InvLSClip_16s8u_C1R(
pContext->block.idct,
frame->Y_comp_data + pContext->offset_l + 8*pitch_Y,
pitch_Y, 0, 0, 255);
ippiDCT8x8InvLSClip_16s8u_C1R(
pContext->block.idct,
frame->Y_comp_data +
pContext->offset_l + 8*pitch_Y + 8,
pitch_Y, 0, 0, 255);
…
ippiReconstructDCTBlockIntra_MPEG2_32s(
&video->bitstream_current_data,
&video->bitstream_bit_ptr,
pContext->vlcTables.ippTableB5b,
pContext->Table_RL,
scan_1[pContext->PictureHeader.alternate_scan],
q_scale[pContext->PictureHeader.q_scale_type]
[pContext->quantizer_scale],
video->curr_chroma_intra_quantizer_matrix,
&pContext->slice.dct_dc_cb_past,
pContext->curr_intra_dc_multi,
pContext->block.idct, &i1);
ippiReconstructDCTBlockIntra_MPEG2_32s(
…
&pContext->slice.dct_dc_cr_past,
pContext->curr_intra_dc_multi,
pContext->block.idct + 64,&i2);
ippiDCT8x8InvLSClip_16s8u_C1R (
pContext->block.idct,
frame->U_comp_data + pContext->offset_c,
pitch_UV, 0,0,255);
ippiDCT8x8InvLSClip_16s8u_C1R (
pContext->block.idct + 64,
frame->V_comp_data + pContext->offset_c,
pitch_UV, 0,0,255);
Рисунок 3. Декодирование Intra-макроблока в MPEG-2
Подставной параметр перед первым вызовом ippiReconstructDCTBlock здесь отсутствует, но может использоваться для оптимизации. Возврат значения 1 означает, что только коэффициент DC не равен нулю и обратное DCT можно пропустить. Если значение меньше 10, то все ненулевые коэффициенты находятся в первом блоке 4x4, и можно использовать обратное DCT блока 4х4.
Вместо функции ippiDCT8x8InvLSClip_16s8u_C1R можно вызвать ippiDCT8x8Inv_16s8u_C1R, поскольку данные ограничиваются диапазоном от 0 до 255 по умолчанию.
В случае с не Intra-блоками, ссылка на матрицу квантования может быть равна 0. При этом будут использоваться матрицы по умолчанию.
На рисунке 4 представлен еще один подход к декодированию с использованием модели MPEG-2 для Intel IPP 5.2. Вместо использования функции ippiReconstructDCTBlock для декодирования, здесь реализуется псевдо-IPP функция под названием ippiDecodeIntra8x8IDCT_MPEG2_1u8u. Указанная функция охватывает практически весь цикл декодирования – от кодирования переменных длин до компенсации движения.
При использовании этой функции большая часть процесса декодирования выполняется на языке C++ с широким применением макросов и логики состояний. Декодирование по модели Хаффмана в этой модели выполняется на языке C++ с использованием макросов. Квантование выполняется на языке C++ для каждой модели по мере ее декодирования. Компенсация движения выполняется вместе с DCT одним из макросов DCT.
Вызовы функций используют несколько функций DCT. Большая часть DCT выполняется двумя полезными функциями: ippiDCT8x8Inv_16s8u_C1R и ippiDCT8x8Inv_16s_C1R для Intra- и не Intra-блоков соответственно. Первая функция преобразует результат в Ipp8u, поскольку для внутренних блоков эти значения представляют пиксели. Вторая функция оставляет результат в Ipp16s, поскольку получаемые значения является значениями расхождения, прибавляемыми к опорному блоку с компенсацией движения. В модели также используются другие функции DCT, например специализированная функция ippiDCT8x8Inv_AANTransposed, которая допускает, что дискретные значения транспонированы и образуют зигзагообразный профиль, и устанавливает явные нулевые коэффициенты в конце. Для блоков, содержащих преимущественно нули, декодер также использует функцию ippiDCT8x8Inv_4x4_16s_C1.
MP2_FUNC(IppStatus, ippiDecodeInter8x8IDCTAdd_MPEG2_1u8u, (
Ipp8u** BitStream_curr_ptr,
Ipp32s* BitStream_bit_offset,
IppiDecodeInterSpec_MPEG2* pQuantSpec,
Ipp32s quant,
Ipp8u* pSrcDst,
Ipp32s srcDstStep))
{
// VLC decode & dequantize for one block
for (;;) {
if ((code & 0xc0000000) == 0x80000000) {
break;
} else if (code >= 0x08000000) {
tbl = MPEG2_VLC_TAB1[UHBITS(code - 0x08000000, 8)];
common:
i++;
UNPACK_VLC1(tbl, run, val, len)
i += run;
i &= 63; // just in case
j = scanMatrix[i];
q = pQuantMatrix[j];
val = val * quant;
val = (val * q) >> 5;
sign = SHBITS(code << len, 1);
APPLY_SIGN(val, sign);
SKIP_BITS(BS, (len+1));
pDstBlock[j] = val;
mask ^= val;
SHOW_HI9BITS(BS, code);
continue;
} else if (code >= 0x04000000) {
...
}
}
...
pDstBlock[63] ^= mask & 1;
SKIP_BITS(BS, 2);
COPY_BITSTREAM(*BitStream, BS)
IDCT_INTER(pDstBlock, i, idct, pSrcDst, srcDstStep);
return ippStsOk;
}
#define FUNC_DCT8x8 ippiDCT8x8Inv_16s_C1
#define FUNC_DCT4x4 ippiDCT8x8Inv_4x4_16s_C1
#define FUNC_DCT2x2 ippiDCT8x8Inv_2x2_16s_C1
#define FUNC_DCT8x8Intra ippiDCT8x8Inv_16s8u_C1R
#define FUNC_ADD8x8 ippiAdd8x8_16s8u_C1IRS
#define IDCT_INTER(SRC, NUM, BUFF, DST, STEP) \
if (NUM < 10) { \
if (!NUM) { \
IDCTAdd_1x1to8x8(SRC[0], DST, STEP); \
} else \
IDCT_INTER_1x4(SRC, NUM, DST, STEP) \
/*if (NUM < 2) { \
FUNC_DCT2x2(SRC, BUFF); \
FUNC_ADD8x8(BUFF, 16, DST, STEP); \
} else*/ { \
FUNC_DCT4x4(SRC, BUFF); \
FUNC_ADD8x8(BUFF, 16, DST, STEP); \
} \
} else { \
FUNC_DCT8x8(SRC, BUFF); \
FUNC_ADD8x8(BUFF, 16, DST, STEP); \
}
Рисунок 4. Вариант декодирования не Intra-макроблока в MPEG-2
Функции DCT библиотеки Intel IPP также поддерживают альтернативную, гибридную структуру YUV-данных, состоящую из двух плоскостей – Y и UV. В плоскости UV U- и V-данные чередуются. В этом случае, макроблок состоит из блока UV-данных размером 16x8. В альтернативной структуре функция библиотеки Intel IPP ippiDCT8x8Inv_AANTransposed_16s_P2C2R поддерживает не Intra-кадры, а функция ippiDCT8x8Inv_AANTransposed_16s8u_P2C2R – Intra-кадры. Функция ippiMC16x8UV_8u_C1 и ippiMC16x8BUV_8u_C1 поддерживает компенсацию движения в этой конфигурации.
При кодировании функции практически аналогичны функциям декодирования, перечисленным выше. Для Intra-блоков функция прямого DCT ippiDCT8x8Fwd_8u16s_C1R преобразует блок пикселей Ipp8u в коэффициенты DCT Ipp16s. Затем функция ippiQuantIntra_MPEG2 выполняет квантование, а функция ippiPutIntraBlock рассчитывает пары серий, которые кодируются по алгоритму Хаффмана. Параметры для двух последних функций очень похожи на параметры, используемые для аналогичных функций декодирования.
Для не Intra-блоков, функция ippiDCT8x8Fwd_16s_C1R преобразует разностную информацию в коэффициенты DCT, функция ippiQuant_MPEG2 выполняет квантование, а функция ippiPutNonIntraBlock рассчитывает и кодирует пары серий.
Процесс оценки движения кодером требует значительных вычислительных ресурсов, поскольку, в сущности, требуется провести повторную оценку эффективности потенциальных векторов компенсации движения. Однако возможные векторы движения выбираются при помощи функции быстрой оценки, которая позволяет ускорить выполнение алгоритма. Функции библиотеки Intel IPP ippiSAD16x16, ippiSqrDiff16x16 и ippiSqrDiff16x16 сравнивают блоки из одного кадра с блоками опорного кадра, для которых была выполнена компенсация движения. Функция ippiSAD вычисляет сумму абсолютной разницы между пикселями, в то время как ippiSqrDiff определяет сумму квадратов разностей. В модели Intel IPP используется первая функция.
После того, как кодер определяет пространство возможных векторов движения, он может использовать многие функции ippiGetDiff для вычисления разницы между текущим и опорным кадром после компенсации движения.
Алгоритм компенсации движения необходим как кодеру, так и декодеру. Для объединения опорного кадра с данными о декодированной разнице, в алгоритмах из библиотеки Intel IPP могут использоваться функции ippiMC или ippiAdd. На рисунке 6 показан такой алгоритм для макроблока из B-кадра 4:2:0.
// Determine whether shift is half or full pel
// in horizontal and vertical directions
// Motion vectors are in half-pels in bitstream
// The bit code generated is:
// FF = 0000b; FH = 0100b; HF = 1000b; HH = 1100b
flag1 = pContext->macroblock.prediction_type |
((pContext->macroblock.vector[0] & 1) << 3) |
((pContext->macroblock.vector[1] & 1) << 2);
flag2 = pContext->macroblock.prediction_type|
((pContext->macroblock.vector[0] & 2) << 2) |
((pContext->macroblock.vector[1] & 2) << 1);
flag3 = pContext->macroblock.prediction_type|
((pContext->macroblock.vector[2] & 1) << 3) |
((pContext->macroblock.vector[3] & 1) << 2);
flag4 = pContext->macroblock.prediction_type|
((pContext->macroblock.vector[2] & 2) << 2) |
((pContext->macroblock.vector[3] & 2) << 1);
// Convert motion vectors from half-pels to full-pel
// also convert for chroma subsampling
// down, previous frame
vector_luma[1] = pContext->macroblock.vector[1] >>1;
vector_chroma[1] = pContext->macroblock.vector[1] >>2;
// right, previous frame
vector_luma[0] = pContext->macroblock.vector[0] >> 1;
vector_chroma[0] = pContext->macroblock.vector[0] >> 2;
// down, subsequent frame
vector_luma[3] = pContext->macroblock.vector[3] >> 1;
vector_chroma[3] = pContext->macroblock.vector[3] >> 2;
// right, subsequent frame
vector_luma[2] = pContext->macroblock.vector[2] >> 1;
vector_chroma[2] = pContext->macroblock.vector[2] >> 2;
offs1 =
(pContext->macroblock.motion_vertical_field_select[0] +
vector_luma[1] + pContext->row_l) * pitch_y +
vector_luma[0] + pContext->col_l,
offs2 =
(pContext->macroblock.motion_vertical_field_select[1] +
vector_luma[3] + pContext->row_l) * pitch_y +
vector_luma[2] + pContext->col_l,
i = ippiMC16x16B_8u_C1(
ref_Y_data1 + offs1, ptc_y, flag1,
ref_Y_data2 + offs2, ptc_y, flag3,
pContext->block.idct, 32,
frame->Y_comp_data + pContext->offset_l,
ptc_y, 0);
assert(i == ippStsOk);
offs1 =
(pContext->macroblock.motion_vertical_field_select[0] +
vector_chroma[1] + pContext->row_c)* pitch_uv +
vector_chroma[0] + pContext->col_c;
offs2 =
(pContext->macroblock.motion_vertical_field_select[1] +
vector_chroma[3] + pContext->row_c)* pitch_uv +
vector_chroma[2] + pContext->col_c;
i = ippiMC8x8B_8u_C1(
ref_U_data1 + offs1, ptc_uv, flag2,
ref_U_data2 + offs2, ptc_uv, flag4,
pContext->block.idct+256,16,
frame->U_comp_data + pContext->offset_c,
ptc_uv, 0);
assert(i == ippStsOk);
i = ippiMC8x8B_8u_C1(
ref_V_data1 + offs1, ptc_uv,flag2,
ref_V_data2 + offs2, ptc_uv,flag4,
pContext->block.idct+320,16,
frame->V_comp_data + pContext->offset_c,
ptc_uv, 0);
assert(i == ippStsOk);
Рисунок 5. Двунаправленная компенсация движения в MPEG-2
Сначала точность векторов движения необходимо перевести от полупикселей к полным пикселям, поскольку информация о полупикселях передается в функцию ippiMC в виде флага. Код опускает наименее значимый бит каждого вектора движения и использует его для создания флага. Затем начальная точка каждого опорного блока смещается по вертикали и горизонтали на величину вектора движения.
Поскольку этот код используется для двунаправленного прогнозирования, все эти действия повторяются для двух отдельных векторов движения и двух отдельных опорных кадров. На этом заключительном этапе декодирования результат помещается непосредственно в получаемый кадр YCbCr.
Стандартные функции преобразования цветовой палитры из библиотеки Intel IPP поддерживают преобразование в формат YCbCr 4:2:2, 4:2:0 и 4:4:4 и обратно. Поскольку, в принципе, эти функции представляют собой наборы преобразования цветов, они называются RGBToYUV422 / YUV422ToRGB, RGBToYUV420 / YUV420ToRGB и RGBToYUV / YUVToRGB. Эти функции поддерживают чередующиеся и планарные YCbCr-данные. На рисунке 7 представлено преобразование декодированных пикселей MPEG-2 в палитру RGB для отображения.
src[0] = frame->Y_comp_data +
pContext->Video[0].frame_buffer.video_memory_offset;
src[1] = frame->V_comp_data +
pContext-Video[0].frame_buffer.video_memory_offset/4;
src[2] = frame->U_comp_data +
pContext->Video[0].frame_buffer.video_memory_offset/4;
srcStep[0] = frame->Y_comp_pitch;
srcStep[1] = pitch_UV;
srcStep[2] = pitch_UV;
ippiYUV420ToRGB_8u_P3AC4R(src, srcStep, video_memory +
pContext->Video[0].frame_buffer.video_memory_offset/4,
roi.width<<2, roi);
Рисунок 6. Преобразование цветовой модели YCbCr 4:2:0 в RGB для просмотра
Два семейства видеокодеков с номенклатурой H.26x и MPEG-x пересекаются. MPEG-2 имеет название H.262 в схеме H.26x. Подобный образом, другой популярный кодек – H.264 – является подмножеством стандарта MPEG-4, который также известен под названием MPEG-4 Advanced Video Coding (AVC). Его целью, как и всех кодеков группы MPEG-4, было сжатие видеосигнала с приемлемым качеством при очень низкой скорости потока, примерно в два раза ниже, чем той, которая использовалась в его предшественниках, MPEG-2 и H.263.
В настоящем разделе приводится описание компонентов стандарта H.264 и рассказывается о том, как каждый из этих компонентов реализован в Intel IPP.
Как и ранние представители семейства H.26x, стандарт H.264 предусматривает два режима кодирования для отдельных видеокадров – Intra (опорные) и Inter (промежуточные). В первом случае, кадр видеоизображения кодируется как независимое изображение без опоры на другие изображения в видеопоследовательности. Во втором случае, для предсказания значений используются предыдущие и, возможно, следующие кадры. На рисунке 7 показаны высокоуровневые блоки, задействованные в кодировании и декодировании Intra-кадров по алгоритму H.264. На рисунке 9 представлен процесс кодирования и декодирования для Inter-кадров.
Далее в данном разделе приводится описание каждого из этих блоков в порядке их обработки кодером.
Рисунок 7. Кодирование и декодирование Intra-кадров по алгоритму H.264
Рисунок 8. Кодирование и декодирование Inter-кадров по алгоритму H.264
Блоки в H.264 (в Inter- или Intra-кадрах) можно представлять относительно предыдущих или последующих блоков или кадров. В Inter-кадрах это называется оценкой движения и относится к блокам в других кадрах. Значительное сжатие происходит именно здесь. Как и в других методах сжатия видеоизображений, здесь действует утверждение, что энтропия значительно меньше в разнице между схожими блоками, чем в абсолютных значениях этих блоков. Это особенно справедливо, если разница между исходным и восстановленным блоками приходится на смещение от блока, расположенного в другом кадре.
Стандарт H.264 предусматривает очень гибкую поддержку компенсации движения. В процессе оценки возможен выбор опорных изображений из 32 других кадров. Также, можно установить связи с изображениями, которые необходимо сформировать интерполяцией.
Кодер отвечает за определение опорного изображения, блока и вектора движения. Такой блок, как правило, выбирается путем поиска среди возможных вариантов, начиная с наиболее вероятного. Затем кодер рассчитывает и выполняет кодирование разницы между предыдущими, закодированными блоками и новыми данными.
При декодировании (после декодирования опорных блоков) опорные данные и декодированные разностные данные складываются. С определенной вероятностью блоки и кадры декодируются вне временной последовательности, поскольку кадры можно кодировать по отношению к опережающим блокам и кадрам.
Кодирование по алгоритму H.264 поддерживает точность векторов движения до субпикселя, что означает, что опорный блок фактически вычисляется путем интерполяции внутри блока реальных пикселей. Векторы движения для блоков яркости выражены с точностью до четверти пикселя, а цветоразностные блоки – с точностью до восьмой части пикселя.
Такое субпиксельное разрешение значительно усложняет алгоритмику и процесс расчета. На процесс декодирования, требующий выполнение субпиксельной компенсации движения один раз для каждого блока, приходится от 10 до 20 процентов работы конвейера. Основная часть этого времени уходит на интерполяцию значений между пикселями для формирования опорных блоков с субпиксельным смещением. Ресурсоемкость процесса субпиксельной оценки зависит от алгоритма декодирования, но процесс может потребовать повторного выполнения компенсации движения.
Алгоритм интерполяции для формирования опорных блоков со смещением определяется по разному для блоков яркости и цветности. Для блоков яркости интерполяция выполняется в два этапа – интерполяция с точностью до полупикселя, и затем до четверти пикселя. Затем, путем фильтрования по горизонтали и вертикали получаются значения полупикселей:
[1 -5 20 20 -5 1]/32
Интерполяция с точностью до четверти пикселя выполняется по линейно усредненным, смежным полупикселям.
Компенсация движения цветовых блоков выполняется путем билинейной интерполяции с точности до четверти пикселя или восьмой части пикселя в зависимости от формата цветности. Каждое положение полупикселя представляет собой линейную группу соседних пикселей.
На рисунке 9 показано, какие пиксели используются для обоих вариантов интерполяции.
После выполнения интерполяции и создания опорного блока, алгоритм добавляет опорный блок к информации о декодированной разнице для получения восстановленного блока. Кодер выполняет это действие для получения восстановленных опорных кадров, а декодер – для получения выходных кадров.
Рисунок 9. Субпиксельная интерполяция для компенсации движения по алгоритму H.264
Структура Intra-кадров позволяет им не зависеть от предшествующих или следующих за ними кадров при восстановлении. Однако, в стандарте H.264 ранние блоки могут использоваться кодером из одного кадра в качестве опорных для новых блоков. Процесс прогнозирования Intra-кадров может обеспечить дополнительное сжатие Intra-макроблоков и может оказаться особенно эффективным, если будет найден соответствующим образом подходящий опорный блок.
Опорные блоки не используются так же, как промежуточные блоки прогнозирования, то есть не принимают попиксельную разницу фактических блоков смежных кадров. Вместо этого, значение прогнозирования текущего блока рассчитывается как среднее значение ряда пикселей, граничащих с ним. То, какие пиксели выбираются и как они используются для расчета блока, зависит от режима предсказания Intra-кадров. На рисунке 10 показаны направления, которым могут следовать пиксели, а также номера режимов, определенных спецификацией H.264 [JVT-G050].
Данная операция также может оказаться одной из самых ресурсоемких в вычислительном плане частей процесса кодирования. Для полного просчета всех возможных вариантов, кодер должен сравнить блок яркости 16x16 или цветности 8x8 с 4 другими блоками, и каждый блок яркости 4x4 или 8x8 с 9 другими боками.
Рисунок 10. Номера режимов для прогнозирования Intra-кадров по алгоритму H.264
Поскольку кодер способен обрабатывать разные размеры блоков, желательна схема, обеспечивающая достижение оптимального компромисса между необходимым числом битов для представления видеопоследовательности и точностью результатов.
Вместо DCT, алгоритм H.264 использует целочисленное преобразование в качестве основного режима преобразования для пересчета разностных данных между пространственными и частотными областями. Преобразование выполняется приближением DCT, что проще с вычислительной точки зрения и обеспечивает отсутствие потери данных. Базовое преобразование, показанное на рисунке 11, можно реализовать только при помощи смещения и сложения.
Такое преобразование блока 4x4 является лишь одной разновидностью преобразования по алгоритму H.264. В H.264 определяются преобразования для блоков 2x2 и 4x4 в базовом профиле, а дополнительные профили поддерживают преобразование для блоков большего размера – прямоугольных или квадратных с размерами со степенью двух.
Алгоритм применяет преобразование по отдельности для первого, или яркостного, DC и компонента цветности. В базовом профиле, в алгоритме H.264 используется одно преобразование DC-коэффициентов цветности блока 2x2, преобразование DC-коэффициентов яркости блока 4x4 и основное преобразование блока 4x4 для всех других коэффициентов.
Рисунок 11. Матрицы для преобразования по алгоритму H.264
На этапе квантования происходит сокращение объема информации путем деления каждого коэффициента на определенное число с целью уменьшения количества возможных значений, которые может принимать результат. Поскольку это приводит к сужению диапазона значений, статистическое кодирование позволяет выразить значения в более компактной форме.
Квантование по алгоритму H.264 с точки зрения арифметики представлено двухступенчатой операцией. Сначала каждый коэффициент в блоке 4x4 умножается на фиксированное значение коэффициента. Это позволяет масштабировать коэффициенты в зависимости от значимости или информации. Затем происходит деление на подстраиваемое значение параметра квантования (ПК). Это дает определенный «рычаг» для настройки качества и конечной скорости потока в результате кодирования. Две операции можно объединить в отдельную операцию умножения и смещения.
ПК выражен в виде целого числа от 0 до 51. Это целое число нелинейно преобразуется в длину шага квантования (QStep). Каждые 6 шагов увеличивают длину шага на 2, и между каждой парой длин шагов степени двух N и 2N получается 5 шагов: 1,125N, 1,25N, 1,375N, 1,625N, 1,75N.
При статистическом кодировании коэффициентов каждого макроблока, блоки обрабатываются кодеком в определенном порядке. Упорядочивание позволяет увеличить количество последовательных нулей.
Сохранение подобного порядка при расчете результата преобразования и квантования является принятой практикой.
Алгоритм H.264 определяет два режима статистического кодирования – контекстно-зависимое адаптивное кодирование с переменной длиной кодового слова (CAVLC) и контекстно-зависимое адаптивное бинарное арифметическое кодирование (CABAC).
Режим CAVLC можно рассматривать в качестве базового кодирования с переменной длиной кодового слова. Это традиционный алгоритм кодирования с переменной длиной кодового слова на основе таблицы кодов переменной длины бит с уникальными префиксами, но для большей эффективности стандарт определяет дополнительные таблицы. Выбор из таких таблиц и длина суффикса значений коэффициента фиксированной длины основывается на локальной статистике текущего потока в рамках контекста.
В режиме CAVLC используется 12 дополнительных кодовых таблиц: 6 для описания содержимого блока преобразования в целом, 4 для указания числа коэффициентов, 1 для указания общей величины квантованного значения коэффициента и 1 для представления последовательностей квантованных коэффициентов с нулевым значением. Эффективность выполнения таблиц кодирования с переменной длиной кодового слова вкупе с предельным адаптивным кодированием для повышения эффективности процесса позволяет достичь компромисса между скоростью выполнения и производительностью.
Режим CABAC доказал свою эффективность сжатия, превысив показатели режима CAVLC примерно на 10 процентов, однако режим CABAC гораздо сложнее в вычислительном плане. Сначала происходит выбор подходящей модели согласно группе прежних наблюдений за соответствующими элементами синтаксиса – это называется контекстным моделированием. Затем, если заданный символ имеет не двоичное значение, он будет сопоставлен с последовательностью двоичных решений, так называемых элементов кодированного сигнала. Подобная бинаризация выполняется с определенным двоичным значением на основе древовидной структуры, подобной коду VLC. После чего каждый элемент кодируется механизмом адаптивного двоичного арифметического кодирования с помощью вероятностных оценок, зависящих от определенного контекста. Схема выполнения конвейера показана на рисунке 12.
Рисунок 12. Конвейер арифметического кодирования по алгоритму H.264
Последним этапом перед восстановлением является обработка фильтром сглаживания блоков. Этот фильтр призван сгладить зрительную неравномерность между блоками преобразования, и именно он применяется только к пикселям, приближенным к этим границам – не более 4 с каждой стороны границ блока. Фильтр состоит из отдельных горизонтальных и вертикальных фильтров. На рисунке 13 показаны границы в макроблоке и пиксели, являющиеся объектом обработки горизонтальным фильтром вдоль вертикальной границы.
Стандарт H.264 устанавливает применение фильтра к кадрам после деквантования и прежде, чем изображение будет использоваться в качестве опорного для компенсации движения. Для Intra-кадров он используется после прогнозирования Intra-кадров.
Фильтрование в процессе декодирования требует значительных вычислительных ресурсов, отнимая от 15 до 30 процентов ресурсов ЦП для потоков с низким битовым потоков, где фильтрование используется особенно интенсивно.
Фильтр разделения блоков является адаптивным, и его эффективность настраивается автоматически согласно устойчивости границ и разнице между значениями пикселей на границе. Границы Intra-блоков устойчивей, чем Inter-блоков, так же как устойчивей, если рассматриваемые блоки имеют разностные опорные изображения, и устойчивей вдоль границы макроблока. Разницы значений пикселей должны быть меньше порогового значения, которое уменьшается с повышением качества. При малой величине параметра квантования, приводящей к повышению качества сжатых данных, любое значительное отклонение считается скорее особенностью изображения, чем ошибкой, и эффективность фильтра таким образом снижается. При малом значении длины шага квантования фильтр полностью отключается. Кодер может также принудительно отключить фильтр или настроить его эффективность на уровне среза.
Рисунок 13. Горизонтальный фильтр сглаживания блоков по алгоритму H.264
В этом разделе описана реализация большинства составляющих блоков алгоритма H.264 в библиотеке Intel IPP. Как и в предыдущем разделе, описание каждого блока приводится в порядке их обработки кодером.
Часть алгоритма H.264, требующая наибольшего объема вычислений, заключается в создании опорных блоков. Поскольку алгоритм H.264 допускает смещение на долю пикселя от фактических данных, в реализации должен использоваться определенный фильтр интерполяции для расчета блоков.
В библиотеке Intel IPP определен набор функций интерполяции для выполнения интерполяции в различных участках изображения. В число функций входят следующие:
ippiInterpolateLuma_H264_[8u|16u]_C1R ippiInterpolateLumaTop_H264_[8u|16u]_C1R ippiInterpolateLumaBottom_H264_[8u|16u]_C1R ippiInterpolateLumaBlock_H264_[8u|16u]_C1R ippiInterpolateChroma_H264_[8u|16u]_C1R ippiInterpolateChromaTop_H264_[8u|16u]_C1R ippiInterpolateChromaBottom_H264_[8u|16u]_C1R ippiInterpolateChromaBlock_H264_[8u|16u]_C1R
Указанные функции подразделяются на две группы: функции, обрабатывающие плоскость яркости и функции, обрабатывающие плоскости цветности. Их также можно разделить на подфункции, обрабатывающие блоки, для которых имеют значение все данные, а также данные, появляющиеся на границе кадров, за пределами которых они отсутствуют.
Функции, обрабатывающие все не пограничные блоки кадра (ippiInterpolateLuma_H264 и ippiInterpolateChroma_H264) не учитывают интегральную часть векторов движения. Они выполняют только интерполяцию. Входящий указатель для опорных данных должен всегда указывать на опорный блок со смещением на целое значение. Затем функции рассчитывают интерполированный опорный блок, используя 2 или 3 бита, определяющих неполный вектор движения с точностью до четвертой или восьмой части пикселя.
Среди других функций, функции со словом «Top» или «Bottom» в названии выполняют интерполяцию данных на границе кадра. Параметры функций указывают, насколько удален опорный блок от изображения. Путем репликации пограничного ряда функции создают несуществующие данные за пределами изображения с последующей обычной интерполяцией.
Функция последнего типа со словом «Block» в названии выполняет интерполяцию над всем опорным блоком внутри изображения, но также учитывает весь вектор движения, выполняя расчет смещения. На рисунке 14 показано выполнение таких функций.
Функция SelectPredictionMethod определяет, должен ли алгоритм учитывать версии функций, выполняющих обработку пограничных данных. Остальная часть кода взята из другой, неопределенной функции.
Основная часть кода функции подготавливает все аргументы для функций интерполяции. В переменных «mvx» и «mvy» хранятся полные векторы движения. В этом коде переменные «xh» и «yh» задаются для младших битов вектора движения – дробной части. Затем, после усечения векторов движения до максимального диапазона, в коде задаются переменные «xint» и «yint» для целочисленной части вектора движения. В конце происходит расчет указателя на опорный блок смещения и вызывается соответствующая функция из библиотеки Intel IPP.
Обратите внимание, что репликация пограничных данных применяется к областям вверху и внизу, но не справа и слева. Такая схема обусловлена тем, что репликация верхних и нижних границ происходит на уровне макроблока, а левая и правая границы реплицируются на уровне кадра.
inline
Ipp8s SelectPredictionMethod(Ipp32s MBYoffset,Ipp32s mvy,
Ipp32s sbheight,Ipp32s height)
{
Ipp32s padded_y = (mvy&3)>0?3:0;
mvy>>=2;
if (mvy-padded_y+MBYoffset<0)
{
return PREDICTION_FROM_TOP;
}
if (mvy+padded_y+MBYoffset+sbheight>=height)
{
return PREDICTION_FROM_BOTTOM;
}
return ALLOK;
}
{
...
// set pointers for this subblock
pMV_sb = pMV + (xpos>>2) + (ypos>>2)*4;
mvx = pMV_sb->mvx;
mvy = pMV_sb->mvy;
...
xh = mvx & (INTERP_FACTOR-1);
yh = mvy & (INTERP_FACTOR-1);
Ipp8u pred_method = 0;
if (ABS(mvy) < (13 << INTERP_SHIFT))
{
if (is_need_check_expand)
{
pred_method = SelectPredictionMethod(
mbYOffset+ypos,
mvy,
roi.height,
height);
}
} else {
pred_method = SelectPredictionMethod(
mbYOffset+ypos,
mvy,
roi.height,
height);
mvy = MIN(mvy, (height - ((Ipp32s)mbYOffset + ypos +
roi.height -
1 - D_MV_CLIP_LIMIT))*INTERP_FACTOR);
mvy = MAX(mvy, -((Ipp32s)(mbYOffset + ypos +
D_MV_CLIP_LIMIT)*INTERP_FACTOR));
}
if (ABS(mvx) > (D_MV_CLIP_LIMIT << INTERP_SHIFT))
{
mvx = MIN(mvx, (width - ((Ipp32s)mbXOffset + xpos +
roi.width -
1 - D_MV_CLIP_LIMIT))*INTERP_FACTOR);
mvx = MAX(mvx, -((Ipp32s)(mbXOffset + xpos +
D_MV_CLIP_LIMIT)*INTERP_FACTOR));
}
mvyc = mvy;
xint = mvx >> INTERP_SHIFT;
yint = mvy >> INTERP_SHIFT;
pRef = pRefY_sb + xint + yint * pitch;
switch(pred_method)
{
case ALLOK:
ippiInterpolateLuma_H264_8u_C1R(pRef, pitch,
pTmpY, nTmpPitch,
xh, yh, roi);
break;
case PREDICTION_FROM_TOP:
ippiInterpolateLumaTop_H264_8u_C1R(pRef, pitch,
pTmpY, nTmpPitch,
xh, yh, - ((Ipp32s)mbYOffset+ypos+yint),roi);
break;
case PREDICTION_FROM_BOTTOM:
ippiInterpolateLumaBottom_H264_8u_C1R(pRef, pitch,
pTmpY, nTmpPitch,
xh, yh, ((Ipp32s)mbYOffset+ypos+yint+roi.height)-
height,roi);
break;
default:VM_ASSERT(0);
break;
}
}
Рисунок 14. Структура интерполяции по алгоритму H.264
В библиотеке Intel IPP присутствует три функции для прогнозирования Intra-блоков. Это функция ippiPredictIntra_4x4_H264_8u_C1IR для блоков 4x4, ippiPredictIntra_16x16_H264_8u_C1IR для блоков 16x16 и ippiPredictIntraChroma8x8_H264_8u_C1IR для блоков цветности.
В качестве аргументов этих функций задается указатель на начало блока и значение шага буфера, режим прогнозирования, представленный на рисунке 10, и набор флагов, указывающих на доступные блоки данных до верхней или левой границы. На рисунке 15 представлен код, в котором эти функции используются для выполнения предсказания.
Код включает в себя три пути: 16x16, 8x8 и 4x4. Блоки 16x16 сразу вызывают функцию ippiPredictIntra. Блоки 8x8 вызывают функцию AddResidualAndPredict8x8, а блоки 4x4 – функцию AddResidualAndPredict. Блоки меньшего размера организованы в отдельные функции согласно их относительной сложности. Меньшие блоки имеют много типов общих границ с другими блоками, а также цикл внутри макроблока. Из этих функций представлена только функция для блока 4x4. Вариант функции для блока 8x8 практически идентичен.
Функции прогнозирования используют определенный алгоритм из стандарта для расчета опорного блока на основе предыдущих блоков. Режим определяет направление потока данных, представляющих интерес, после чего алгоритм рассчитывает прогнозирование для каждого пикселя на основе среднего значения одного или более пикселей в этом направлении.
В нашем коде в качестве аргумента принимается уже рассчитанный режим. Итак, основная часть кода служит для определения доступных внешних опорных блоков и расчета положений блоков в памяти. Пограничные блоки доступны, если спрогнозированный блок не находится на такой границе с другим макроблоком, или если переменная «edge_type» не указывает, что макроблок расположен на глобальной границе (границе кадра). После расчета прогнозируемого блока, каждая из двух функций AddResidualAndPredict складывает остаточное значение, используя вариант функции компенсации движения (с ippiMC в начале названия) с точностью до полного пикселя.
void AddResidualAndPredict(Ipp16s ** luma_ac,
Ipp8u * pSrcDstPlane,
Ipp32u step,
Ipp32u cbp4x4,
const IppIntra4x4PredMode_H264 *pMBIntraTypes,
Ipp32s edge_type,
bool is_half,
Ipp32s bit_depth)
{
Ipp32s srcDstStep = step;
Ipp8u * pTmpDst = pSrcDstPlane;
/* bit var to isolate cbp for block being decoded */
Ipp32u uCBPMask = (1 << IPPVC_CBP_1ST_LUMA_AC_BITPOS);
for (Ipp32s uBlock = 0; uBlock < (is_half ? 8 : 16);
uBlock++, uCBPMask <<= 1)
{
pTmpDst = pSrcDstPlane;
Ipp32s left_edge_subblock = left_edge_tab16[uBlock];
Ipp32s top_edge_subblock = top_edge_tab16[uBlock];
Ipp32s top = top_edge_subblock &&
(edge_type & IPPVC_TOP_EDGE);
Ipp32s left = left_edge_subblock &&
(edge_type & IPPVC_LEFT_EDGE);
Ipp32s top_left = ((top || left) && (uBlock != 0)) ||
((edge_type & IPPVC_TOP_LEFT_EDGE) && (uBlock == 0));
Ipp32s top_right = (top && (uBlock != 5)) ||
(!above_right_avail_4x4[uBlock]) ||
((edge_type & IPPVC_TOP_RIGHT_EDGE) && (uBlock == 5));
Ipp32s avail = (left == 0)*IPP_LEFT +
(top_left == 0)*IPP_UPPER_LEFT +
(top_right == 0)*IPP_UPPER_RIGHT +
(top == 0)*IPP_UPPER;
ippiPredictIntra_4x4_H264_8u_C1IR(pTmpDst,
srcDstStep, pMBIntraTypes[uBlock], avail);
if ((cbp4x4 & uCBPMask) != 0)
{
const Ipp8u * pTmp = pSrcDstPlane;
ippiMC4x4_8u_C1(pTmp, srcDstStep, *luma_ac, 8,
pSrcDstPlane, srcDstStep, IPPVC_MC_APX_FF, 0);
*luma_ac += 16;
}
pSrcDstPlane +=
xyoff[uBlock][0] + xyoff[uBlock][1]*srcDstStep;
}
}
{
...
Ipp32s availability =
((edge_type & IPPVC_LEFT_EDGE) == 0)*IPP_LEFT +
((edge_type & IPPVC_TOP_LEFT_EDGE) == 0)*IPP_UPPER_LEFT +
((edge_type & IPPVC_TOP_RIGHT_EDGE) == 0)*IPP_UPPER_RIGHT +
((edge_type & IPPVC_TOP_EDGE) == 0)*IPP_UPPER;
if (mbtype == MBTYPE_INTRA_16x16)
{
ippiPredictIntra_16x16(
context->pYPlane + offsetY,
rec_pitch_luma,
(IppIntra16x16PredMode_H264) pMBIntraTypes[0],
availability);
if (luma_ac)
AddResidual(luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma,
sd->bit_depth_luma);
}
else // if (intra16x16)
{
if (is_high_profile)
{
switch (special_MBAFF_case)
{
default:
if (pGetMB8x8TSFlag(sd->m_cur_mb.GlobalMacroblockInfo))
{
AddResidualAndPredict_8x8(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp,
(IppIntra8x8PredMode_H264 *) pMBIntraTypes,
edge_type_2t,
true,
sd->bit_depth_luma);
AddResidualAndPredict_8x8(
&luma_ac,
context->pYPlane + offsetY + 8*rec_pitch_luma,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp >> 2,
(IppIntra8x8PredMode_H264 *) pMBIntraTypes + 2,
edge_type_2b,
true,
sd->bit_depth_luma);
}
else
{
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes,
edge_type_2t,
true,
sd->bit_depth_luma);
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY + 8*rec_pitch_luma,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma >> 8,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes + 8,
edge_type_2b,
true,
sd->bit_depth_luma);
}
break;
case 0:
if (pGetMB8x8TSFlag(sd->m_cur_mb.GlobalMacroblockInfo))
{
AddResidualAndPredict_8x8(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp,
(IppIntra8x8PredMode_H264 *) pMBIntraTypes,
edge_type,
false,
sd->bit_depth_luma);
}
else
{
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes,
edge_type,
false,
sd->bit_depth_luma);
}
break;
}
}
else
{
switch (special_MBAFF_case)
{
default:
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes,
edge_type_2t,
true,
sd->bit_depth_luma);
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY + 8*rec_pitch_luma,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma >> 8,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes + 8,
edge_type_2b,
true,
sd->bit_depth_luma);
break;
case 0:
AddResidualAndPredict(
&luma_ac,
context->pYPlane + offsetY,
rec_pitch_luma,
sd->m_cur_mb.LocalMacroblockInfo->cbp4x4_luma,
(IppIntra4x4PredMode_H264 *) pMBIntraTypes,
edge_type,
false,
sd->bit_depth_luma);
break;
}
}
...
}
Рисунок 15. Прогнозирование Intra-кадров по алгоритму H.264
В функциях Intel IPP возможности преобразования и квантования совмещены для большей эффективности. Для декодирования потока H.264 существует четыре функции:
ippiTransformDequantLumaDC_H264_16s_C1I ippiTransformDequantChromaDC_H264_16s_C1I ippiDequantTransformResidual_H264_16s_C1I ippiDequantTransformResidualAndAdd_H264_16s_C1I There are analogous functions for encoding: ippiTransformQuantLumaDC_H264_16s_C1I ippiTransformQuantChromaDC_H264_16s_C1I ippiTransformQuantResidual_H264_16s_C1I
Дополнительные функции обрабатывают блоки 8x8.
На рисунке 16 представлен блок кода по алгоритму H.264 с использованием этих функций.
Переменная cbp4x4 является битовой маской, указывающей на наличие в макроблоке коэффициентов DC с данными, и отдельно, на наличие каких-либо данных в остаточном блоке (AC) внутри макроблока. В переменной QP указывается параметр качества, определяющий степень квантования.
Если битовая маска указывает на наличие любых данных DC, описывающих яркость, то происходит преобразование этих данных при помощи функции ippiTransformDequantLumaDC. Затем код повторяется для 16 блоков внутри макроблока. Для каждого блока, при наличии данных DC или остаточных данных, выполняется преобразование и деквантование блока. Происходит подстановка декодированного коэффициента DC, который может быть равен 0, буфера остаточных данных вместе с флагом, указывающим на допустимость остаточных данных, и параметра качества.
if ((cbp4x4 & (IPPVC_CBP_LUMA_AC | IPPVC_CBP_LUMA_DC)) != 0)
{
Ipp16s *pDC;
Ipp16s DCCoeff;
Ipp16s *tmpbuf;
/* bit var to isolate cbp for block being decoded */
Ipp32u uCBPMask = (1 << IPPVC_CBP_1ST_LUMA_AC_BITPOS);
if ((cbp4x4 & IPPVC_CBP_LUMA_DC) != 0)
{
luma_dc = (*ppSrcCoeff);
*ppSrcCoeff += 16;
ippiTransformDequantLumaDC_H264_16s_C1I(luma_dc, QP);
}
tmpbuf = 0; /* init as no ac coeffs */
pDC = 0; /* init as no dc */
ac_coeffs = pDstCoeff;
for (Ipp32s uBlock = 0; uBlock < 16;
uBlock++, uCBPMask <<= 1)
{
DCCoeff = (Ipp16s)luma_dc[block_subblock_mapping[uBlock]];
if (DCCoeff != 0)
pDC = &DCCoeff; /* dc coeff presents */
if ((cbp4x4 & uCBPMask) != 0)
{
memcpy(pDstCoeff, *ppSrcCoeff, 16*sizeof(Ipp16s));
tmpbuf = pDstCoeff;
pDstCoeff += 16;
*ppSrcCoeff += 16;
}
Ipp32s hasAC = tmpbuf != 0;
if (tmpbuf || pDC)
{
if (!pDC)
{
if (tmpbuf)
{
if (dc_present)
tmpbuf[0] = 0;
}
}
else
{
if (!tmpbuf)
{
tmpbuf = pDstCoeff;
pDstCoeff += 16;
cbp4x4 |= uCBPMask;
}
}
ippiDequantTransformResidual_H264_16s_C1I(tmpbuf, 8, pDC,
hasAC, QP);
tmpbuf = 0;
pDC = 0;
}
}
}
Рисунок 16. Преобразование и квантование по алгоритму H.264
Функции библиотеки Intel IPP, выполняющие фильтрование по границам макроблоков разграничиваются по горизонтальным и вертикальным границам, блокам яркости и цветности, размеру блоков, глубине цвета и частоте дискретизации. В число этих функций входят следующие:
ippiFilterDeblockingLuma_VerEdge_H264_[8u|16u]_C1IR ippiFilterDeblockingLuma_HorEdge_H264_[8u|16u]_C1IR ippiFilterDeblockingChroma_HorEdge[422|444]_H264_[8u|16u]_C1IR ippiFilterDeblockingChroma_VerEdge[422|444]_H264_[8u|16u]_C1IR ippiFilterDeblockingLuma_VerEdge_MBAFF_H264_[8u|16u]_C1IR ippiFilterDeblockingChroma_VerEdge_MBAFF_H264_[8u|16u]_C1IR
Версии функций MBAFF выполняют фильтрование блоков 16x8 вместо 16x16, и предназначены для использования с чередующимися видеоизображениями.
Незначительно отличающиеся варианты некоторых из этих функций принимают структуру параметров, а не помещают все параметры в стек. Таким образом, достигается небольшой прирост производительности, благодаря снижению числа обращений к стеку.
На рисунке 17 представлен отрывок кода, в котором выполняется фильтр сглаживания блоков. Поведение фильтров определяется альфа- и бета-порогами и порогами ограничения, а также интенсивностью фильтра. Альфа-параметр представляет собой порог для перехода между границами, в то время как бета-параметр является порогом для перехода на одной стороне границы. Пороги ограничения в массиве Clipping под названием tc0 в стандарте ограничивают действие фильтра. Параметры порога основаны на фиксированных таблицах с индексированием по параметру качества (QP) с коэффициентом настройки. Параметр интенсивности pStrength, обозначаемый в стандарте [JVTG050] как bS, влияет на фильтр сглаживания блоков различными способами, так же как и на основной алгоритм. И таблицы, и формулы, используемые при расчете индексов, берутся из стандарта H.264.
Для упрощения примера указанный код использует простые функции надстройки для каждой функции Intel IPP. Надстройки подстраивают аргументы и обеспечивают единый прототип для всех фильтров разделения блоков, но никаких вычислений не осуществляют. Поскольку для функций существует единый прототип, они вызываются напрямую по таблице из другого источника.
Ipp8u BETA_TABLE[52] =
{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 3, 3, 3, 3, 4,
4, 4, 6, 6, 7, 7, 8, 8,
9, 9, 10, 10, 11, 11, 12, 12,
13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18
};
...
{
...
IppStatus ( *(IppDeblocking[])) (Ipp8u *, Ipp32s, Ipp8u *,
Ipp8u *, Ipp8u *, Ipp8u *, Ipp32s ) =
{
&(FilterDeblockingLuma_VerEdge),
&(FilterDeblockingLuma_HorEdge),
&(FilterDeblockingChroma_VerEdge),
&(FilterDeblockingChroma_HorEdge),
&(FilterDeblockingChroma422_VerEdge),
&(FilterDeblockingChroma422_HorEdge),
&(FilterDeblockingChroma444_VerEdge),
&(FilterDeblockingChroma444_HorEdge),
&(FilterDeblockingLuma_VerEdge_MBAFF),
&(FilterDeblockingChroma_VerEdge_MBAFF)
};
IppStatus ( *(IppDeblocking16u[])) (Ipp16u *, Ipp32s, Ipp8u *,
Ipp8u *, Ipp8u *, Ipp8u *, Ipp32s ) =
{
&(FilterDeblockingLuma_VerEdge),
&(FilterDeblockingLuma_HorEdge),
&(FilterDeblockingChroma_VerEdge),
&(FilterDeblockingChroma_HorEdge),
&(FilterDeblockingChroma422_VerEdge),
&(FilterDeblockingChroma422_HorEdge),
&(FilterDeblockingChroma444_VerEdge),
&(FilterDeblockingChroma444_HorEdge),
&(FilterDeblockingLuma_VerEdge_MBAFF),
&(FilterDeblockingChroma_VerEdge_MBAFF)
};
// internal edge variables
QP = pmq_QP;
index = IClip(0, 51, QP + BetaOffset);
Beta[1] = (Ipp8u) (BETA_TABLE[index]);
index = IClip(0, 51, QP + AlphaC0Offset);
Alpha[1] = (Ipp8u) (ALPHA_TABLE[index]);
pClipTab = CLIP_TAB[index];
// create clipping values
{
Ipp32s edge;
for (edge = 1;edge < 4;edge += 1)
{
if (*((Ipp32u *) (pStrength + edge * 4)))
{
// create clipping values
Clipping[edge * 4 + 0] =
(Ipp8u) (pClipTab[pStrength[edge * 4 + 0]]);
Clipping[edge * 4 + 1] =
(Ipp8u) (pClipTab[pStrength[edge * 4 + 1]]);
Clipping[edge * 4 + 2] =
(Ipp8u) (pClipTab[pStrength[edge * 4 + 2]]);
Clipping[edge * 4 + 3] =
(Ipp8u) (pClipTab[pStrength[edge * 4 + 3]]);
}
}
}
if (pParams->bitDepthLuma > 8)
{
IppDeblocking16u[dir]((Ipp16u*)pY,
pic_pitch,
Alpha,
Beta,
Clipping,
pStrength,
pParams->bitDepthLuma);
}
else
{
IppDeblocking[dir](pY,
pic_pitch,
Alpha,
Beta,
Clipping,
pStrength,
pParams->bitDepthLuma);
}
}
Рисунок 17. Фильтры сглаживания блоков по алгоритму H.264
В целом, алгоритмы H.264 и MPEG-4 поддаются многопоточной обработке. На рисунке 18 представлен ключевой отрывок кода из модели кодека библиотеки Intel IPP для алгоритма H.264, в котором используется одна прагма OpenMP для распараллеливания этого кодера.
Ключевой частью кода является срез. Срез определяется как независимый сегмент изображения, который не использует другие видеосрезы в качестве опорных при прогнозировании, как и не служит опорным для других видео срезов. Благодаря этому он хорошо подходит для распараллеливания, поскольку кодек может обрабатывать несколько срезов одновременно и не переключаться принудительно в последовательный режим компенсацией движения.
templateStatus H264CoreEncoder ::CompressFrame( EnumPicCodType & ePictureType, EnumPicClass & ePic_Class, MediaData* dst) { Status status = UMC_OK; Ipp32s slice; for (m_field_index=0; m_field_index <= (Ipp8u) (m_pCurrentFrame->m_PictureStructureForDec< FRM_STRUCTURE); m_field_index++) { ... #if defined _OPENMP vm_thread_priority mainTreadPriority = vm_get_current_thread_priority(); #pragma omp parallel for private(slice) #endif // _OPENMP for (slice = (Ipp32s)m_info.num_slices*m_field_index; slice < m_info.num_slices*(m_field_index+1); slice++) { #if defined _OPENMP vm_set_current_thread_priority(mainTreadPriority); #endif // _OPENMP UpdateRefPicList(m_Slices + slice, m_pCurrentFrame->GetRefPicLists(slice), m_SliceHeader, &m_ReorderInfoL0, &m_ReorderInfoL1); // Compress one slice if (m_is_cur_pic_afrm) m_Slices[slice].status = Compress_Slice_MBAFF(m_Slices + slice); else{ m_Slices[slice].status = Compress_Slice(m_Slices + slice, slice == m_info.num_slices*m_field_index); } ... }
Рисунок 18. Многопоточная обработка в кодере H.264
Два видеокодека, описанные в данной статье, лишь частично демонстрируют расширенную поддержку кодеков в библиотеке Intel IPP. Помимо MPEG-2 и H.264, корпорацией Intel построены модели на основе библиотеки Intel IPP для алгоритмов MPEG-1, MPEG-4, DV, H.261 и H.263. Эти модели призваны продемонстрировать работу со специальными функциями библиотеки Intel IPP для соответствующих кодеков, а также с более общими функциями кодирования видеосигнала и арифметических операций.
Кроме того, библиотека Intel IPP включает межплатформенную структуру для декодирования микшированного сигнала и воспроизведения аудио- и видеосигналов. Данная структура образует общий интерфейс, который обеспечивает взаимозаменяемость кодеков, делителей и рендереров, и практически все модели кодеков разрабатываются с учетом этой структуры. Интерфейс структуры описан в руководстве UMC (Intel 2007), которое является более полным документом, чем данная ознакомительная статья.
Стюарт Тейлор, архитектор ПО, работает в корпорации Intel. Ранее он возглавлял разработку функций Intel IPP.
Статья подготовлена на основе материалов «Оптимизация приложений для многоядерных процессоров», автор Стюарт Тейлор (Stewart Taylor). © 2007 Корпорация Intel. Все права защищены. Дополнительную информацию вы можете найти на сайте:
http://www.intel.com/intelpress/sum_ipp2.htm
Никакая часть настоящего документа не может быть воспроизведена, сохранена в системе хранения и извлечения данных, либо передана в любой форме или любым способом (электронным, механическим, методом фотокопирования, записи, сканирования или иным способом) для любых целей, кроме предусмотренных Разделами 107 или 108 Закона Соединенных Штатов Америки об авторских правах от 1976 года, без предварительного письменного разрешения Издателя или уплаты соответствующего взноса за каждую копию в Центр по проверке авторских прав, расположенный по адресу: 222 Rosewood Drive, Danvers, MA 01923, телефон (978) 750-8400, факс (978) 750-4744. Заявки на получение разрешения Издателя направляйте на адрес Издателя: Intel Press, Intel Corporation, 2111 NE 25 Avenue, JF3-330, Hillsboro, OR 97124-5961. Адрес электронной почты: intelpress@intel.com
