рефераты

рефераты

 
 
рефераты рефераты

Меню

Реферат: Алгоритм сжатия видео 'pixel behaviour check' рефераты

cpBehaviour: Byte; // индекс текущего поведения, выполняемого из набора

cpCode: Byte; // код выполняемого поведения

cpRepeat: Word; // количество кадров повторения поведения

cpCurrent: Double; // текущее значение цветовой плоскости пикселя

cpExtent: Double; // величина изменения плоскости за один кадр

end;

// глобальные переменные

var

Frame: array of ColorPlane; // опорный кадр

Отмечу, что структура ColorPlane в таком виде не позволит использовать кодирование наборов поведений через другие наборы, когда один набор на каком-то своем участке может ссылаться на содержимое другого набора. В наборах поведений допустимо использовать поведения с кодом 11 (закодирован в массиве поведений), а значит, можно реализовать высокую вложенность наборов поведений. Это может помочь сильнее сжимать видео. Приведу пример, где зацикленные друг на друга наборы приводят к эффективному сжатию.

Допустим, у нас есть видеофрагмент в 1000 кадров. Пусть он состоит из одной точки. В каждом нечетном кадре точка имеет черный цвет, а в каждом четном - белый. И вот так 1000 кадров она постоянно "моргает". В закодированном потоке окажется всего два элемента (набора поведений) в массиве поведений, причем каждый набор поведений будет состоять всего из двух поведений, где второе поведение всегда будет ссылаться на другой набор поведений (второе поведение первого набора - на второй набор, второе поведение второго набора - на первый набор). И будет еще одно поведение в общем видеопотоке (поток после массива поведений), которое ссылается на один из наборов поведений. Декодер, считав поведение из общего видеопотока, по ссылке попадает на один из наборов и начинает обслуживать его поведения. В первом поведении набора указано, что точка имеет белый цвет один кадр, а второе поведение набора отсылает декодер на второй набор. Во втором наборе первое поведение указывает, что точка имеет черный цвет один кадр, а второе поведение отсылает декодер на первый набор. И так в цикле декодер отрисовывает 1000 кадров видеофрагмента, больше ничего не читая из видеопотока.

Пример не совсем удачный, хотя объясняет суть организации вложенности наборов поведений. Но ведь вложенность может быть и с возвратом, когда один набор ссылается на другой набор, но за своей ссылкой содержит еще продолжение поведений (ссылка внутри набора, или несколько ссылок в наборе). Вот это и не позволит использовать указанное содержимое структуры ColorPlane, так как в ней не предусмотрен контроль вложенности наборов. А теперь возвратимся к работе декодера, и позже объясню, почему поля cpCurrent и cpExtent в структуре ColorPlane заданы типами с плавающей точкой.

Первым делом декодер читает заголовок видеопотока. Получив ширину и высоту кадра видеофильма (поля Width и Height структуры PBCvideoHeader), декодер устанавливает размер массива опорного кадра по формуле Width * Height * 3. Значения полей StartRCr, StartGY и StartBCb заносятся в поля cpCurrent соответствующих элементов массива опорного кадра. Декодеру нужно сразу заполнить цветовые плоскости опорного кадра соответствующими начальными значениями. Первые три элемента массива принадлежат первому пикселю (его координаты = 0,0), следующие три элемента - второму пикселю (0,1), следующие - третьему, и так далее. Поэтому в поле cpCurrent первого элемента массива заносится значение поля StartRCr, во второй элемент массива - значение поля StartGY, в третий элемент - значение поля StartBCb, в четвертый элемент - снова поле StartRCr, в пятый - поле StartGY, и так далее. Все остальные поля элементов массива обнуляются. Замечу, что поле cpIndex обнуляется значением 0FFFFh. Декодер использует это поле, чтобы выяснить, обслуживается ли в настоящий момент для данной цветовой плоскости пикселя поведение из видеопотока или оно берется из массива поведений. Индекс набора поведений может лежать только в пределах от 0 до 8191 (всего получается 8192 набора поведений), а значение 0FFFFh находится за пределами массива, поэтому декодер легко определяет, что текущее поведение взято не из массива поведений, а прямо из видеопотока. Для наглядности приведу фрагмент программы.

// глобальные переменные

var

Header: PBCvideoHeader; // заголовок видеопотока

Behaviours: PBCvideoBehaviours; // массив поведений

Frame: array of ColorPlane; // опорный кадр

FrameNum: DWord; // номер текущего кадра

procedure InitFrame;

var

W: Word; // ширина

H: Word; // высота

I: DWord; // индекс плоскости в массиве опорного кадра

begin

// читаем заголовок видеопотока из некоторого файла

BlockRead(F1, Header, SizeOf(Header));

// устанавливаем размер массива опорного кадра

I := Header.Width * Header.Height * 3;

SetLength(Frame, I);

// сначала обнулим остальные поля элементов массива опорного кадра

// (поле cpIndex обнуляем значением 0FFFFh)

repeat

Dec(I);

Frame[I].cpIndex := $FFFF;

Frame[I].cpBehaviour := 0;

Frame[I].cpCode := 0;

Frame[I].cpRepeat := 0;

Frame[I].cpExtent := 0;

until I = 0;

// теперь занесем значения полей StartRCr, StartGY и StartBCb

// в соответствующие цветовые плоскости опорного кадра

for H := 1 to Header.Height do begin

for W := 1 to Header.Width do begin

// вычисляем в переменной I индекс элемента массива

// опорного кадра, с которого расположены подряд

// три цветовых плоскости пикселя с координатами W и H

// (где W = X, H = Y)

I := (H-1) * Header.Width * 3 + (W-1) * 3;

// а теперь заносим в три плоскости пикселя

// начальные цветовые значения

Frame[I].cpCurrent := Header.StartRCr;

Frame[I+1].cpCurrent := Header.StartGY;

Frame[I+2].cpCurrent := Header.StartBCb;

end;

end;

// читаем массив поведений из файла в переменную Behaviours

ReadBehavioursData(F1, Behaviours);

// сбрасываем внутренний счетчик декодированных кадров,

// а по нему будем определять, что видеопоток закончился

FrameNum := 0;

end;

За заголовком декодер должен прочитать из видеопотока массив поведений. Это обычный массив и декодер должен "сложить" его где-нибудь у себя в памяти, чтобы иметь к нему быстрый доступ в случае кодирования поведения цветовой плоскости пикселя набором поведений из массива. В конце приведенного фрагмента кода указан вызов якобы уже написанной процедуры ReadBehavioursData, которая загружает массив поведений из файла в переменную с именем Behaviours. Эта переменная содержит в себе количество элементов в массиве (поле ItemCount) и массив Behaviours с элементами, представляющими собой наборы поведений. У каждого набора есть поле Count (количество поведений в наборе) и поле Behaviour с набором этих поведений. Отмечу, что в наборе поведений всегда есть хотя бы одно поведение, поэтому поле Count со значение 0 обозначает одно поведение, со значением 1 - два поведения, и так далее до 255, что обозначает 256 поведений.

За массивом поведений следует набор сведений о поведении конкретных цветовых плоскостей пикселей изображения - общий видеопоток. Декодер извлекает эти сведения, опираясь на данные элементов массива опорного кадра. Если в некотором элементе массива поле cpIndex равно 0FFFFh (поведение цветовой плоскости пикселя было взято прямо из видеопотока) и поле cpRepeat равно нулю (повторы поведения закончились), значит, сейчас в видеопотоке находятся данные о следующем поведении текущей цветовой плоскости пикселя, и эти данные нужно извлечь и занести в текущий элемент массива. Если же это не так, и поле cpRepeat не равно нулю (еще остались повторы поведения), тогда нужно в текущем элементе массива уменьшить на 1 поле cpRepeat и, в зависимости от поля cpCode (00, 01 или 10), либо оставить без изменения, либо увеличить или уменьшить поле cpCurrent на величину поля cpExtent.

Образно говоря, декодирование каждого кадра начинается с того, что декодер проходит последовательно все элементы массива опорного кадра - от первого до последнего элемента (фактически проходит по всем цветовым плоскостям пикселей видеокадра). Если в поле cpRepeat остались повторы, тогда уменьшить поле cpRepeat на 1, а к полю cpCurrent прибавить или отнять (в зависимости от значения поля cpCode) значение поля cpExtent. Новое значение поля cpCurrent и будет тем, что заносится в соответствующую цветовую плоскость пикселя реального кадра на экране. Но перед этим значение поля должно быть еще обработано, о чем будет рассказано ниже.

// глобальные переменные

var

Header: PBCvideoHeader; // заголовок видеопотока

Behaviours: PBCvideoBehaviours; // массив поведений

Frame: array of ColorPlane; // опорный кадр

procedure CreateNextFrame;

var

I: DWord; // индекс плоскости в массиве опорного кадра

L: DWord; // количество элементов в массиве опорного кадра

C: Byte; // код только что прочитанного поведения

R: Word; // количество повторов поведения

E: Byte; // величина изменения плоскости за весь период поведения

begin

L := Length(Frame);

I := 0;

while I < L do begin

// если еще остались повторы для текущей цветовой плоскости,

// тогда вычислить значение цветовой плоскости для

// текущего кадра

if Frame[I].cpRepeat <> 0 then begin

Dec(Frame[I].cpRepeat);

// при поведении 00 цветовая плоскость не изменяется,

// поэтому в структуре CASE не используется проверка на 00,

// а код 11 всегда заменяется реальным поведением из набора поведений,

// поэтому его проверка тоже не нужна

case Frame[I].cpCode of

01: Frame[I].cpCurrent := Frame[I].cpCurrent + Frame[I].cpExtent;

10: Frame[I].cpCurrent := Frame[I].cpCurrent - Frame[I].cpExtent;

end;

end else begin

// этот фрагмент смотрите ниже, где рассматривается, что происходит,

// если в поле cpRepeat не осталось повторов

end;

Inc(I);

end;

end;

Когда же в поле cpRepeat не осталось повторов, декодер должен проанализировать поле cpIndex. Если там не указан индекс обслуживаемого набора поведений цветовой плоскости пикселя (cpIndex = 0FFFFh), тогда нужно взять следующее поведение прямо из видеопотока и занести его данные в текущий элемент массива опорного кадра. Как уже было сказано ранее, декодер должен уметь обслуживать специальный код внедрения других данных в видеопоток, и продолжать декодирование видео после внедренного блока данных. Заметьте, что в элементах опорного кадра хранятся коды реальных поведений, чтобы знать, что в действительности происходит с цветовой плоскостью пикселя. Поэтому при встрече кода "закодирован в массиве поведений" (cpCode = 11), декодер должен извлечь первое поведение из указанного набора и использовать код этого поведения. Описанный мной фрагмент кода поддерживает безвозвратную вложенность наборов поведений, но с возвратной не справится.

Итак, допишем недостающий фрагмент процедуры CreateNextFrame. Для простоты считаем, что у нас уже написана процедура ReadBehaviour, читающая из потока одно поведение по описанным выше правилам (смотрите описание содержимого структур PBCvideoIdentical, PBCvideoChanged и PBCvideoEncoded). Также считаем, что у нас уже есть процедура чтения N-го поведения из указанного набора в массиве поведений - GetBehaviour.

if Frame[I].cpIndex = $FFFF then begin

// читаем из видеопотока поведение для текущей

// цветовой плоскости пикселя

// в переменную C возвращается код поведения

// в R - количество повторов поведения или индекс,

// если поведение задано в массиве поведений

// (в этом случае переменная C = 11)

// в E - величина изменения плоскости за весь период поведения

// или 0, если C = 00 или C = 11

ReadBehaviour(C, R, E);

// с помощью цикла делаем обслуживание возможных

// внедренных в видеопоток блоков данных

while (C = 00) and (R = 8192) do begin

// здесь должен быть вызов вашей функции обработки

// внедренного в видеопоток блока данных

// а затем продолжаем читать из потока поведение

// для текущей цветовой плоскости пикселя

ReadBehaviour(C, R, E);

end;

// Если поведение закодировано в массиве набором поведений,

// тогда необходимо прочитать первое поведение из набора

// и использовать его данные, так как в опорном кадре

// используются только коды реальных поведений.

// Следующим циклом обеспечивается выбор первого поведения

// при кодировании с помощью набора, а также он обеспечивает

// поддержку безвозратной вложенности наборов поведений.

while C = 11 do begin

Frame[I].cpIndex := R;

Frame[I].cpBehaviour := 0;

// читаем поведение с индексом Frame[I].cpBehaviour = 0

// из набора поведений с индексом Frame[I].cpIndex,

// а результат чтения возвращается в переменные C, R и E

GetBehaviour(Frame[I].cpIndex, Frame[I].cpBehaviour, C, R, E);

end;

// в поле cpExtent заносится величина изменения цветовой

// плоскости за один кадр повтора, а не за весь его период

Frame[I].cpCode := C;

Frame[I].cpRepeat := R;

Frame[I].cpExtent := E / R;

// а теперь нам осталось вычислить поле cpCurrent для прочитанного

// поведения

Dec(Frame[I].cpRepeat);

case Frame[I].cpCode of

01: Frame[I].cpCurrent := Frame[I].cpCurrent + Frame[I].cpExtent;

10: Frame[I].cpCurrent := Frame[I].cpCurrent - Frame[I].cpExtent;

end;

end else begin

// этот фрагмент приведен ниже и описывает, что происходит,

// когда поле cpIndex содержит индекс обслуживаемого набора поведений

end;

Теперь пришло время объяснить, почему поля cpCurrent и cpExtent заданы типами с плавающей точкой. Дело в том, что в поле cpExtent элемента массива опорного кадра хранится величина изменения цветовой плоскости за один кадр повтора, а не за весь его период. Когда величина изменения за весь период делится на количество повторов, остаются дробные части. Отбрасывать дробные части нельзя, потому что динамика изменения цветовой плоскости может быть разная. Из кадра в кадр поле cpCurrent изменяется на величину поля cpExtent. Представьте, что за 100 кадров цветовая плоскость пикселя изменилась всего на 1 процент. Разделив 1 на 100 кадров и округлив результат, мы получим 0. В результате точка на протяжении 100 кадров не изменится на 1 процент, ведь мы будем добавлять к полю cpCurrent нулевое значение поля cpExtent. А вот если мы будем добавлять с дробными частями, точка за 100 кадров плавно достигнет изменения в 1 процент. Округление до целого числа выполняется только в момент вывода цветовых плоскостей пикселя в реальный кадр на экране, но внутри опорного кадра все вычисления делаются исключительно с дробными частями.

И снова возвратимся к программному коду декодера. Нам осталось рассмотреть часть кода, когда в поле cpRepeat не осталось повторов, а в поле cpIndex указан индекс обслуживаемого набора поведений цветовой плоскости. В этом случае нужно взять следующее поведение из обслуживаемого набора и занести его данные в текущий элемент массива опорного кадра. Если же в наборе пройдены все поведения (выполненное поведение было последним в наборе), тогда нужно взять следующее поведение прямо из видеопотока и уже его данные занести в текущий элемент массива опорного кадра. Еще раз вспомним, что при чтении из видеопотока декодер должен уметь обслуживать специальный код внедрения других данных в видеопоток.

// вычисляем индекс следующего поведения в наборе

Inc(Frame[I].cpBehaviour);

// если в наборе еще не закончились поведения, тогда

// читаем следующее поведение из набора, иначе

// сбрасываем cpIndex в 0FFFFh и читаем поведение из общего видеопотока

if (Frame[I].cpBehaviour <> 0) and 

(Frame[I].cpBehaviour <= Behaviours.Behaviours[Frame[I].cpIndex].Count) then begin

// читаем следующее поведение набора (индекс следующего

// поведения уже находится в Frame[I].cpBehaviour),

// а результат чтения возвращается в переменные C, R и E

GetBehaviour(Frame[I].cpIndex, Frame[I].cpBehaviour, C, R, E);

// следующим циклом обеспечиваем поддержку безвозратной вложенности

// наборов поведений

while C = 11 do begin

Frame[I].cpIndex := R;

Frame[I].cpBehaviour := 0;

// читаем поведение с индексом Frame[I].cpBehaviour = 0

// из набора поведений с индексом Frame[I].cpIndex,

// а результат чтения возвращается в переменные C, R и E

GetBehaviour(Frame[I].cpIndex, Frame[I].cpBehaviour, C, R, E);

end;

// Заносим данные поведения, прочитанного из набора, в текущий элемент

// массива опорного кадра. В поле cpExtent заносится величина

// изменения цветовой плоскости за один кадр повтора,

// а не за весь его период

Frame[I].cpCode := C;

Frame[I].cpRepeat := R;

Frame[I].cpExtent := E / R;

// а теперь нам осталось вычислить поле cpCurrent для прочитанного

// поведения

Dec(Frame[I].cpRepeat);

case Frame[I].cpCode of

01: Frame[I].cpCurrent := Frame[I].cpCurrent + Frame[I].cpExtent;

10: Frame[I].cpCurrent := Frame[I].cpCurrent - Frame[I].cpExtent;

end;

end else begin

// сбрасываем cpIndex в 0FFFFh, так как мы уже читаем поведение

// из видеопотока, а не из массива поведений

Frame[I].cpIndex := $FFFF;

Frame[I].cpBehaviour := 0;

// А теперь читаем поведение из видеопотока. Следующий фрагмент

// кода один в один похож на участок рассмотренного выше кода,

// поэтому здесь я его не комментирую

ReadBehaviour(C, R, E);

while (C = 00) and (R = 8192) do begin

// здесь обрабатывается вложенный блок данных

ReadBehaviour(C, R, E);

end;

while C = 11 do begin

Frame[I].cpIndex := R;

Frame[I].cpBehaviour := 0;

GetBehaviour(Frame[I].cpIndex, Frame[I].cpBehaviour, C, R, E);

end;

Frame[I].cpCode := C;

Frame[I].cpRepeat := R;

Frame[I].cpExtent := E / R;

Dec(Frame[I].cpRepeat);

case Frame[I].cpCode of

01: Frame[I].cpCurrent := Frame[I].cpCurrent + Frame[I].cpExtent;

Страницы: 1, 2, 3