平滑化フィルタ

averaging

平滑化は画像をぼかして滑らかにする処理です。

左図のように、ターゲットとなる pixel を中心に 3x3 もしくは 5x5 の枠で周りの pixel から色素値を集めて平均値を中心の色素値に置き換えます。

実際に平滑化を行った画像を以下に載せておきます。ぼかして滑らかになっているのが分かるかと思います。

sanma_original
原画
sanma_averaging
平滑化

サブルーチン

以下は平滑化フィルタのサブルーチンです。

ターゲットである $org[$i][$j] とその周辺の画素を RGB に分解してそれぞれに和算、平均値を算出して、整数化した後に RGB をまとめた画素値としてを返しています。

lib/averaging.pm :

  1  sub averaging {
  2 
  3      my $color;
  4 
  5      ($r[0], $g[0], $b[0]) = split /\,/, $org[$i-1][$j-1];  # up left
  6      ($r[1], $g[1], $b[1]) = split /\,/, $org[$i][$j-1];    # up center
  7      ($r[2], $g[2], $b[2]) = split /\,/, $org[$i+1][$j-1];  # up right
  8      ($r[3], $g[3], $b[3]) = split /\,/, $org[$i-1][$j];    # center left
  9      ($r[4], $g[4], $b[4]) = split /\,/, $org[$i][$j];      # center center
 10      ($r[5], $g[5], $b[5]) = split /\,/, $org[$i+1][$j];    # center right
 11      ($r[6], $g[6], $b[6]) = split /\,/, $org[$i-1][$j+1];  # down left
 12      ($r[7], $g[7], $b[7]) = split /\,/, $org[$i][$j+1];    # down center
 13      ($r[8], $g[8], $b[8]) = split /\,/, $org[$i+1][$j+1];  # down right
 14      $r_sum = 0; $g_sum = 0; $b_sum = 0;
 15      for ($k = 0; $k < 9; $k++) {
 16          $r_sum = $r_sum + $r[$k];
 17          $g_sum = $g_sum + $g[$k];
 18          $b_sum = $b_sum + $b[$k];
 19      }
 20      $r_ave = $r_sum / 9;
 21      $g_ave = $g_sum / 9;
 22      $b_ave = $b_sum / 9;
 23      $r_ave_i = int($r_ave);
 24      $g_ave_i = int($g_ave);
 25      $b_ave_i = int($b_ave);
 26      $color = "$r_ave_i" . ',' . "$g_ave_i" . ',' . "$b_ave_i";
 27      return $color;
 28  }
 29 
 30  1;

親プログラム

上の平滑化フィルタのサブルーチンを使って、実際の画像処理をするのが以下のプログラムです。

averaging.pl :

  1  #!/usr/bin/perl
  2 
  3  use Getopt::Long;
  4 
  5  use lib '/home/user/Lab/ImageProcessing/Perl/lib';
  6  require read_jpg;
  7  require averaging;
  8 
  9  sub print_synopsis {
 10      print "Options : \n";
 11      print "  --input_file : \n";
 12      print "      input original file name. \n";
 13      print "  --nodisplay : \n";
 14      print "      do not display convertion result.\n";
 15  }
 16 
 17  &GetOptions(
 18      'input_file=s' => \$in_file,
 19      'nodisplay' => \$in_nodisp,
 20      );
 21 
 22  if (!defined($in_file)) {
 23      print "##ERROR## input file is not defined\n";
 24      &print_synopsis;
 25      exit;
 26  }
 27 
 28  &read_jpg($in_file);
 29 
 30  print "averaging\n";
 31  for ($j = 0; $j < $size_y; $j++) {
 32      for ($i = 0; $i < $size_x; $i++) {
 33          if (($i < 1) || ($i >= ($size_x - 1)) || ($j < 1) || ($j >= ($size_y - 1))) {
 34              $average[$i][$j] = $org[$i][$j];
 35          } else {
 36              $average[$i][$j] = &averaging();
 37          }
 38      }
 39  }
 40 
 41  print "output text file\n";
 42  open(OUT_TEXT, ">", "../Temp/averaging.txt") or die "##ERROR## Could not open output file : ../Temp/averaging.txt";
 43  print OUT_TEXT "$header\n";
 44  for ($y = 0; $y < $size_y; $y++) {
 45      for ($x = 0; $x < $size_x; $x++) {
 46          $state = "$x" . ',' . "$y" . ': ' . '(' . "$average[$x][$y]" . ')';
 47          print OUT_TEXT "$state\n";
 48      }
 49  }
 50  close OUT_TEXT;
 51 
 52  print "convert text to jpeg\n";
 53  `convert ../Temp/averaging.txt ../Temp/averaging.jpg`;
 54 
 55  if (!defined($in_nodisp)) {
 56      if ( ($size_x <= 1920) && ($size_y <= 1080) ) {
 57          print "display\n";
 58          `display ../Temp/averaging.jpg`;
 59      } else {
 60          print "jpeg size is too large, open with gimp\n";
 61      }
 62  }

親プログラムについて解説しようと思います。


Getopt::Long

親プログラムに対して引数を与えて、動作のオプションを付けるため、標準ライブラリ Getopt::Long を使います。

  3  use Getopt::Long;

これで、Getopt::Long を使うことを宣言して、下記のように標準関数 GetOptions でオプションスイッチを定義して引数を受け取ります。

 17  &GetOptions(
 18      'input_file=s' => \$in_file,
 19      'nodisplay' => \$in_nodisp,
 20      );

Getopt::Std もあるのですが、Getopt::Long の利点は オプションスイッチを省略形に出来ること。つまり、--input_file と スイッチを入力しなくても、-in でもよい ということです。

具体的には、以下の 親プログラム 実行コマンドはどちらも同じとなります。

 % averaging.pl --input_file ../Sample/JPEG/forest.jpg --nodisplay
 % averaging.pl -in ../Sample/JPEG/forest.jpg -nodisp

引数の型の指定について、 'input_file=s' の =s は --input_file につづく 引数が string (文字列) であることを指定しており、 'nodisplay' は 真偽値 であることを表しており、引数を必要とせず、--nodisplay が宣言されたことで 真 、宣言されていないと 偽 となります。 =s : string (文字列) の他に =i : integer (整数) 、 =f : floot (実数) があります。


use lib

サブルーチンはそれぞれ個別のファイルにして、親プログラム が必要に応じて読み出せるようにすると便利です。以下が 親プログラム でサブルーチンを使うことを宣言している部分です。

use lib で サブルーチン の格納されているディレクトリを示し、 require でどの サブルーチン を使用するか宣言します。

サブルーチン 実態は、拡張子を .pm として 最後の行には 1; を記述して lib ディレクトリに格納します。

  5  use lib '/home/user/Lab/ImageProcessing/Perl/lib';
  6  require read_jpg;
  7  require averaging;

最後に

 41  print "output text file\n";
 42  open(OUT_TEXT, ">", "../Temp/averaging.txt") or die
         
"##ERROR## Could not open output file : ../Temp/averaging.txt";
 43  print OUT_TEXT "$header\n";
 44  for ($y = 0; $y < $size_y; $y++) {
 45      for ($x = 0; $x < $size_x; $x++) {
 46          $state = "$x" . ',' . "$y" . ': ' . '(' . "$average[$x][$y]" . ')';
 47          print OUT_TEXT "$state\n";
 48      }
 49  }
 50  close OUT_TEXT;
 51 
 52  print "convert text to jpeg\n";
 53  `convert ../Temp/averaging.txt ../Temp/averaging.jpg`;
 54 
 55  if (!defined($in_nodisp)) {
 56      if ( ($size_x <= 1920) && ($size_y <= 1080) ) {
 57          print "display\n";
 58          `display ../Temp/averaging.jpg`;
 59      } else {
 60          print "jpeg size is too large, open with gimp\n";
 61      }
 62  }

41行目 から 50行目 は ImageMagick で読み取り可能な TEXT形式 で画像データをファイルに出力しています。

53行目 は ImageMagick で TEXT から JPEG への変換を行っています。

55行目 から 62行目 では 変換された画像を確認するために ImageMagick の display コマンドを実行しています。これには オプション が付いており、 averaging.pl 実行時に --nodisplay を付けることで画像を表示しないで終了することも出来るようにしています。



TITLE

自己紹介

50才になる半導体エンジニアです。
大学で電子電気工学を学び、1990年にその分野のまま就職。ASICやマイコンの設計を長く続けてきましたが20年も同じ分野にいると業態も衰退したり変化するもので退職し、今は外資のIT系会社に再就職して設計請負業をやっております。
お問い合わせは
nakata.xianzhi@outlook.com







Linux と 小ネタ

デジタル回路設計

海外駐在後記