PHPで256インデックスカラーで表現するため、減色処理を行う話。
実際にプログラムをかいてみました
まずは、その結果
左が、元の画像。右が、その結果
![]() |
うーん、たしかに、2段目、3段目のカベが荒いけど、十分なんじゃーないでしょーか?
で、やり方は、今まで触れてきたとおり、こんなかんじ
1.
Rについて、0,100,200,255,
Gについて、0,100,200,255
Bについて、0,100,200,255 計4X4X4=64階調を登録しておく
2.あとで、ソートが簡単になるように、これら、64階調の頻度に、下駄をはかせて、頻度でソートしたら、かならずこいつらが、上位になるようにする(総ドット数分、頻度を足しこめば、絶対こいつらが上位にくる
3.全部のドットについて、頻度を数える。
ただし、まったく同じ色というわけでなく、ある程度の誤差範囲を決め、その範囲内だったら、その色とみなして、頻度を数える。
4.そこから、上位256色を選び、インデックスカラーとして登録する。
5.PHPには、ここで使っているように、imagecolorclosestっていう関数で、一番近い色を探してくれるので、それを使って、インデックスカラーを選び、描画する。
で、これが、それを書いたプログラム
<?php //画像の作成 $img = imagecreatefromjpeg("gentest.jpg"); $x = imagesx ($img); $y = imagesy ($img); $newimg = imagecreate($x,$y); // 1.64階調を登録しておく $ix = 0; for($i = 0 ; $i < 4 ; $i++) { for($j = 0; $j < 4 ; $j++) { for($k = 0 ; $k < 4 ; $k ++) { switch($i) { case 0: $r[$ix] = 0; break; case 1: $r[$ix] = 100; break; case 2: $r[$ix] = 200; break; case 3: $r[$ix] = 255; break; } switch($j) { case 0: $g[$ix] = 0; break; case 1: $g[$ix] = 100; break; case 2: $g[$ix] = 200; break; case 3: $g[$ix] = 255; break; } switch($k) { case 0: $b[$ix] = 0; break; case 1: $b[$ix] = 100; break; case 2: $b[$ix] = 200; break; case 3: $b[$ix] = 255; break; } //2.64階調の頻度に、下駄をはかせて $ken[$ix] = $x * $y; $ix++; } } } // 3.全部のドットについて、頻度を数える for($i = 0 ; $i < $x ; $i+=7) { //7を足している理由は、あとで書きます for($j = 0 ; $j < $y ; $j +=7) { $rgb = imagecolorat($img, $i,$j); $rr = ($rgb >> 16) & 0xFF; $gg = ($rgb >> 8) & 0xFF; $bb = $rgb & 0xFF; for($k = 0 ; $k < $ix ; $k ++) { if ( ($r[$k]-3 <= $rr ) && ($r[$k]+3 >= $rr ) && ($g[$k]-3 <= $gg ) && ($g[$k]+3 >= $gg ) && ($b[$k]-3 <= $bb ) && ($b[$k]+3 >= $bb )) { $ken[$k] = $ken[$k] + 1; break; } } if ($k == $ix ) { $r[$ix] = $rr; $g[$ix] = $gg; $b[$ix] = $bb; $ken[$ix] = 0; $ix = $ix + 1; } } } // 頻度数をもとにソート for($i = 0 ; $i < $ix ; $i ++ ) { for($j = $i+1 ; $j < $ix ; $j ++) { if ( $ken[$i] < $ken[$j] ) { $tr = $r[$i]; $tb = $g[$i]; $tg = $b[$i]; $r[$i] = $r[$j]; $g[$i] = $g[$j]; $b[$i] = $b[$j]; $r[$j] = $tr; $g[$j] = $tg; $b[$j] = $tb; } } } // 4.そこから、上位256色を選び、インデックスカラーとして登録する。 for($i = 0 ; $i < $ix ; $i ++ ) { if ( $i >= 256 ) { break; } imagecolorallocate ($newimg, $r[$i],$g[$i],$b[$i]); } //5.一番近い色を探して、描画する for($i = 0 ; $i < $x ; $i++) { for($j = 0 ; $j < $y ; $j ++) { $rgb = imagecolorat($img, $i,$j); $rr = ($rgb >> 16) & 0xFF; $gg = ($rgb >> 8) & 0xFF; $bb = $rgb & 0xFF; imagesetpixel ($newimg,$i,$j, imagecolorclosest($newimg, $rr,$gg,$bb)); } } //画像出力 header("Content-type: image/jpeg"); header("Cache-control: no-cache"); imagejpeg($newimg); //画像の消去(メモリの解放) imagedestroy($img); imagedestroy($newimg); ?> |
(上記のプログラム中、 < > ¥は本当は半角です。赤字のところは、元のファイルを指定するところですので、環境によって変わります)
上記で、色を取り出すとき、7つづつ、とびとびに取り出している理由は、とびとびにしないで、1つ1つ取り出して、ロリポップで動かしたら、30秒以上計算しているエラーになったので、7つとびにしてみました。
なお、3.で「ある程度の誤差範囲を決め」という誤差範囲ですが、+-3にしてみました(if文で、聞いている)