画像を「色別」してみる〜(プログラムの話)


えーと、たぶん、このエントリの前後にも書いてると思うんだけど、「はてなフォトライフ」の「色別」みたいなことしてみたかったんですよ。

このエントリは、どーやってやってみたのか。そのプログラムについて語っちゃおうかな〜ってカンジですよ! うっひゃーwww

・・・・と言いつつも、

なんか書いてて疲れてきたので(マジで風邪引いてて全身だるいのdeath)使ったperlソースべた貼りします。

そのうち、コメント書きますかね・・・。

「こうやった方がいいぜ!」的なものがあったら教えていただけると嬉しいです。

ソースさん(classification.pl)

#!perl

 use strict;
 use GD;

 # ↓テーブルヘッダみたいな
 my $starttimes = time;
 print '[start] '.$starttimes."\n";
 print "file:\twidth\theight\t";
 print "hsv:1st\thsv:2nd\thsv:3rd\t";
 print "rgb:1st\trgb:2nd\trgb:3rd\t";
 print "len:1st\tlen:2nd\tlen:3rd\t";
 print "\n";
 my $count = 0;

 # ファイル名渡して回す〜
 my @files = glob('*.jpg'); # 調査対象指定♪
 foreach(@files) {
  $count += &check_classification($_);
 }
 if(! $count) {exit;}

 # ↓テーブルフッタみたいな
 my $endtimes = time;
 my $stay = $endtimes - $starttimes;
 my $one = $stay / $count;
 print '[end] '.$endtimes."\n";
 print ' -> '.$stay.'sec/'.$count.'files ('.$one.'sec@1file)'."\n";

 exit;

### 以下はサブルーチンさん

sub check_classification { # 名前悪すぎwww
 my($fno, $file) = @_;
 my @c = (
  ['blacK ', 0, 0, 0],
  ['Blue  ', 0, 0, 0],
  ['Green ', 0, 0, 0],
  ['Cyan  ', 0, 0, 0],
  ['Red   ', 0, 0, 0],
  ['Purple', 0, 0, 0],
  ['Yellow', 0, 0, 0],
  ['White ', 0, 0, 0],
 ); # hsv[x][1], rgb[x][2], length[x][3] となっとります

 print $fno."\t".$file."\t";
 GD::Image->trueColor(1);
 my $image = GD::Image->new($file);
 if(! $image) {print "failure...\n"; return(0);} # 失敗したら0

 my($width, $height) = $image->getBounds();
 print $width."\t".$height."\t";

 for(my $y = 0; $y < $height; $y++) {
  for(my $x = 0; $x < $width; $x++) {
   my($r, $g, $b) = $image->rgb($image->getPixel($x, $y));
   my($h, $s, $v) = &rgb_to_hsv($r, $g, $b);

   # HSV投票 白と黒の判別は・・・テキトーです
   if   ($v <  32) {$c[0][1]++;}
   elsif($s <  12) {$c[7][1]++;}
   elsif($h <  30) {$c[4][1]++;}
   elsif($h <  90) {$c[6][1]++;}
   elsif($h < 150) {$c[2][1]++;}
   elsif($h < 210) {$c[3][1]++;}
   elsif($h < 270) {$c[1][1]++;}
   elsif($h < 330) {$c[5][1]++;}
   else            {$c[4][1]++;}

   # RGB投票
   $c[&is_over($r)*4+&is_over($g)*2+&is_over($b)][2]++;

   # Length測定(全累計)
   $c[0][3] += &cal_length_rgb($r,$g,$b,   0,  0,  0);
   $c[1][3] += &cal_length_rgb($r,$g,$b,   0,  0,255);
   $c[2][3] += &cal_length_rgb($r,$g,$b,   0,255,  0);
   $c[3][3] += &cal_length_rgb($r,$g,$b,   0,255,255);
   $c[4][3] += &cal_length_rgb($r,$g,$b, 255,  0,  0);
   $c[5][3] += &cal_length_rgb($r,$g,$b, 255,  0,255);
   $c[6][3] += &cal_length_rgb($r,$g,$b, 255,255,  0);
   $c[7][3] += &cal_length_rgb($r,$g,$b, 255,255,255);
  }
 }

 # RGBカラーのものは白と黒の評価をテキトーに下げとく
 $c[0][2] /= (2**3);
 $c[7][2] /= (2**3);
 $c[0][3] *= (2);
 $c[7][3] *= (2);

 # HSV要素でソートしますよ(降順?)
 @c = sort {@$b[1] <=> @$a[1]} @c;
 for(my $i = 0; $i < 3; $i++) {print $c[$i][0]."\t";}

 # RGB要素でソートしますよ(降順?)
 @c = sort {@$b[2] <=> @$a[2]} @c;
 for(my $i = 0; $i < 3; $i++) {print $c[$i][0]."\t";}

 # length(RGB)要素でソートしますよ(昇順?)
 @c = sort {@$a[3] <=> @$b[3]} @c;
 for(my $i = 0; $i < 3; $i++) {print $c[$i][0]."\t";}

 print "\n";
 return(1); # ここまで来たら成功扱いで1を返しときます
}


sub rgb_to_hsv { # 書き方に統一感がなくてスマソ(並べたかった)
 my($r, $g, $b) = @_;
 my($h, $s, $v, $min, $max);

 if($r < $g) {$min = $r; $max = $g;}
 else        {$min = $g; $max = $r;}
 if   ($max < $b) {$max = $b;}
 elsif($min > $b) {$min = $b;}

 $v = $max;
 if($max == 0 || $max == $min) {$h = 0; $s = 0;}
 else {
  $s = 255 * ($max - $min) / $max;
  my $cal_r = ($max - $r) / ($max - $min);
  my $cal_g = ($max - $g) / ($max - $min);
  my $cal_b = ($max - $b) / ($max - $min);
  $h = $max == $r ? 60 * (    $cal_b - $cal_g):
       $max == $g ? 60 * (2 + $cal_r - $cal_b):
                    60 * (4 + $cal_g - $cal_r);
  if($h >= 360) {$h -= 360;}
  elsif($h < 0) {$h += 360;}
 }

 return($h, $s, $v);
}


sub is_over { # RGBカラー判定で半分より上(1)か下(0)か
 my($value,) = @_;
 return($value < 128 ? 0 : 1);
}


sub cal_length_rgb { # 2点間の3次元での距離を計算
 my($r1,$g1,$b1, $r2,$g2,$b2) = @_;
 return(sqrt(($r2 - $r1)**2 + ($g2 - $g1)**2 + ($b2 - $b1)**2));
}

あー、やっちまったなw

う〜ん。よい子はまねしちゃいかん、感じになってますね〜。まーしゃーないwww

HSVのSとVは0〜100(%)が一般ポイらしいですがこのプログラムではRGBに合わせて0〜255にしてます。


そーいや、perlって次乗計算を ** って書くんだね。最初 ^2 とか書いてて全然わけわかんない計算されててビビったよw
てか、perlで次乗計算したの初めてか自分orz ダメじゃんwww


あと、Image::Magickさんを使う場合はnewするあたり(ファイルを指定したりするあたり)を

 my $image = Image::Magick->new();
 $image->Read($file);
 my($width, $height) = $image->Get('width', 'height');

みたいに変えて、ピクセルカラーを取得するあたりを

  my($r, $g, $b,) = split(',', $image->Get("pixel[$x, $y]"));
  $r /= 256; $g /= 256; $b /= 256;
  # ↑各要素が16ビットで返ってくるみたいなので

みたいにしとけば良いと思います。(途中までこれでやってたw

->ちょいと変えた物を書いてみた!