目次


Perl、ImageMagick、MPlayer を使ってモザイク・ムービーを作成する

凝ったビデオ・エフェクトを作成するのに高性能のハードウェアは必要ありません

Comments

モザイク・ムービーは、ビジュアル的な魅力があり、技術的な進歩を感じさせるので、今日のテレビや広告媒体で人気があります。この記事では、テキストとグラフィックスを前景に使った独自のモザイク・ムービーを作成する方法について説明します。背景の対象となる画像は、既存のビデオ・ライブラリーのフレーム・セットで構成します。

まず MPlayer を使ってビデオのフレームを jpeg ファイルに抽出します。その画像をPerl スクリプトで処理し、順番に合成します。さらに、MPlayer のムービー・エンコーダーであるmencoder を使って、ムービー・フレームを mpeg4 や divx でエンコードされたビデオ・ストリームにします。

必要なシステム

画像の操作と合成処理は、メモリーや CPU への負荷が非常に高くなります。この記事では、最低800 MHz の Pentium® 3 プロセッサーと 4 プロセッサーに、512 MB を超えるRAM を搭載したシステムを使用しました。さらに、分解処理では、ソースの解像度によっては大量のディスク・スペースが必要になることがあります。今回は、コンポーネント・フレームごとに0.7 メガピクセルのタイルを使います。高品質のソース・ムービーを使う場合は、タイル用に大容量のディスク・スペースを用意してください。さらに、合成フェーズでは総当たりの方法を使って、タイルを「マスク」画像に挿入します。このステップには比較的時間がかかります。完成したビデオの1 フレームを作成するのに、2-GHz の Pentium 4 では 15 秒必要です。高速なプロセッサーは必要ありませんが、古いハードウェアを使うとレンダリング時間が長くなることを覚えておいてください。

また、Perl、GD、The Gimp、ImageMagick が必要です。これらはすべてデフォルトで、ほとんどのLinux® ディストリビューションにインストールされています。MPlayer と、ムービー・フォーマットの読み書きに必要なビデオ・コーデック(Mplayer と関連したもの) も必要です。これらのツールの入手先について詳しくは、参考文献を参照してください。必須ではありませんが、モザイク・タイルの処理と完成したレンダリング・フレームの表示のために、優れた画像表示プログラムであるfeh を使うことをお勧めします。

The Gimp で前景画像を作成する

前景画像のサイズは、ムービー画像のタイル・サイズの何倍も大きくします。例えば、解像度が320x240 のタイルに適した前景画像のサイズは 3520x720 で、最終レンダリングで見栄えのよいワイドスクリーンになります。ワイド・フォーマットは、ELENAあるいは他の名前のような一語のテキスト・エントリーにも適しています。3520x720の透明な背景を使用して、The Gimp で新規ファイルを作成します。次に、白い前景の「マスク」画像をこのキャンバスに挿入します。この段階では、純粋な白であればどのようなテキストやグラフィックスでも使用できます。画像内の白いピクセルは、後のステップではパススルー・マスクとして扱われます。この例では、テキストELENA を Gill Sans Bold 2nd Medium フォントにします。これで、さまざまな段階のズーム処理でタイルをちょうどよい幅で表示する領域ができます。ここでは、前景のテキスト・オーバーレイ・ファイルを3520x720whiteGillSansBoldSecondMedium.png として保存して、合成レンダリング・ステップで使用します。

図 1. The Gimp 3520x720 ELENA
The Gimp 3520x720 ELENA
The Gimp 3520x720 ELENA

ムービーのタイル画像

この記事を書くために、私のデジタル・カメラから 320x240 の AVI フォーマットのムービーを使いました。この記事で使っているツールの別バージョンと別の方法では、より高い解像度やワイドスクリーン・フォーマットのビデオを使っても、正常に行うことができました。しかし、この記事では、必要なシステム・リソースを少なくするために、すべてのソース・ビデオを320x240 に変換することをお勧めします。

タイルを処理する

モザイク・ビデオを作成するには、すべてのソース・ビデオから各フレームを抽出する必要があります。MPlayerと単純な Perl スクリプトを使って、ソース・ファイルからディスク上の jpegファイルへのフレームの抽出を自動化します。

  1. すべてのムービー・ファイルを入れるディレクトリーを作成します。この例では、ディレクトリーavis を使います。すべてのビデオ・ファイルをこの avis ディレクトリーにコピーしてください。
  2. 次のコードを使って、buildTiles.pl というファイルを作成します。
    リスト 1. buildTiles.pl
    #!/usr/bin/perl -w
    # buildTiles.pl - create jpgs of each frame of source movies
    use strict;   # like a librarian
    my $cmd = ""; # command line string
    
    my $mPlayer = `which mplayer`; # get location of mplayer
    chomp($mPlayer);
    if( $mPlayer eq "" ){
      die "no mplayer detected!\n" .
          "this project requires mplayer for decoding/encoding of\n".
          "video frames, please install mplayer.\n";
    }# if mplayer not detected
    
    my $dirName = $ARGV[0] or die "specify directory with video files\n";
    
    my @files = `ls -1 $dirName`;
    
    for my $fName ( @files ){
    
      chomp($fName);
      print "processing $fName\n";
    
      my $tileDir = "$dirName/$fName" . ".tiles";
    
      $cmd = `mkdir $tileDir`;
    
      # mplayer code to extract each frame into tileDir
      $cmd = `$mPlayer -vo jpeg $dirName/$fName`;
      $cmd = `mv *.jpg $tileDir/`;
    
    }#while stdin
    
    print "all files processed \n";
  3. コマンド perl buildTiles.pl avis を使って、タイルの生成ステップを実行します。これで、avis 内のすべてのファイル名についてavis の下にサブディレクトリーが作成されます。そこには、サブディレクトリーに保存されたムービーの各フレームのjpeg が入ります。

これらのタイルは、次のステップで背景コンポーネントとして使われます。MPlayerが正しくインストールされていなかったり、選択したビデオ・ストリームをデコードするために必要なコーデックがなかったりすると、このプロセスのステップは失敗するので注意してください。この例では、-vojpeg オプションで、フレームを jpeg 画像として現行ディレクトリーに抽出するようMPlayer に伝えています。その後、すべてのファイルがそのファイル用の tileディレクトリーに移動されます。

背景フレームを作成する

ここまでで、ソース・ビデオ・ファイルの各フレームを抽出したので、最終プロダクトのフレームの順番で、タイル状の背景画像を作成することができます。このステップは、最終レンダリング・ステップにつながっているため、単に指定されたパラメーターで背景画像を1 つずつ作成すれば、フレームごとに生成処理を自動で行います。

  1. 自動生成された背景のすべての格子画像を入れる backgrounds ディレクトリーを作成します。
  2. 次のコードを使って、buildBackgroundFrame.pl というファイルを作成します。
    リスト 2. buildBackgroundFrame.pl
    #!/usr/bin/perl -w
    # buildBackgroundFrame.pl - create a background image from tiles in sequence
    # assumes a tile resolution of 320x240
    use strict; # like an English banker
    use GD;     # image library
    
    my $errMsg = "specify an image width, height, frame number, file list";
    my $im_width  = $ARGV[0] or die $errMsg; # width of overlay image
    my $im_height = $ARGV[1] or die $errMsg; # height of overlay image
    my $frameNum  = $ARGV[2] or die $errMsg; # frame number
    shift; shift; shift;                     # remove image parameters
    my @tileList  = @ARGV    or die $errMsg; # list of tile directories 
    
    # create a new image in true color for tiles to be inserted into
    my $image = new GD::Image($im_width,$im_height,1);
    
    my $tilePos = 0;  # position in tile list
    my $xpos = 0;     # incrementer for x axis
    my $ypos = 0;     # incrementer for y axis
    
    # number of rows for a 720 pixel high image, assumes a 240 tile image height
    my $row_total = $im_height / 240;
    
    # for each row
    while( $ypos < $im_height ){
    
      $xpos = 0;
    
      #for each column
      while( $xpos < $im_width ){
    
        # use each image in tile list in order
        my $currimage = $tileList[ $tilePos ];
        $tilePos++;
        # reset to beginning of tile list if all have been used
        if( $tilePos == @tileList ){ $tilePos = 0; }
    
        # open the image
        my $tileimg = newFromJpeg GD::Image( "$currimage/$frameNum.jpg");
    
        # insert the image into the existing large template
        # at position xpos, ypos (640,480 for example), starting at 
        # position 0,0 in the source image, with a height and width
        # of 320,240
        $image->copy($tileimg,$xpos,$ypos,0,0,320,240);
    
        # move to next column
        $xpos += 320;
    
      }#while xpos < width
    
      # move to next row
      $ypos+= 240;
    
    }#while ypos < im_height
    
    # write out the image
    print "writing image backgrounds/back_$frameNum\n";
    open( TILEOUT,"> backgrounds/back_$frameNum.png") or die
       "can't write to backgrounds/back_$frameNum.png";
      print TILEOUT $image->png;
    close(TILEOUT);
  3. このステップをテストするには、コマンド perl buildBackgroundFrame.pl 3520 720 00000001 `ls -1d avis/*.tiles` を実行します。

上記のコマンドは、00000001.jpg という名前のタイルで満たされた 3520x720の画像を作成します。00000001.jpg は、ls -1d avis/*.tiles コマンドで指定された各ディレクトリーに入っています。このステップは、3520x720の格子全体にビデオ抽出処理でできたタイルを入れることに注意してください。これらのタイルがすべて最終合成画像で表示されるわけではないので、ちょっとした処理をすることでこのステップのパフォーマンスを向上させることができます。この記事で示したステップはすべて単純に設計されているので、この記事全体を通して、処理速度を速くできる箇所が数多くあります。

フレームのレンダリング

最終レンダリング処理では、前のステップで作成した画像の合成、抽出、そしてサイズ変更を自動的に行う必要があります。静止画像でこの作業を行う方法の例については、「Perlと ImageMagick を使ってモザイク画像を作る」(参考文献を参照) を参照してください。

以下のステップを実行してください。

  1. 一時的なフレームやファイナライズしたレンダリング・フレームを入れる renderディレクトリーを作成します。
  2. 次のコードを使って、buildVideoFrames.pl というファイルを作成します。
    リスト 3. buildVideoFrames.pl
    #!/usr/bin/perl -w
    # buildVideoFrames.pl - create frames of mosaic video
    use strict;               # like Captain von Trapp
    use GD;                   # image processing library
    use List::Util 'shuffle'; # to randomize the image list
    
    my $errMsg = "specify an image width, height, frame max, frame window, ".
                 "transparent, tiles directory \n";
    my $im_width    = $ARGV[0] or die $errMsg; # width of source image
    my $im_height   = $ARGV[1] or die $errMsg; # height of source image
    my $frameMax    = $ARGV[2] or die $errMsg; # maximum frames to process
    my $frameWindow = $ARGV[3] or die $errMsg; # size to increment each frame
    my $transP      = $ARGV[4] or die $errMsg; # transparent or white background
    my $tilesDir    = $ARGV[5] or die $errMsg; # tiles directory
    
    my $currFrame   = 1;  # current frame counter
    my $cmd         = ""; # command line string
    my $tileSc      = ""; # space delimited list of tile names
    
    my ($sizeX, $sizeY, $cropX, $cropY, $frSizeX, $frSizeY) ="";
    $sizeX = 960;   # end image size X dimension
    $sizeY = 176;   # end image size Y dimension 
    $cropX = 1280;  # start of crop area X dimension
    $cropY = 262;   # start of crop area Y dimension
    
    # frSizeX is actually X dimension pieces of frameWindow size
    # frSizeY is computed based on frameWindow so zoom out is smooth
    $frSizeX = ($im_width/2) /$frameWindow;
    $frSizeY = ($im_height/2) /$frameWindow;
    
    # if necessary, create a white background of sizeXxsizeY
    if( $transP == 1 ){ createSmallWhiteBackground(); }
    
    # build a randomized list of tile names
    my @tileList = `ls -1d $tilesDir/*.tiles`;
    @tileList = shuffle(@tileList);  # randomize the array
    
    # build a space delimited list of tile names for passing on the command line
    for( @tileList ){ chomp($_); $tileSc .= "$_ "; }
    
    
    while( $currFrame <= $frameMax ){
    
      # create a number like 00000001
      my $padFrame = sprintf( "%08d", $currFrame);
    
      # create background grid of images - if you are happy with your background
      # tiles, and just want to create mosaics with different foregrounds, comment
      # out this step to increase speed
      $cmd = "perl ./buildBackgroundFrame.pl " .
             "$im_width $im_height $padFrame $tileSc";
      print "$cmd \n";
      $cmd = `$cmd`;
    
      # composite the foreground with background images
      # insert your own foreground image filename in the second step
      $cmd = "composite -compose in backgrounds/back_$padFrame.png " .
             "3520x720whiteGillSansBoldSecondMedium.png " .
             "composite/frame_$padFrame.png";
      print "$cmd\n";
      $cmd = `$cmd`;
    
      # extract the zoomed in portion of the image
      $cmd = "convert -crop ${sizeX}x${sizeY}+${cropX}+${cropY} " .
             "composite/frame_$padFrame.png render/render_$padFrame.png";
      print "$cmd\n";
      $cmd = `$cmd`;
    
      # resize the zoomed in portion of the image
      $cmd = "convert -resize 960x196! render/render_$padFrame.png " .
             "render/render_$padFrame.png";
      print "$cmd\n"; 
      $cmd = `$cmd`;
    
      if( $transP == 1 ){
        # overlay on white if desired 
        $cmd = "composite -compose over render/render_$padFrame.png " .
              "backgrounds/final_backwhiteimage.png render/render_$padFrame.png";
        print "$cmd \n";
        $cmd = `$cmd`;
      }# if transP active
      
      
      # increment frame counter
      $currFrame++;
      
      # increment the size of the image by the window size, so it extracts a 
      # increasingly large portion of the main image, if the border of the 
      # image has been reached, extract the entire image
      $sizeX = $sizeX + $frSizeX;
      $sizeY = $sizeY + $frSizeY;
      if( $sizeX > $im_width  ){ $sizeX = $im_width; }
      if( $sizeY > $im_height ){ $sizeY = $im_height; }
    
      # increment the crop area by half the window size, so it gets half the 
      # content above/left, and the other half below/right. Change this if 
      # you want to zoom out towards the upper left or lower right, etc.
      $cropX = $cropX - ($frSizeX/2);
      $cropY = $cropY - ($frSizeY/2);
      if( $cropX < 0 ){ $cropX = 0; }
      if( $cropY < 0 ){ $cropY = 0; }
    
    }#while frameCounter <= maxFrames
    
    sub createSmallWhiteBackground{
    
      # create a new image in true color for background only
      my $backimage = new GD::Image($sizeX,$sizeY);
    
      # initialize some colors
      my $white = $backimage->colorAllocate(255,255,255);
      my $black = $backimage->colorAllocate(0,0,0);
    
      # write the file to disk
      open(PNGOUT,"> backgrounds/final_backwhiteimage.png") or die
        "can't write to output file backgrounds/final_backwhiteimage.png";
        print PNGOUT $backimage->png;
      close(PNGOUT);
    
    }#createSmallWhiteBackground
  3. コマンド perl buildVideoFrames.pl 3520 720 120 20 1 avis で、最初のレンダリング・ステップを実行します。

上記のコマンドでは、最初の 2 つのオプションは、合成画像に解像度 3520x720を指定します。これには、ビデオの各フレームの背景画像、およびテキスト ELENAの前景画像が含まれます。次の 2 つのオプションは、最大の画像フレームと frameWindowです。最大の画像フレームは、すべてのビデオ・ソース・ファイル内のフレームの最小数より小さくしなければなりません。例えば、あるソース・ファイルに60 個しかフレームがない場合に、最大の画像フレーム・パラメーターとして 75を指定すると、最終レンダリング結果では、画像の一部が空白になります。

frameWindow パラメーターは、切り抜いた画像を何ピクセルまで拡張するかを表す、フレームあたりのピクセル数です。例えば、frameWindowパラメーターに 20 を指定すると、切り抜かれた内部の画像のフレームは、画像の幅に達するまでそれぞれ20 ピクセル広くなります。高さの frameWindow パラメーターは、画像の縦横比に関係なく両方の寸法でスムーズにズームアウトできるように、合成画像の指定された寸法に基づいて計算されます。

比較的大きな frameWindow パラメーターを使ったのは、見る人がモザイクのエフェクトの斬新さを理解できるよう、素早くズームアウトするためです。ビデオ・ソース・ファイルにたくさんフレームがある場合は、frameWindowパラメーターに 2 を指定して、ズームアウトのエフェクトを極端に遅くしてみてください。

残りの 2 つのオプションは、透明度フラグと画像タイルのディレクトリーです。背景画像をどうするか最後のエンコード・スキームに決定させるには、透明度フラグに0 を指定します。例えば、0 オプションで透明の背景を指定して、mpeg4 でエンコードすると、背景色は黒になります。最終的なムービーの背景色を白にするには、1を使用します。パラメーター avis は、単にすべての画像タイルのディレクトリー内で、画像タイルが保管されているディレクトリーの名前です。

最終的なムービーをエンコードする

buildVideoFrames.pl コマンドを実行すると、render ディレクトリーにはムービーの合成済みの各フレームが作成されます。あとは、コーデックを選択して、ビデオ・ストリームとしてエンコードするだけです。次のコマンドを使って最終レンダリング・ステップを実行します。

リスト 4. 最終レンダリング・ステップ
mencoder \
  "mf://render/*.png" -mf fps=17 \
  -ovc lavc \
  -lavcopts vcodec=mpeg4 \
  -o videoMosaic.mpeg

mencoder は mf://filemask というパラメーターを使います。この場合は、render ディレクトリー内のすべてのpng ファイルです。-mf fps=17 パラメーターは、1 秒に 17 個のフレームでムービーを作成するよう mencoderに伝えます。これは、ソース・ビデオ・ファイルの低いフレーム・レートに合わせるとよいようです。最終結果のアニメーションが自然に流れて見えるようにするには、この値を実験してみる必要があるかもしれません。

-ovc lavc パラメーターは、libavcodec コーデックを使うよう mencoder に伝えます。-lavcopts vcodec=mpeg4 は、ビデオのエンコードに mpeg4 コーデックを使うことを指定します。また、-ovideoMosaic.mpeg は、できあがったビデオを書き込むファイルです。

図 2 は、最終レンダリング処理を行ってできたフレーム例です。mpeg4 でエンコードされたビデオのモザイク処理の例については、ソース・コードをダウンロードしてください。

図 2. モザイクの結果の例
モザイクの結果の例
モザイクの結果の例

まとめ

最新のフレームワークとモザイク・ムービーの例を使うと、プロモーション・ビデオやホーム・ムービーの面白い例を作成することができます。前景画像に入れた白のピクセルは、背景フレームの上のマスクになることを覚えておいてください。この方法と、参考文献にリストされている静止画像のモザイクに関する記事のヒントを使うと、好みのテキストのビデオ・モザイク画像を作成することができます。


ダウンロード可能なリソース


関連トピック

  • 「Perl と ImageMagick を使ってモザイク画像を作る」を読んで、オープン・ソースのグラフィカル編集ツールである ImageMagick、GD、TheGimp の機能を学んでください。
  • ImageMagick.org で ImageMagick について学んでください。
  • GD の Web サイトで、GD とその API について学び、サンプル・コードを入手してください。
  • 最高の画像操作ソフトウェアが必要な場合は、The Gimp にアクセスしてください。
  • developerWorks の Open source ゾーンをご覧ください。オープン・ソース技術を使用した開発や、IBM 製品でオープン・ソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
  • CPAN で GD.pm などの Perl モジュールを入手してください。CPAN は、総合的な Perl アーカイブ・ネットワークです。
  • Tom Gilbert による画像ビューアー、feh をダウンロードしてください。
  • ダウンロードまたは DVD で入手可能な IBM トライアル・ソフトウェアを次のオープン・ソース開発プロジェクトに取り入れてください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=237329
ArticleTitle=Perl、ImageMagick、MPlayer を使ってモザイク・ムービーを作成する
publish-date=07112006