局所近隣平滑化フィルタ

near_ave

中心のターゲット画素の対し、ターゲット画素の9通りの近傍領域中でその中の画素値の分散が最小になる領域を選び、その平均値をターゲット画素と置き換える。

すると以下のように印象派絵画のとうなフィルタ効果を得ることが出来る。

sanma_original
原画
sanma_near_averaging
局所近隣平滑化

サブルーチン

0:左上、1:上、2:右上、3:右、4:右下、5:下、6:左下、7:左、8:中 のエリアの中からターゲットの画素と最も近い画素で出来ているエリアを見付けるため、RGBを一旦グレイスケールに変換します。

3値のRGBで近似の画素エリアを探すより、1値であるグレイスケールの方が近似エリアを決定し易いからです。

lib/rgb2gray.pm :

  1  sub rgb2gray {
  2    print "gray scaling\n";
  3    for ($j = 0; $j < $size_y; $j++) {
  4      for ($i = 0; $i < $size_x; $i++) {
  5        @tmp = split /\,/, $org[$i][$j];
  6        $rgb2gray = ( ($tmp[0] * 0.299) + ($tmp[1] * 0.578) + ($tmp[2] * 0.114) );
  7        $rgb2gray_i = int($rgb2gray);
  8        $gray[$i][$j] = $rgb2gray_i;
  9      }
 10    }
 11  }
 12  1;

以下が局所近隣平滑化フィルタによってターゲットの画素値を求めるサブルーチンです。

29行目〜67行目でグレイスケールを使って各エリアの平均値とターゲットとの差が最も小さくなるエリアを探しています。
探し出した最も近いエリアによって、その平均値をRGBで計算してターゲットの画素と置き換えるのが69行目以降になっています。

lib/near_part_averaging.pm :

   1  sub near_part_averaging {
   2 
   3    ($r[0],  $g[0],  $b[0] ) = split /\,/, $org[$i-2][$j-2];
   4    ($r[1],  $g[1],  $b[1] ) = split /\,/, $org[$i-1][$j-2];
   5    ($r[2],  $g[2],  $b[2] ) = split /\,/, $org[$i][$j-2];
   6    ($r[3],  $g[3],  $b[3] ) = split /\,/, $org[$i+1][$j-2];
   7    ($r[4],  $g[4],  $b[4] ) = split /\,/, $org[$i+2][$j-2];
   8    ($r[5],  $g[5],  $b[5] ) = split /\,/, $org[$i-2][$j-1];
   9    ($r[6],  $g[6],  $b[6] ) = split /\,/, $org[$i-1][$j-1];
  10    ($r[7],  $g[7],  $b[7] ) = split /\,/, $org[$i][$j-1];
  11    ($r[8],  $g[8],  $b[8] ) = split /\,/, $org[$i+1][$j-1];
  12    ($r[9],  $g[9],  $b[9] ) = split /\,/, $org[$i+2][$j-1];
  13    ($r[10], $g[10], $b[10]) = split /\,/, $org[$i-2][$j];
  14    ($r[11], $g[11], $b[11]) = split /\,/, $org[$i-1][$j];
  15    ($r[12], $g[12], $b[12]) = split /\,/, $org[$i][$j];
  16    ($r[13], $g[13], $b[13]) = split /\,/, $org[$i+1][$j];
  17    ($r[14], $g[14], $b[14]) = split /\,/, $org[$i+2][$j];
  18    ($r[15], $g[15], $b[15]) = split /\,/, $org[$i-2][$j+1];
  19    ($r[16], $g[16], $b[16]) = split /\,/, $org[$i-1][$j+1];
  20    ($r[17], $g[17], $b[17]) = split /\,/, $org[$i][$j+1];
  21    ($r[18], $g[18], $b[18]) = split /\,/, $org[$i+1][$j+1];
  22    ($r[19], $g[19], $b[19]) = split /\,/, $org[$i+2][$j+1];
  23    ($r[20], $g[20], $b[20]) = split /\,/, $org[$i-2][$j+2];
  24    ($r[21], $g[21], $b[21]) = split /\,/, $org[$i-1][$j+2];
  25    ($r[22], $g[22], $b[22]) = split /\,/, $org[$i][$j+2];
  26    ($r[23], $g[23], $b[23]) = split /\,/, $org[$i+1][$j+2];
  27    ($r[24], $g[24], $b[24]) = split /\,/, $org[$i+2][$j+2];
  28 
  29    $ave_area[0] = ( ($gray[$i-2][$j-2] + $gray[$i-1][$j-2] +
  30                      $gray[$i-2][$j-1] + $gray[$i-1][$j-1] + $gray[$i][$j-1]   +
  31                                          $gray[$i-1][$j]   + $gray[$i][$j]     ) / 7);
  32    $ave_area[1] = ( ($gray[$i-1][$j-2] + $gray[$i][$j-2]   + $gray[$i+1][$j-2] +
  33                      $gray[$i-1][$j-1] + $gray[$i][$j-1]   + $gray[$i+1][$j-1] +
  34                                          $gray[$i][$j]                         ) / 7);
  35    $ave_area[2] = ( (                    $gray[$i+1][$j-2] + $gray[$i+2][$j-2] +
  36                      $gray[$i][$j-1]   + $gray[$i+1][$j-1] + $gray[$i+2][$j-1] +
  37                      $gray[$i][$j]     + $gray[$i+1][$j]                       ) / 7);
  38    $ave_area[3] = ( (                    $gray[$i+1][$j-1] + $gray[$i+2][$j-1] +
  39                      $gray[$i][$j]     + $gray[$i+1][$j]   + $gray[$i+2][$j]   +
  40                                          $gray[$i+1][$j+1] + $gray[$i+2][$j+1] ) / 7);
  41    $ave_area[4] = ( ($gray[$i][$j]     + $gray[$i+1][$j]   +
  42                      $gray[$i][$j+1]   + $gray[$i+1][$j+1] + $gray[$i+2][$j+1] +
  43                                          $gray[$i+1][$j+2] + $gray[$i+2][$j+2] ) / 7);
  44    $ave_area[5] = ( (                    $gray[$i][$j]     +
  45                      $gray[$i-1][$j+1] + $gray[$i][$j+1]   + $gray[$i+1][$j+1] +
  46                      $gray[$i-1][$j+2] + $gray[$i][$j+2]   + $gray[$i+1][$j+2] ) / 7);
  47    $ave_area[6] = ( (                    $gray[$i-1][$j]   + $gray[$i][$j]     +
  48                      $gray[$i-2][$j+1] + $gray[$i-1][$j+1] + $gray[$i][$j+1]   +
  49                      $gray[$i-2][$j+2] + $gray[$i-1][$j+2]                     ) / 7);
  50    $ave_area[7] = ( ($gray[$i-2][$j-1] + $gray[$i-1][$j-1] +
  51                      $gray[$i-2][$j]   + $gray[$i-1][$j]   + $gray[$i][$j]     +
  52                      $gray[$i-2][$j+1] + $gray[$i-1][$j+1]                     ) / 7);
  53    $ave_area[8] = ( ($gray[$i-1][$j-1] + $gray[$i][$j-1]   + $gray[$i+1][$j-1] +
  54                      $gray[$i-1][$j]   + $gray[$i][$j]     + $gray[$i+1][$j]   +
  55                      $gray[$i-1][$j+1] + $gray[$i][$j+1]   + $gray[$i+1][$j+1] ) / 9);
  56 
  57    $target = $gray[$i][$j];
  58 
  59    $sub_c = 65535;
  60    for ($c = 0; $c < 9; $c++) {
  61      $sub = $target - $ave_area[$c];
  62      $sub_a = abs($sub);
  63      if ($sub_a < $sub_c) {
  64        $sub_c = $sub_a;
  65        $n = $c;
  66      }
  67    }
  68 
  69    if ($n == 0) {
  70      $r_ave = ( ($r[0]  + $r[1]  +
  71                  $r[5]  + $r[6]  + $r[7]  +
  72                           $r[11] + $r[12] ) / 7);
  73      $g_ave = ( ($g[0]  + $g[1]  +
  74                  $g[5]  + $g[6]  + $g[7]  +
  75                           $g[11] + $g[12] ) / 7);
  76      $b_ave = ( ($b[0]  + $b[1]  +
  77                  $b[5]  + $b[6]  + $b[7]  +
  78                           $b[11] + $b[12] ) / 7);
  79    }
  80    if ($n == 1) {
  81      $r_ave = ( ($r[1]  + $r[2]  + $r[3]  +
  82                  $r[6]  + $r[7]  + $r[8]  +
  83                           $r[12]          ) / 7);
  84      $g_ave = ( ($g[1]  + $g[2]  + $g[3]  +
  85                  $g[6]  + $g[7]  + $g[8]  +
  86                           $g[12]          ) / 7);
  87      $b_ave = ( ($b[1]  + $b[2]  + $b[3]  +
  88                  $b[6]  + $b[7]  + $b[8]  +
  89                           $b[12]          ) / 7);
  90    }
  91    if ($n == 2) {
  92      $r_ave = ( (         $r[3]  + $r[4]  +
  93                  $r[7]  + $r[8]  + $r[9]  +
  94                  $r[12] + $r[13]          ) / 7);
  95      $g_ave = ( (         $g[3]  + $g[4]  +
  96                  $g[7]  + $g[8]  + $g[9]  +
  97                  $g[12] + $g[13]          ) / 7);
  98      $b_ave = ( (         $b[3]  + $b[4]  +
  99                  $b[7]  + $b[8]  + $b[9]  +
 100                  $b[12] + $b[13]          ) / 7);
 101    }
 102    if ($n == 3) {
 103      $r_ave = ( (         $r[8]  + $r[9]  +
 104                  $r[12] + $r[13] + $r[14] +
 105                           $r[18] + $r[19] ) / 7);
 106      $g_ave = ( (         $g[8]  + $g[9]  +
 107                  $g[12] + $g[13] + $g[14] +
 108                           $g[18] + $g[19] ) / 7);
 109      $b_ave = ( (         $b[8]  + $b[9]  +
 110                  $b[12] + $b[13] + $b[14] +
 111                           $b[18] + $b[19] ) / 7);
 112    }
 113    if ($n == 4) {
 114      $r_ave = ( ($r[12] + $r[13] +
 115                  $r[17] + $r[18] + $r[19] +
 116                           $r[23] + $r[24] ) / 7);
 117      $g_ave = ( ($g[12] + $g[13] +
 118                  $g[17] + $g[18] + $g[19] +
 119                           $g[23] + $g[24] ) / 7);
 120      $b_ave = ( ($b[12] + $b[13] +
 121                  $b[17] + $b[18] + $b[19] +
 122                           $b[23] + $b[24] ) / 7);
 123    }
 124    if ($n == 5) {
 125      $r_ave = ( (         $r[12] +
 126                  $r[16] + $r[17] + $r[18] +
 127                  $r[21] + $r[22] + $r[23] ) / 7);
 128      $g_ave = ( (         $g[12] +
 129                  $g[16] + $g[17] + $g[18] +
 130                  $g[21] + $g[22] + $g[23] ) / 7);
 131      $b_ave = ( (         $b[12] +
 132                  $b[16] + $b[17] + $b[18] +
 133                  $b[21] + $b[22] + $b[23] ) / 7);
 134      }
 135    if ($n == 6) {
 136      $r_ave = ( (         $r[11] + $r[12] +
 137                  $r[15] + $r[16] + $r[17] +
 138                  $r[20] + $r[21]          ) / 7);
 139      $g_ave = ( (         $g[11] + $g[12] +
 140                  $g[15] + $g[16] + $g[17] +
 141                  $g[20] + $g[21]          ) / 7);
 142      $b_ave = ( (         $b[11] + $b[12] +
 143                  $b[15] + $b[16] + $b[17] +
 144                  $b[20] + $b[21]          ) / 7);
 145    }
 146    if ($n == 7) {
 147      $r_ave = ( ($r[5]  + $r[6]  +
 148                  $r[10] + $r[11] + $r[12] +
 149                  $r[15] + $r[16]          ) / 7);
 150      $g_ave = ( ($g[5]  + $g[6]  +
 151                  $g[10] + $g[11] + $g[12] +
 152                  $g[15] + $g[16]          ) / 7);
 153      $b_ave = ( ($b[5]  + $b[6]  +
 154                  $b[10] + $b[11] + $b[12] +
 155                  $b[15] + $b[16]          ) / 7);
 156    }
 157    if ($n == 8) {
 158      $r_ave = ( ($r[6]  + $r[7]  + $r[8]  +
 159                  $r[11] + $r[12] + $r[13] +
 160                  $r[16] + $r[17] + $r[18] ) / 9);
 161      $g_ave = ( ($g[6]  + $g[7]  + $g[8]  +
 162                  $g[11] + $g[12] + $g[13] +
 163                  $g[16] + $g[17] + $g[18] ) / 9);
 164      $b_ave = ( ($b[6]  + $b[7]  + $b[8]  +
 165                  $b[11] + $b[12] + $b[13] +
 166                  $b[16] + $b[17] + $b[18] ) / 9);
 167    }
 168    $r_ave_i = int($r_ave);
 169    $g_ave_i = int($g_ave);
 170    $b_ave_i = int($b_ave);
 171    $color = "$r_ave_i" . ',' . "$g_ave_i" . ',' . "$b_ave_i";
 172    return $color;
 173  }
 174 
 175  1;

親プログラム

上の局所近隣平滑化フィルタのサブルーチンを使って、実際の画像処理をするのが以下のプログラムです。前ページの鮮鋭化フィルタと同じ構造で、サブルーチンとして sharping となっていた箇所を near_part_averaging に書き換えただけです。

near_part_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 rgb2gray;
   8  require near_part_averaging;
   9  require apply_area;
  10 
  11  sub print_synopsis {
  12    print "Options : \n";
  13    print "  --input_file : \n";
  14    print "      input original file name.\n";
  15    print "  --shape : \n";
  16    print "      shape of filer area either s as square or c as circle.\n";
  17    print "      if not defined, n is set to this option.\n";
  18    print "  --center : \n";
  19    print "      center position x,y\n";
  20    print "      if not defined, 960,540 is set to this option.\n";
  21    print "  --volume : \n";
  22    print "      filter area size. distance from center.\n";
  23    print "      if not defined, 1400 is set to this option.\n";
  24    print "  --xratio : \n";
  25    print "      ratio of x size of filter area\n";
  26    print "      if not defined, 1 is set to this option.\n";
  27    print "  --eio : \n";
  28    print "      filter effective area either i as inside or o as outside.\n";
  29    print "      if not defined, o is set to this option.\n";
  30  }
  31 
  32  GetOptions(
  33    'input_file=s' => \$in_file,
  34    'shape=s' => \$in_shape,
  35    'center=s' => \$in_center,
  36    'volume=i' => \$in_volume,
  37    'xratio=f' => \$in_xratio,
  38    'eio=s' => \$in_eio,
  39    'nodisp' => \$in_nodisp,
  40  );
  41 
  42  if (!defined($in_file)) {
  43    print "##ERROR## input file has not been defined\n";
  44    &print_synopsis;
  45    exit;
  46  }
  47 
  48  if (!defined($in_shape)) {
  49    print "shape of averaging area is not defined\n";
  50    $shape = 'n';
  51  } else {
  52    if (!(($in_shape eq "s")||($in_shape eq "c"))) {
  53      print "##ERROR## invalid shape is defined. define s as square or c as circle\n";
  54      exit;
  55    } else {
  56      $shape = $in_shape;
  57    }
  58  }
  59 
  60  if (!defined($in_center)) {
  61    $center_x = 960;
  62    $center_y = 540;
  63  } else {
  64    @tmp_center = split /\,/, $in_center;
  65    $center_x = $tmp_center[0];
  66    $center_y = $tmp_center[1];
  67  }
  68 
  69  if (!defined($in_volume)) {
  70    $volume = 1400;
  71  } else {
  72    $volume = $in_volume;
  73  }
  74 
  75  if (!defined($in_xratio)) {
  76    $xratio = 1;
  77  } else {
  78    $xratio = $in_xratio;
  79  }
  80 
  81  if (!defined($in_eio)) {
  82    $eio = 'o';
  83  } else {
  84    if (!(($in_eio eq "i")||($in_eio eq "o"))) {
  85      print "##ERROR## invalid effective area is defined. define i as inside or o as outside\n";
  86      exit;
  87    } else {
  88      $eio = $in_eio;
  89    }
  90  }
  91 
  92  &read_jpg($in_file);
  93 
  94  &rgb2gray();
  95 
  96  print "near averaging\n";
  97  for ($j = 0; $j < $size_y; $j++) {
  98    for ($i = 0; $i < $size_x; $i++) {
  99      if (($i < 2) || ($i >= ($size_x - 2)) || ($j < 2) || ($j >= ($size_y - 2))) {
 100        $near_part_average[$i][$j] = $org[$i][$j];
 101      } else {
 102        if ($shape eq "n") {
 103          $near_part_average[$i][$j] = &near_part_averaging;
 104        } else {
 105          $apply_calc = &apply_area($shape, $center_x, $center_y, $volume, $xratio, $eio, $i, $j);
 106          if ($apply_calc == 1) {
 107            $near_part_average[$i][$j] = &near_part_averaging;
 108          } else {
 109            $near_part_average[$i][$j] = $org[$i][$j];
 110          }
 111        }
 112      }
 113    }
 114  }
 115 
 116  print "output text file\n";
 117  open(OUT_TEXT, ">", "../Temp/near_part_averaging.txt") or die
          "##ERROR## Could not open output file : ../Temp/near_part_averaging.txt";
 118  print OUT_TEXT "$header\n";
 119  for ($y = 0; $y < $size_y; $y++) {
 120    for ($x = 0; $x < $size_x; $x++) {
 121      $state = "$x" . ',' . "$y" . ': ' . '(' . "$near_part_average[$x][$y]" . ')';
 122      print OUT_TEXT "$state\n";
 123    }
 124  }
 125  close OUT_TEXT;
 126 
 127  print "convert text to jpeg\n";
 128  `convert ../Temp/near_part_averaging.txt ../Temp/near_part_averaging.jpg`;
 129 
 130  if (!defined($in_nodisp)) {
 131    if ( ($size_x <= 1920) && ($size_y <= 1080) ) {
 132      print "display\n";
 133      `display ../Temp/near_part_averaging.jpg`;
 134    } else {
 135      print "jpeg size is too large, open with gimp\n";
 136    }
 137  }
 138 

親プログラムについての解説は、前ページの鮮鋭化フィルタと同じですので省略します。




TITLE

自己紹介

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







Linux と 小ネタ

デジタル回路設計

海外駐在後記