Pull to refresh

Работа с видео в Matlab

Reading time 9 min
Views 21K
Matlab — известная среда для численных вычислений, широко используемая научным сообществом. Работает в Windows, nix-системых и на маках. Язык матлаба оптимизирован для работы с матрицами и многомерными массивами, помимо этого существует огромное количество расширений (официально они называются Toolboxes) для решения задач оптимизации, статистических расчетов, обработки сигналов и изображений и т.д. Плюс ко всему существует большое количество научного кода уже написанного на Matlab, что дополнительно подстегивает его популярность.

В данной статье я вкратце опишу возможности Matlab для работы с видео. Для тех, кто не знаком с синтасисом матлаба, краткий обзор основных возможностей на русском можно почитать здесь.


Формат видео


Первое, что нужно сделать для работы с видео — это загрузить его в Matlab. Основная сложность которая возникла у меня связана с кодеками. По умолчанию список поддерживаемых видео форматов невелик (английская документация) и откроет ли матлаб ваше видео зависит от установленных в системе кодеков. Общее правило такое — если ваш встроенный в систему проигрыватель(в Windows это Windows Media Player) не сможет открыть файл, то и Matlab скорее всего его не откроет.

Соответственно и возможности записи видео сильно зависят от установленных кодеков, хотя несжатое видео Matlab сможет создавать на любой системе без установленных кодеков.

Проблемы с чтением и записью видео я решил установив KLite Codec Pack Full в варианте «Lots of stuff»:


Различные способы чтения видео


В матлабе есть несколько способов чтения видео. О двух из них рассказано ниже.

Чтение видео с помощью методов mmreader и read


Первый способ чтения видео — это использование метода mmreader для открытия файла и в дальнешем использования метода read для чтения кадров:

% открываем файл
video = mmreader('d:\test\1.avi');

% вот так можно узнать о некоторых свойствах открытого видео
width = video.Width;
height = video.Height;
frameRate = video.FrameRate;
numOfFrames = video.NumberOfFrames;

% читаем кадр
% кадры нумеруются с 1
frameNo = 50;
frame = read(video, frameNo);

% кадр можно прочитать и вот так
frame = video.read(frameNo);

% читаем диапазон кадров
% 50 - номер первого прочитанного кадра
% 100 - номер последнего прочитанного кадра
framesRange = [50 100];
frames = read(video, framesRange);

% читаем все видео
% операция не будет выполнена, в случае, если видео большое и не влезает в память
entireVideo = read(video);

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


Все функции чтения кадров возвращают в качестве результата выполнения многомерную матрицу. Для примера вот результат выполения нескольких команд работы с видео в интерактивной консоли матлаба:

>> video.Width

ans =

   704

>> video.Height

ans =

   576

>> frame = read(video, 5);
>> size(frame)

ans =

   576   704     3

>> frames = read(video, [5 10]);
>> size(frames)

ans =

   576   704     3     6


Таким образом в результате чтения одного кадра получается матрица размером Height x Width x 3, элементами которой являются 8-ми битовые целые числа без знака. В результате чтения диапазона из нескольких кадров получается матрица размером Height x Width x 3 x NumOfFrames.

Соответсвенно, когда вы загрузили кадр, то с ним можно работать как с обычной матлабовской матрицей, а так же применять к нему всевозможные функции для обработки изображений входящие в Image Processing Toolbox

Чтение видео с помощью System Objects — нововведения Matlab 2010a


В матлабе версии 2010a было введено понятие системных объектов. В руководстве матлаба говорится, что системные объекты предоставляют объекто-ориентированную реализацию алгоритмов. По сравнению с обычными матлабовскими функциями они автоматичести управляют своим состоянием и хорошо подходят для потоковой обработки информации. В общем основная суть в том, что по утверждениям разработчиком их удобнее использовать для потоковой обработки информации. На счет удобства можно поспорить, но код становится чуточку короче.

Вот пример покадрового чтения видео с помощью системных объектов:

% создаем объект для чтения видео
reader = video.MultimediaFileReader('Filename', 'd:\test\1.avi');

% покадрово читаем все видео
% функция step будет возвращать матрицу размером Height x Width x  3
% правда элементы этой матрицы будут уже типа single - 4рех байтные числа с плавающей точкой
while ~isDone(reader)
   frame = step(reader);
end

% здесь как и в предыдущем случае не нужно выполнять никаких операций закрытия
% по окончанию чтения видео


Существенный недостаток такого подхода в том, что отсутсвует произвольный доступ к видео — читать кадры можно только последовательно. Так же, по субъективным ощущениям открытие видео и чтение кадров с помощью системных объектов происходит несколько дольше, по сравнению с использованием mmreader и read.

Проигрывание видео


Помимо того, что видео можно читать и обрабатываеть, его можно еще и проиграть встроенными в матлаб средствами.

Проигрывание с помощью функции movie


Функция movie позволяет проиграть видеоролик. Особенность использование этой функции состоит в том, что нужно заранее подготовить весь видеоролик, который должен быть проигран. Вдобавок, ролик должен хранится в памяти в формате отличном от того, который получается при чтении видео. Далее приведен пример того, как можно использовать эту функцию для проигрыша первых 200 кадров видео:

%% чтение видео
filename = 'd:\test\1.avi';
video = mmreader(filename);
nFrames = 200;

% подготавливаем структуру в которую будет прочитана информация
mov(1:nFrames) = struct('cdata', [], 'colormap', []);

% Читаем покадрово
for k = 1 : nFrames
    mov(k).cdata = read(video, k);
end

%% проигрываем видео
hf = figure;
set(hf, 'position', [150 150 video.Width video.Height])
movie(hf, mov, 1, video.FrameRate);


В этом коде, в переменной mov подготавливается ролик. Эта переменная хранит массив из структур. Каждая структура имеет два поля: cdata и colormap. Поле cdata хранит битовый образ кадра, а поле colormap нужно что-бы хранить цветовую палитру для видео с индексированным цветом. В данном случае это полноценное RGB-видео и поле colormap пусто.

Проигрывание с помощью системных объектов


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

% создаем объект для чтения видео
reader = video.MultimediaFileReader('Filename', 'd:\test\1.avi');

% создаем объект для проигрывания видео
player = video.VideoPlayer;

% читаем покадрово и проигрываем видео
while ~isDone(reader)
   frame = step(reader);
   step(player, frame);
end


Запись видео


Записывать видео тоже можно несколькими способами. Здесь я разберу 3 метода: покадровая запись, запись видео одним куском и использование системных объектов.

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

Запись видео покадрово



%% подготавливаем переменные
filename = 'd:\test\1.avi';
result_file = 'd:\test\result.avi';
nFrames = 200;
video = mmreader(filename);
% открываем файл для записи
% компрессор устанавливаем - XVID
writer = avifile(result_file, 'compression', 'XVID');

%% в цикле читаем-модифицируем-записываем
% структурирующий элемент для выполнения дилатации
se = strel('rectangle', [3 3]);
for k = 1:nFrames
    % читаем кадр
    frame = read(video, k);
    % выполняем дилатацию кадра
    frame = imdilate(frame, se);
    % записываем кадр
    writer = addframe(writer, frame);
end
% обязательно закрывайте writer
writer = close(writer);


Здесь отмечу способ которым задаются аргументы функции avifile. Первый аргумент — это имя файла, далее аргументы идут парами — сначала имя параметра, потом значение. В вышеуказанном примере функции avifile помимо имени файла передается еще и параметр compression со значением XVID.

В качестве параметра compression можно указать один из идентификаторов: 'None', 'MSVC', 'RLE', 'Cinepak', 'Indeo3' или 'Indeo5'. В *nix системах доступен только вариант 'None'. Помимо вышеуказанных вариантов можно еще указать FourCC желаемого компрессора (как в примере). Здесь уже все зависит от установленных в системе кодеков для кодирования видео. Стоит отметить, что не все из установленных кодеков пригодны как для декодирования, так и для кодирования видео. У меня на системе из нескольких опробованных вариантов заработал только XVID.

Запись видео одним куском


Помимо покадровой записи, можно еще подтоговить массив кадров и записать их все сразу в файл. Массив кадров должен быть подготовлен в том-же виде, как и в примере использования функции movie для проигрывания видео. Более делатьно: массив должен состоять из структур, один из членов структуры — поле cdata, хранящее битовый образ картинки, а второй — поле colormap, хранящее палитру для изображения с индексированным цветом:

%% чтение видео
filename = 'd:\test\1.avi';
video = mmreader(filename);
nFrames = 200;

% подготавливаем структуру в которую будет прочитана информация
mov(1:nFrames) = struct('cdata', [], 'colormap', []);

% Читаем и модифицируем первые 200 кадров видео
se = strel('rectangle', [3 3]);
for k = 1 : nFrames
    frame = read(video, k)
    mov(k).cdata = imdilate(frame, se);
end

%% запись видео
movie2avi(mov, 'd:\test\result.avi', 'compression', 'XVID', 'fps', 25);


Параметр compression у функции movie2avi такой же как и в предыдущем разделе.

Запись видео с помощью системных объектов


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

reader = video.MultimediaFileReader('Filename', 'd:\test\1.avi');
% открываем видео на запись,
% имя файла = d:\test\result.avi
% аудио - нет, видео - есть
% видео кодек = MJPEG
writer = video.MultimediaFileWriter('Filename', 'd:\test\result.avi', ...
    'AudioInputPort', false, 'VideoInputPort', true, ...
    'VideoCompressor', 'MJPEG Compressor');
% структурирующий элемент для дилатации
se = strel('rectangle', [3 3]);
while ~isDone(reader)
   % читаем кадр
   frame = step(reader);
   % выполняем дилатацию кадра
   frame = imdilate(frame, se);
   % записываем кадр
   step(writer, frame);
end

% обязательно закрывайте writer
close(writer);


В качестве аргументов функции video.MultimediaFileWriter указываются пары 'имя параметра', значение параметра. Даже имя файла передается таким образом (т.е. указывается пара 'Filename', 'имя файла').

Возможные значения параметра VideoCompressor здесь уже отличаются от тех, которые можно указывать для функций avifile и movie2avi. В качестве VideoCompressor может быть указано 'None (uncompressed)', еще у меня заработало 'MJPEG Compressor'. Если в списке аргументов параметр VideoCompressor вообще опустить, то это будет эквивалентно указанию 'None (uncompressed)'.

Свободная альтернатива в лице GNU Octave


Вообще продукт Matlab достаточно дорогой, однако есть частично совместимые с ним свободные альтернативы. Одна из них — GNU Octave.

GNU Octave активно развивается и синтаксис собственно языка практически идентичен матлабовскому. Отличия в синтаксисе в основном обусловлены тем, что какая-то фича уже появилась в матлабе, а в Octave еще не добавлена.

С набором библиотек дела обстоят хуже. Тулбоксы матлаба в GNU Octave отсутствуют, хотя для части функций все же написаны замены.

Установка GNU Octave в Ubuntu 10.04


Я работал с Octave в Ubuntu, поэтому для тех, кто захочет повторить мой опыт описываю инструкцию к установке:

1. С помощью менеджера пакетов нужно установить линуксовые пакеты octave3.2; octave3.2-doc; octave3.2-headers со всеми зависимостями.
2. Octave в отличие от матлаба состоит только из интерпретатора. Соответсвенно вам может понадобится среда разработки с графическим интерфейсом. Я в Ubuntu 10.04 установил QtOctave (тоже через менеджер пакетов).
3. Если вы планируете работать с видео, то необходимо установить линуксовые пакеты libavformat-dev, libavcodec-dev, libavutil-dev, libswscale-dev
4. В самой Octave есть также понятие пакета. Octave-пакеты включают в себя набор различных функций расширяющих функциональность Octave. Скачать Octave-пакеты можно с сайта Octave-Forge.
Для установки Octave-пакета нужно запустить Octave с административными правами: sudo octave. А затем в Octave выполнить команду pkg install /path/to/package.tar.gz
Для работы с изображениями и видео понядобятся Octave-пакеты image и video

Собственно работа с видео в Octave


В целом, загрузка и сохранение видео в Octave выполнятеся сходным образом с тем, как это делается в матлабе. Далее приведен код выполняющий дилатацию первых 25-ти кадров видео:

filename = '/home/alexey/octave/1.avi';
result_file = '/home/alexey/octave/result.avi';

% раскомментируйте следующий строчки, для доступа к свойствам видео
%info = aviinfo(filename);
%width = info.Width
%height = info.Height

% создаем объект записывающий видео
writer = avifile(result_file);
se = ones([3 3 3]);
for i=1:25
  % читаем кадр с номером i из видео
  frame = aviread(filename, i);
  % выполняем дилатацию кадра
  frame = imdilate(frame, se);
  % записываем кадр
  addframe(writer, frame);
end


Здесь функция avifile(filename, frameNo) выполняет чтение кадра с номером frameNo из видеофайла с именем filename. Заранее не нужно создавать никаких объектов и открывать файл. В матлабе тоже присутствует эта функция, но она объявлена устаревшей.

Если нужно узнать информацию о видео, то это можно сделать с помощью функции aviinfo(filename). В результате будет возвращена структура хранящая информацию о размерах и формате видео, количестве кадров и т.д. В матлабе данная функция объявлена устаревшей.

Запись видео производится с помощью функций avifile и addframe, но в матлабе и Octave синтаксис этих функций немного отличается. Вдобавок, в Octave не нужно закрывать объект выполняющий запись видео (более того даже нет метода который бы его закрывал).

В итоге, что бы вышеприведенный код для Octave заработал в матлабе его нужно модифицировать следующим образом:

%% чтение видео
filename = 'd:\test\result.avi';
result_file = 'd:\test\result2.avi';

%info = aviinfo(filename);
%width = info.Width;
%height = info.Height;

writer = avifile(result_file, 'compression', 'xvid');
se = strel('rectangle', [3 3]);
for i=1:25
   frame = aviread(filename, i);
   frame.cdata = imdilate(frame.cdata, se);
   writer = addframe(writer, frame.cdata);
end
writer = close(writer);


Таким образом в плане обработки видео и картинок на данный момент имеется разница в синтаксисе матлабовских функций и функций GNU Octave.
Tags:
Hubs:
+8
Comments 4
Comments Comments 4

Articles