2006年声優言及数(6)枠線範囲の特定

ようやくlibpngの話が終わって、グラフの解析に。まずやるべきは、グラフの枠線の範囲を特定することでしょう。本当は決めうちでハードコーディングしたかったんだけど、どうやらグラフによって枠線の範囲が違ってくるようなので、毎回測定することにします。
原理は簡単で、上下(横方向)左右(縦方向)の端から画像を見ていって、長い線があれば枠線と判断する・・・というもの。

#define SEQ_TO_REGARD_AS_BORDER 50

typedef struct _graph_area{
     png_bytepp image;
     int width;
     int height;
     int top;
     int left;
     int bottom;
     int right;
} graph_area;

void get_graph_border(graph_area *g)
{
     int i, j;
     png_bytep buf = (png_bytep)malloc(g->height * sizeof(png_byte)); /* buffer for column */

     for(i=0, g->top=-1; i<g->height; i++){
          if(find_sequence(g->image[i], g->width, SEQ_TO_REGARD_AS_BORDER)){
               g->top = i;
               break;
          }
     }
     for(i=g->height-1, g->bottom=-1; i>=0; i--){
          if(find_sequence(g->image[i], g->width, SEQ_TO_REGARD_AS_BORDER)){
               g->bottom = i;
               break;
          }
     }
     for(i=0, g->left=-1; i<g->width; i++){
          for(j=0; j<g->height; j++){
               buf[j]=g->image[j][i];
          }
          if(find_sequence(buf, g->height, SEQ_TO_REGARD_AS_BORDER)){
               g->left = i;
               break;
          }
     }
     for(i=g->width-1, g->right=-1; i>=0; i--){
          for(j=0; j<g->height; j++){
               buf[j]=g->image[j][i];
          }
          if(find_sequence(buf, g->height, SEQ_TO_REGARD_AS_BORDER)){
               g->right = i;
               break;
          }
     }
     free(buf);
     printf("graph border: t=%d b=%d l=%d r=%d\n", g->top, g->bottom, g->left, g->right);
}
int find_sequence(png_bytep image, int size, int n)
{
     int i, cnt, prev=image[0];
     for(i=1; i<size; i++){
          if(image[i] != 0){
               if(prev == 0)  cnt = 0;
               if(++cnt >= n) return 1;
          }
          prev = image[i];
     }
     return 0;
}

今後役立ちそうなのでgraph_area構造体を定義しました。

実行結果は次の通り。ディレクトリ"../l/"には、リストアップした声優のグラフ画像が保存されてます。ファイル名はキーワードをURIエスケープした文字列で、例えば後藤邑子なら「%B8%E5%C6%A3%CD%B8%BB%D2」がファイル名に付いてます。

$ ls ../l/*.png | while read file
> do
> echo $file | perl ../../decode.pl
> ./png $file
> echo
> done > border.list

最後のechoは、paragraphに区切ることでパラグラフ単位のgrepをするため。

$ grep "^graph border:" border.list | sort | uniq -c | sort -r
    285 graph border: t=17 b=161 l=32 r=389
    191 graph border: t=17 b=161 l=21 r=389
    136 graph border: t=17 b=161 l=27 r=389
    120 graph border: t=17 b=161 l=26 r=389
      3 graph border: t=17 b=161 l=31 r=389

ほう。上下はみんな同じだけど、左右の幅が違うようですが、これは後述の縦軸のMAX値と関係しています。それよりも、3つしか出てない枠線を持つグラフが気になりますが・・・。

$ pgrep "graph border: t=17 b=161 l=31 r=389" border.list
../l/茅原実里.html.png
input file: [../l/%B3%FD%B8%B6%BC%C2%CE%A4.html.png]
png signature: 89 50 4e 47 0d 0a 1a 0a
graph border: t=17 b=161 l=31 r=389

../l/後藤邑子.html.png
input file: [../l/%B8%E5%C6%A3%CD%B8%BB%D2.html.png]
png signature: 89 50 4e 47 0d 0a 1a 0a
graph border: t=17 b=161 l=31 r=389

../l/田村ゆかり.html.png
input file: [../l/%C5%C4%C2%BC%A4%E6%A4%AB%A4%EA.html.png]
png signature: 89 50 4e 47 0d 0a 1a 0a
graph border: t=17 b=161 l=31 r=389

いずれもMAXが100の声優さんたちのようです。ちなみにpgrepはプロセスのgrepじゃなくてgrep -pへのエイリアス*1

どうでもいい余談

最初SEQ_TO_REGARD_AS_BORDERの値を「10くらいでいいだろ」って思ってたけど、「黒柳徹子」が名前だけで横線10pixel超えてしまうことが発覚。この辺の値も結局経験則ですね・・・。ってか、そもそも黒柳徹子は声優なのか・・・?

*1:perlで実装したもの。grep -pってGNUの実装にはないのかな・・・?