Perl で デジタル画像処理を学ぶ

私は仕事の関係でASICで画像処理を行うための回路設計に携わることがありました。
しかし、それまでハードウェアで画像処理という分野の関わりがなかったので、とにかく本を買って勉強することにしました。

digital_image_processing

この本は、評判通りのいい本でした。
レンズの仕組みからCMOSセンサーの話、そしてデジタル画像処理としてフィルターの色々の説明があり、私の欲しかった情報、知識を得ることが出来ました。

ただ、本を読むだけでは記憶に残りにくいので、なんとか本に書かれて内容を自ら体現したいと思いました。

もちろん、仕事を受けているのですから、少しは体現は出来るのですが、仕事だと、メンバーと作業を分担してますので、自分の担当している部分の設計に集中して、いまいち全体を理解するところまで行けない。
しかも、商品開発、画像処理速度も精度も要求を満たすものを作るのですから、最善を尽くし、理論の検証にはC/C++で行い、プロトタイプをFPGAで作り、C/C++の検証と比較するといったことが行われます。
そんなこと、個人の持つ環境で全体を学習することはなかなか出来ないのです。

そこで、なんとか Linux(Ubuntu) に入っているフリーソフトで出来ないかと探したところ、ImageMagick で JPEG から TEXT に変換する機能があることを知り、その変換された TEXT を Perl で処理できそうだと考えました。
ちなみに、ImageMagick について調べてみると GIF を作成するなど、いろいろ出来そうなので、気が向いたら試してみたいところです。

ImageMagick

ImageMagick を使って JPEG から TEXT への変換は以下のように実行します。

  SYNOPSIS
    convert [input-option] input-file [output-option] output-file
  EXAMPLE
    % convert venezia.jpg venezia.txt

変換した後の TEXT の中身は以下のようになっています。

ファイルの先頭には、コメント行のように見える情報が記載されています。
実際に ImageMagick で TEXT から他の形式に変換する時に使われる情報のようで、横のpixel数, 縦のpixel数, 65535階調, といった情報のようです。

2行目以降は、各pixelの情報で、座標位置, RGB 65535階調, RGB hex表記, RGB 256階調 になっているようです。


  # ImageMagick pixel enumeration: 555,416,65535,srgb
  0,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
  1,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
  2,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
  3,0: (1542,40606,56797) #069EDD srgb(6,158,221)
  4,0: (1799,40349,56026) #079DDA srgb(7,157,218)
  5,0: (2056,40092,56026) #089CDA srgb(8,156,218)
      ~ ~ ~ ~ ~ ~ ~ ~
  550,415: (23901,11565,1799) #5D2D07 srgb(93,45,7)
  551,415: (15420,2056,0) #3C0800 srgb(60,8,0)
  552,415: (21331,7196,0) #531C00 srgb(83,28,0)
  553,415: (25443,10280,0) #632800 srgb(99,40,0)
  554,415: (29812,14135,2570) #74370A srgb(116,55,10)

さて、この TEXT を読み取って 画素情報を配列に格納する perl プログラムを作りたいと思います。

また、この TEXT 読み取りプログラムは他のところでも使えるように サブルーチンにして、かつ、別ファイルにして 本体プログラムが use文 で読み取って使えるよう、ライブラリとして作りたいと思います。


lib/read_jpg :

  1 sub read_jpg {
  2   my $in_file = $_[0];
  3
  4   print "convert jpeg to text\n";
  5   `convert $in_file ../Temp/convert_from_jpg.txt`;
  6
  7   print "read text\n";
  8   open(IN_TEXT, "<", "../Temp/convert_from_jpg.txt") or die "##ERROR## Could not open input file : ../Temp/convert_from_jpg.txt\n";
  9   while (<IN_TEXT>) {
 10     if (/^# ImageMagick /) {
 11       chop;
 12       $header = $_;
 13       my @tmp = split / +/, $_;
 14       my @tmps = split /\,/, $tmp[4];
 15       $size_x = $tmps[0];
 16       $size_y = $tmps[1];
 17       print "size x:$size_x y:$size_y\n";
 18       $header_prefix = '# ImageMagick pixel enumeration: ';
 19       $header_sufix = ',65535,srgb';
 20     } else {
 21       my @tmp = split / +/, $_;
 22       $_ = $tmp[0];
 23       s/://;
 24       ($x, $y) = split /\,/, $_;
 25       $_ = $tmp[1];
 26       s/\(//;
 27       s/\)//;
 28       $org[$x][$y] = $_;
 29     }
 30   }
 31   close IN_TEXT;
 32 }
 33
 34 1;

上のサブルーチンは TEXT から画素情報を配列に取り込むプログラムです。
これを Path がわかり易いように 呼び出し元の perl プログラムの下のディレクトリ lib に read_jpg.pm として保存します。拡張子は .pm である必要があります。

このサブルーチンを呼び出すには、呼び出し元の perl プログラムに以下のように記述します。

  use lib '/home/user/Lab/ImageProcessing/Perl/lib';
  require read_jpg;

上のサブルーチンの最後 34行目に

  1;

があるのは 呼び出し側 perl プログラムが lib ファイルを正常に読み出せたと判断するために必要なようです。0 以外なら何でも良いそうですが、1; と書くのが一般的なようです。

それでは、上記の TEXT を読み取って配列に格納する perl サブルーチン プログラムについて解説していこうと思います。



サブルーチン 引数の受け取り

  1 sub read_jpg {
  2   my $in_file = $_[0];

perl のサブルーチンで引数を受け取ると、標準配列 @_ に格納されるとのこと。

上の例では、引数は 1つだけですので、1つ目の要素 $_[0]$in_file に代入しています。
また、上の例では、my 関数を使って $in_file を局所変数にしています。
局所変数とすることで $in_file は、このサブルーチンの中でのみ有効な変数となります。



Linux コマンド実行

ImageMagick の JPEG から TEXT への変換を行います。

  4   print "convert jpeg to text\n";
  5   `convert $in_file ../Temp/convert_from_jpg.txt`;

perl から Linux コマンドを実行する場合、system関数を使う方法もあるのですが、扱いに注意が必要なので、私は ` (バッククォート) で囲む記述をよく使います。

この記述なら、Linux コマンドの標準出力を受け取ることも出来ます。例えば以下の感じです。

  @list = `ls`;

これで ls コマンドを実行した結果である、ファイルリストは配列 @ls に格納されます。



テキストファイルの読み取り

ImageMagick が JPEG から変換したテキストファイルを読み取ります。

open から close までの一連の記述は以下のようになります。この一連の記述がテキストファイルの読み取りの定形です。

  8 open(IN_TEXT, "<", "../Temp/convert_from_jpg.txt") or
      die "##ERROR## Could not open input file : ../Temp/convert_from_jpg.txt\n";
  9 while (<IN_TEXT>) {
        ~ ~ ~ ~ ~
 30 }
 31 close IN_TEXT;

open 関数

  open(FileHandle, "Mode", "FileName") or die "Message";

    第1引数:ファイルハンドル名
    第2引数:モード
    第3引数:ファイル名
    or die は例外処理です。

モードの記号は主に3通りあります。

    "<" :読み取り
    ">" :書き込み (同じ名前のファイルがある場合は上書きされます。)
    ">>" :追加 書き込み (同じ名前のファイルがある場合はファイルの最後に追加されます。)



配列への取り込み

while文でファイルハンドラが有効な間、各行 1行ずつテキストを読み込んで行きます。
なお、読み込まれた各行 1行ずつは 特殊変数 $_ に格納されています。

その 1行ずつが格納された $_ に対して以下の処理を行い、必要な情報を整理して取り込みます。

 10     if (/^# ImageMagick /) {
 11       chop;
 12       $header = $_;
 13       my @tmp = split / +/, $_;
 14       my @tmps = split /\,/, $tmp[4];
 15       $size_x = $tmps[0];
 16       $size_y = $tmps[1];
 17       print "size x:$size_x y:$size_y\n";
 18       $header_prefix = '# ImageMagick pixel enumeration: ';
 19       $header_sufix = ',65535,srgb';
 20     } else {
 21       my @tmp = split / +/, $_;
 22       $_ = $tmp[0];
 23       s/://;
 24       ($x, $y) = split /\,/, $_;
 25       $_ = $tmp[1];
 26       s/\(//;
 27       s/\)//;
 28       $org[$x][$y] = $_;
 29     }

ところで、テキストファイルはこのような文体となっています。ここから必要な情報を取り込んでいきます。

  # ImageMagick pixel enumeration: 555,416,65535,srgb
  0,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
  1,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
  2,0: (1285,41120,57568) #05A0E0 srgb(5,160,224)
      ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

perl の 10行目の if文 は テキストファイルの最初の行の 画像情報 にパターンマッチした場合の処理をすることを意図しています。

11行目の chop; は 改行コードを除いてくれる関数で、引数を明示的に付けていないので $_ を引数として作用します。

12行目で改行コードが除かれた テキストファイルの最初の行を $header に格納しています。なお、$header はグローバル変数で、このサブルーチンが使われる親プログラムで $header に格納された文字列が使用されることを意図しています。

テキストファイルの最初の行には、画像のサイズ情報 横:555 縦:416 があり、これを取り込んでおきたいところです。まず、perl の 13行目において 複数スペース / +/ にマッチするところで 文字列 を分割させています。
分割された文字列は my関数で局所的かつ初期化された配列の宣言 @tmp に格納されます。

    #     ImageMagick   pixel     enumeration:   555,416,65535,srgb
  $tmp[0]   $tmp[1]     $tmp[2]     $tmp[3]       $tmp[4]

ここで $tmp[4] をカンマ で分割して、画サイズ情報を $size_x, $size_y に取り込んでいます。
なお、$size_x, $size_y も意図的にグローバル変数としています。

perl の 20行目の else文 以降のはテキストファイルの最初の行以外(つまり、pixelの座標と色の情報)を処理するプログラムになっています。テキストファイルには冗長な情報があるのですが、欲しいのは、

  0,0:     (1285,41120,57568)   #05A0E0   srgb(5,160,224)
  $tmp[0]     $tmp[1]           $tmp[2]     $tmp[3]

$tmp[0]:座標 と $tmp[1]:65536階調のRGB です。

座標情報は、x, y に分解して、2次元配列の要素番号とし、RGB情報はカンマ区切りが付いたまま分解はせずに2次元配列 $org[$x][$y] に格納しました。

以降、この配列に取り込んだ画像情報を使って画像処理をしていきたいと思います。


TITLE

自己紹介

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







Linux と 小ネタ

デジタル回路設計

海外駐在後記