Способ извлечения рук из видео

Интересно, можно ли будет матлабом извлечь из видео только руки. В видео руки выполняют какой-то жест. Поскольку первые кадры - это только фон, я пробовал так:

readerObj = VideoReader('VideoWithHands.mp4');
nFrames = readerObj.NumberOfFrames;
fr = get(readerObj, 'FrameRate');
writerObj = VideoWriter('Hands.mp4', 'MPEG-4');
set(writerObj, 'FrameRate', fr);
open(writerObj);
bg = read(readerObj, 1);   %background
for k = 1 : nFrames
      frame = read(readerObj, k);
      hands = imabsdiff(frame,bg);
      writeVideo(writerObj,hands);
end
close(writerObj);

Но я понял, что цвета рук не "настоящие" и они прозрачные. Есть ли лучший способ извлечь их из видео, сохраняя цвета и уровень непрозрачности, используя первые кадры (фон)?

РЕДАКТИРОВАТЬ: Что ж, я нашел хорошую настройку для объекта vision.ForegroundDetector, теперь руки являются белыми логическими областями, но когда я пытаюсь визуализировать их с помощью:

videoSource = vision.VideoFileReader('VideoWithHands.mp4', 'VideoOutputDataType', 'uint8');

detector = vision.ForegroundDetector('NumTrainingFrames', 46, 'InitialVariance', 4000, 'MinimumBackgroundRatio', 0.2);

videoplayer = vision.VideoPlayer();
hands = uint8(zeros(720,1280,3));
while ~isDone(videoSource)

    frame = step(videoSource);
    fgMask = step(detector, frame);

    [m,n] = find(fgMask);
    a = [m n];
    if isempty(a)==true

        hands(:,:,:) = uint8(zeros(720,1280,3));
    else


        hands(m,n,1) = frame(m,n,1);
        hands(m,n,2) = frame(m,n,2);
        hands(m,n,3) = frame(m,n,3);

    end



    step(videoplayer, hands)



end

release(videoplayer)
release(videoSource)

или поместите их в видеофайл с помощью:

eaderObj = VideoReader('Video 9.mp4');
nFrames = readerObj.NumberOfFrames;
fr = get(readerObj, 'FrameRate');



writerObj = VideoWriter('hands.mp4', 'MPEG-4');

set(writerObj, 'FrameRate', fr);

detector = vision.ForegroundDetector('NumTrainingFrames', 46, 'InitialVariance', 4000, 'MinimumBackgroundRatio', 0.2);
open(writerObj);

bg = read(readerObj, 1);


frame = uint8(zeros(size(bg)));

for k = 1 : nFrames


frame = read(readerObj, k);

   fgMask =  step(detector, frame);


[m,n] = find(fgMask);

hands = uint8(zeros(720,1280));

if isempty([m n]) == true

    hands(:,:) = uint8(zeros(720,1280));

else

    hands(m,n) = frame(m,n);

end

 writeVideo(writerObj,mani);





end

close(writerObj);

...мой компьютер зависает. Какое-то предложение?


person cyberdyne    schedule 24.01.2014    source источник


Ответы (2)


Итак, вы пытаетесь убрать фон, сделав его черным, верно? Самый простой способ сделать это — отфильтровать его, вы можете сделать это, сравнив данные разности с пороговым значением, а затем используя результат в качестве индексов для установки пользовательского фона.

filtered = imabsdiff(frame,bg);
bgindex = find( filtered < 10 );
frame(bgindex) = custombackground(bgindex);

где custombackground — это любой файл изображения, который вы хотите поместить в фон. Если вы хотите, чтобы он был просто черным или белым, используйте 0 или 255 вместо custombackground(bgindex). Обратите внимание, что числа зависят от формата ваших видеоданных и могут быть неточными (кроме 0, это число всегда должно быть правильным). Если слишком много отфильтровано, уменьшите 10 выше, если слишком много останется неотфильтрованным, увеличьте 10.

В конце вы записываете свой измененный кадр обратно в видео, поэтому он просто заменяет переменную hands в вашем коде.

Кроме того, в зависимости от вашего формата вам, возможно, придется выполнить сравнение между значениями RGB. Это немного сложнее, так как включает одновременную проверку 3 значений и магию с индексами. Это версия RGB (работает со всем, что содержит 3 цветные полосы):

filtered = imabsdiff(frame,bg); % differences at each pixel in each color band
totalfiltered = sum(filtered,3); % sums up the differences
                                 % in each color band (RGB)
bgindex = find( totalfiltered < 10 ); % extracts indices of pixels
                                      % with color close to bg
allind = sub2ind( [numel(totalfiltered),3] , repmat(bgindex,1,3) , ...
                  repmat(1:3,numel(bgindex),1) ); % index magic

frame(allind) = custombackground(allind); % copy custom background into frame

ИЗМЕНИТЬ:

Вот подробное объяснение магии индекса.

Предположим, изображение размером 50x50. Предположим, что пиксель в строке 2 столбца 5 является фоновым, тогда bgindex будет содержать число 202 (линейный индекс, соответствующий [2,5] = (5-1)*50+2). Нам нужен набор из 3 индексов, соответствующих координатам матрицы [2,5,1], [2,5,2] и [2,5,3]. Таким образом, мы можем изменить все 3 цветные полосы, соответствующие этому пикселю. Чтобы упростить расчеты, этот подход фактически предполагает линейную индексацию изображения и, таким образом, преобразует его в изображение размером 2500x1. Затем он расширяет 3 цветные полосы, создавая матрицу 2500x3. Вместо этого мы создадим индексы [202,1], [202,2] и [202,3].

Для этого мы сначала строим матрицу индексов, повторяя наши значения. repmat делает это за нас, он создает матрицы [202 202 202] и [1 2 3]. Если бы в bgindex было больше пикселей, первая матрица содержала бы больше строк, каждая из которых повторяла бы линейные координаты пикселя 3 раза. Вторая матрица будет содержать дополнительные [1 2 3] строк. Первый аргумент sub2ind — это размер матрицы, в данном случае 2500x3, поэтому мы вычисляем количество пикселей, применяя numel к вектору суммы (который сворачивает 3 полосы изображения в 1 значение и, таким образом, имеет 1 значение на пиксель) и добавьте статическую цифру 3 во втором измерении.

sub2ind теперь берет каждый элемент из первой матрицы как индекс строки, каждый соответствующий элемент из второй матрицы как индекс столбца и преобразует их в линейные индексы в матрицу размера, который мы определили ранее. В нашем примере это приводит к индексам [202 2702 5202]. sub2ind сохраняет форму входных данных, поэтому, если бы у нас было 10 пикселей фона, этот результат имел бы размер 10x3. Но поскольку линейная индексация не заботится о форме индексной матрицы, она просто принимает все эти значения.

Чтобы убедиться, что это правильно, давайте изменим значения в примере. Исходные данные изображения будут иметь размер 50x50x3. Для матрицы NxMxP линейный индекс индекса [n m p] может быть рассчитан как ind = (p-1)*M*N + (m-1)*N + n. Используя наши значения, мы получаем следующее:

[2 5 1] => 202
[2 5 2] => 2702
[2 5 3] => 5202

ind2sub подтверждает это.

person scenia    schedule 24.01.2014
comment
В полученном видео я все еще вижу кое-что из фона; в качестве пользовательского фона я установил нули (размер (кадр)) - person cyberdyne; 25.01.2014
comment
Я не проверял это, но просто 0 тоже должен работать. Экономит память. Если и остались реликвии, то, вероятно, это колебания цвета фона. Увеличение порога фильтра (10 в моем примере выше) должно решить эту проблему, но имейте в виду, что это также увеличивает вероятность фильтрации пикселей переднего плана. Однако, если цвета сильно различаются, безопасно дойти до 50. Теоретически вы можете фильтровать каждую полосу отдельно, но это намного сложнее. То же самое касается фильтрации всей связанной области. Это возможно, но не так просто, как этот трюк. - person scenia; 27.01.2014
comment
Большое тебе спасибо. Я попробую с некоторыми пороговыми значениями. Один вопрос: можете ли вы объяснить, что он делает с.... allind = sub2ind( [numel(totalfiltered),3] , repmat(bgindex,1,3) , ... repmat(1:3,numel(bgindex) ,1) ); ???? - person cyberdyne; 27.01.2014
comment
Это преобразует индексы. Я отредактирую подробное объяснение выше, в комментариях недостаточно символов. - person scenia; 27.01.2014
comment
Кроме того, он в основном просто выполняет sub2ind( size(filtered) , ind2sub( size(totalfiltered),bgindex) , i ) в цикле for i=1:3, но использует векторизованную форму для экономии ресурсов. - person scenia; 27.01.2014
comment
Извините, если я снова открою вопрос, но что произойдет, если объект будет иметь тот же цвет, что и фон? Я пробовал с белым объектом на белом фоне, и когда руки находятся близко к объекту, видео показывает фон. - person cyberdyne; 19.02.2014
comment
То же самое происходит, если кто-то входит в синий ящик и имеет одежду того же цвета, что и фон. Алгоритм ошибочно принимает объект за фон. Нет другого способа обойти это, кроме как убедиться, что фон имеет уникальный цвет. Или извлечение признаков, что чертовски сложнее... - person scenia; 19.02.2014
comment
Итак, было бы легко, если бы фон был белым, а объект черным или наоборот, не так ли? - person cyberdyne; 19.02.2014
comment
Да. Фон должен быть далеко от объектов с точки зрения цвета. Предложенный мной метод вычисляет разницу в полосах красного, зеленого и синего цветов и помечает точку как фон, если эта разница в сумме меньше некоторого порогового значения (10 в приведенном выше примере). Если цвет слишком похож на фон, алгоритм не заметит разницы. - person scenia; 19.02.2014

Да, есть лучший способ. Набор инструментов системы компьютерного зрения включает объект vision.ForegroundDetector, который делает то, что вам нужно. Он реализует алгоритм Gaussian Mixture Model для вычитания фона.

person Dima    schedule 25.01.2014
comment
Я использовал его, но он делает хорошее обнаружение, если передний план постоянно движется. - person cyberdyne; 26.01.2014
comment
@user3063488 user3063488 Если Matlab дает сбой на вашем ПК, вам следует позвонить в службу технической поддержки Mathworks. - person Dima; 27.01.2014