金子邦彦研究室プログラミングOctave の活用AVI 形式の動画ファイルを扱う例

AVI 形式の動画ファイルを扱う例

AVI 形式の動画ファイルを扱う例を説明

前準備

必見 Web ページ: http://www.eecs.berkeley.edu/Research/Projects/CS/vision/bsds/

※ プロンプトを変えたいときは,次のように操作する.

PS1('> ')

※ Octave のインストールによっては,Octave の起動時に毎回次の操作を行う必要があるかもしれない

pkg load image

実行方法と実行結果の例

  1. Octave の起動

    Linux の場合

    Windows の場合

    デスクトップのアイコン等を使って Octave を起動

    [image]
  2. 動画ファイルの読み込み(1フレーム分)とフレームの確認表示

    ファイル名とフレーム番号を指定している./usr/OpenCV-2.0.0/samples/c/tree.avi は OpenCV に付属のサンプルデータ

    pkg load video
    img = aviread("/usr/local/share/OpenCV/samples/c/tree.avi", 100);
    imshow(img);
    

    ■ Octave 3.6.3, Ubuntu バージョン 12.04 での実行結果例

    [image]

    [image]

aviread() 関数で,必ずしもすべての種類 avi ファイルが読めるとは限りません. ffmpeg 内のコーデックと avi 動画ファイルの相性に起因すると思われるバグの例を示しておきます (このときは, Linux なら winff のようなツールを使ってエンコードしなおす)

[image]

1004 の挙動がおかしい

■ フレーム番号 1003

[image]

■ フレーム番号 1004

[image]

■ フレーム番号 1005

[image]

Octave で avi 形式動画ファイルを扱う例

avi 形式動画ファイルに関する情報取得

aviinfo() 関数を使って,1秒あたりのフレーム数,縦,横などを取得できる.

pkg load video
avi = aviinfo("/usr/local/share/OpenCV/samples/c/tree.avi");
avi

■ Octave 3.6.3, Ubuntu バージョン 12.04 での実行結果例

[image]

簡単なフレーム間差分表示

pkg load video;
img = aviread("/tmp/V0701001winff.avi", 1000);
img2 = aviread("/tmp/V0701001winff.avi", 1002);
# Octave 3.2.3 よりも古いバージョンの Octave では「imshow( (img2 - img) * 128 + 128 );」でうまく行く場合がある
imshow( ( (img2 - img) + 1 ) / 2 );

■ Octave 3.3.53, Ubuntu での実行例

[image]

(参考)動画ファイルの差分画像の表示例

カメラが固定している場合,背景がきれいに除去できることが確認できた.

[image]

avi 形式動画ファイルの各フレームに関する繰り返し処理

ガウシアンフィルタを使って,avi 形式動画ファイルの各フレームを「ぼかし」た後,32 色に減色することを繰り返す例. kbhit() 関数を使っているので,何かのキーを押すと次のフレームに移る.

pkg load video;
s = 2; 
f = fspecial("gaussian", s * 5, s); 
for i = [100:110]
  rgb = aviread("/usr/OpenCV-2.0.0/samples/c/tree.avi", i);
  rgb2 = imfilter(double(rgb), f, "same"); 
  imwrite(rgb2, "/tmp/hoge.ppm");
  system( "ppmquant 32 /tmp/hoge.ppm > /tmp/hoge2.ppm" ); 
  rgb3 = imread( "/tmp/hoge2.ppm" );
  imshow( rgb3 ); 
  kbhit(); 
end

[image]

※ imwrite() 関数の実行において「セマフォ・・・」のようなエラーがでることが有ります. このときは,プログラムの先頭で,関係の無い画像ファイルでいいので,imread(), imwrite() を実行しておく(つまり aviread() 関数の前に)とエラーを回避できる場合があります.

a = imread("fruits.jpg");
imwrite(a, "/tmp/fruits.jpg");

avi 形式動画ファイルの生成の例

上の例では,画像を 1枚1枚表示していましたが,新しい avi 動画ファイルを作ってみることにします.

pkg load video;
s = 2; 
f = fspecial("gaussian", s * 5, s); 
F = avifile("tree2.avi");
for i = [1:445]
  rgb = aviread("/usr/OpenCV-2.0.0/samples/c/tree.avi", i);
  rgb2 = imfilter(double(rgb), f, "same"); 
  imwrite(rgb2, "/tmp/hoge.ppm");
  system( "ppmquant 32 /tmp/hoge.ppm > /tmp/hoge2.ppm" ); 
  rgb3 = imread( "/tmp/hoge2.ppm" );
  addframe(F, double(rgb3) / 255);
end

動画ファイルのフレーム(複数)を順に読み込み,png 形式で保存

while 文を使用.第 fromframe フレームから読み込み,toframe に達するか,フレームが無くなるまで処理を続ける.

AVI ファイルの全フレームについて同じ処理を繰り返すときのプログラム見本としても使えると思います.

※ 画像ファイルの確認には,ImageMagick の display コマンドが便利です.

function png2avi(filename, basename, fromframe, toframe, fps, bitrate)
# png ファイルの連番画像ファイルを読み込んで,avi ファイルとして書き出す
f = avifile(filename, "fps", fps, "bitrate", bitrate );
i = fromframe;
while(true)
    # 変数 pngname は文字列データ
    pngname = sprintf("%s_%6.6d.png", basename, i); 
    rgb = imread( pngname );
    # 0 から 1 の範囲
    rgb2 = double(rgb) / 255;
    addframe(f, rgb2);
    i = i + 1;
    if ( i > toframe )
      break;
    endif
endwhile
endfunction

function avi2png(filename, basename, fromframe, toframe)
# avi ファイル(ファイル名は filename)を読み込んで,avi ファイルの中のフレームを png ファイルの連番画像ファイル(ファイル名は basename に「_」と数値をつなげたもの)として書き出す.
# png ファイルとして書き出すフレームの範囲は fromframe, toframe で指定する
i = fromframe;
while(true)
  try
    img = aviread(filename, i);
    # OCtave 3.2 系列の場合
    imwrite( img, sprintf("%s_%6.6d.png", basename, i) );
    # OCtave 3.0 系列の場合
#    imwrite( sprintf("\"%s_%6.6d.png\"", basename, i), img(:,:,1), img(:,:,2), img(:,:,3) );
    fdisp(stderr, i);
    i = i + 1;
    if ( i > toframe )
      break;
    endif
  catch
    # もし toframe の値が,動画ファイルの実際のフレーム数より大きいと実行時エラーが出るので catch して break する
    break;
  end_try_catch
endwhile
endfunction

# avi ファイルを png 連番画像ファイルへ
# avi2png( "/home/kaneko/V0701001winff.avi", "/home/kaneko/20090701001_avi01", 2000, 2200 );
# png 連番画像ファイルを avi ファイルへ
# png2avi( "/home/kaneko/hoge.avi", "/home/kaneko/20090701001_avi01", 2000, 2200, 30, 800000);
avi2png( "/home/kaneko/V0701001winff.avi", "/home/kaneko/20090701001_avi01", 1, 10000 );