nigoblog

暫定無職のブログ

PHPのGDライブラリを使って画像の色を取り出す~ルービックキューブの画像処理~

f:id:nigohiroki:20121014234829p:plain

PHPにはすごく便利なライブラリにGDライブラリというものがあります。

用途は主に画像関連の処理!!

というわけでGDライブラリを用いて画像の色を取り出し、解析を行なっていきます!!

  1. そもそも
  2. GDライブラリ
  3. 使う関数とその説明
  4. 実装

このような流れで説明していきます。

そもそも

なんでこれをやろうと思ったかを少し。
このブログで前回ルービックキューブの実装をPHPでしてみた~ルービックキューブクラス~ - nigoblogルービックキューブについてブログを書きました。
それで思いついたアプリケーションが
ルービックの写真から解法を導くアプリケーション
を作ろうと思ったわけです。

そのアプリケーションの流れはこんな感じ

  1. バラバラのルービックキューブの写真を取る(3面)
  2. その画像を解析し、ルービックキューブがどのような状態かを判断
  3. 解くまでの手順を表示
  4. 使った人は簡単にルービックキューブを完成!

みたいな感じの。
それで手順2の画像を解析しルービックキューブがどのような状態かを判断するために今回のテーマをやりました。

GDライブラリ

とはざっくりいうと「PHPで画像を処理するためのライブラリ」です。
ここに色々マニュアルがのっています。PHP: GD - Manual

使う関数とその説明

GDライブラリには色々な関数があるのですが、今回は次の5種類の関数をつかいます。

imagecreatefrompng(filename);

これは説明では「新しい画像をファイルあるいはURLから作成する」
とあります。
正直これはいまいち掴みどころがわからないのですが、ここで作成した画像を今後使います。実際には

$img = imagecreatefrompng(filename);

のように作成した画像を変数に代入します。

imagesx($img);
imagesy($img);

この2つは画像の幅x 、高さy を取得します。

imagecolorat($img, width, height);

これは画像 $img の width, height の時のRGBの値を取得します。ただし16進数で連続で値を返すため、わかりやすくするために次の関数をつかいます。

$rgb = imagecolorat($img, width, height);
$colors = imagecolorsforindex($img, $rgb);
print_r($colors);

こうすると次のように出力されます。(値は適当)

array(
  [red] => 100,
  [green] => 100,
  [blue] => 100
)

というわけで各ピクセルのRGBの値を取り出すことができました。

実装

それで今回、画像を入力して画像の画素で赤の時は 「 r 」 , 白の時は 「 w 」 , 緑の時は 「 g 」 , その他の時は 「 @ 」
を表示するプログラムを書きました。

用いた画像はこちらです
f:id:nigohiroki:20121014234829p:plain

早速コードはこちら

define('CUBE', '20121009234619.png');
	
// 1
$img = imagecreatefrompng(CUBE);
$imagex = imagesx($img);
$imagey = imagesy($img);

// 2	
$red = array(150, 110, 110);
$green = array(110, 110, 110);
$white = array(110, 110, 110);

// 3
for($y = 0; $y < $imagey;$y++){
	for($x = 0; $x < $imagex; $x++){
		$rgb = imagecolorat($img, $x, $y);
		$colors = imagecolorsforindex($img, $rgb);
		$r = $colors['red'];
		$g = $colors['green'];
		$b = $colors['blue'];
	
		if($white[0] < $r && $white[1] < $g && $white[2] < $b){
			echo 'w';
		}else if($red[0] < $r && $red[1] > $g && $red[2] > $b){
			echo 'r';
		}else if($gren[0] > $r && $green[1] < $g && $green[2] > $b){
			echo 'g';	
		}else{
			echo '@';
		}		
	}
	echo '<br>';
}

早速解説します。
まず 1 では画像の生成をしています。
そして生成した画像の幅と高さを取得してそれぞれ $x, $y に代入します。

2では RGB の閾値を設定します。
写真が全体的に暗いため設定も少し甘めになっています。
ここでポイントなのですが
画像処理をするにはその前の下処理が重要となる
今回は全体的に暗い、また全体的に明るいということも考えられます。

補足
ヒストグラム(カラーの分布)という概念があるのですが、これがある程度「疎」になっていると処理が楽になります。
なので実用化させるためには下処理も実装する必要があります。
今回はとりあえず閾値を甘めにするということで対応しています


3で画像をスキャンしていきます。
スキャンは画像の左上から右上、一段さげて左から右へと解析していくことです。
ここで1ピクセルづつRGBを取得し、用意した閾値により赤、白、緑、その他を判断し、それぞれ 「 r 」,「 w 」,「 g 」,「 @ 」を表示します。

以上ざっくりと画像処理についてでした。
しかし、今回赤、白はある程度うまく取得できたのですが、緑だけは中々うまく取得できませんでした。
つまり先程説明した下処理が必要ということが今回の原因のようです。

というわけで次回はその下処理の実装を考えていきたいと思います。
ではでは。