====== PHPで写真などの画像から背景だけを消し去る - ImageDiff.php ====== コメントと更新履歴は[[http://0-oo.net/log/category/php-tool-box/image-diff/|ゼロと無限の間のログ » ImageDiff.php]]でどうぞ。 ---- PHPとGD2を使って、画像から背景部分だけを取り除き、前景のみを得る実験。\\ 結果はこのとおり、ちょっと精度に欠ける。\\ {{php-tool-box:src.png|}} - {{php-tool-box:background.png|}} = {{php-tool-box:result.png|}} 写真での実験結果はこちら。[[http://x.0-oo.net/sam/image-diff/index.html|ImageDiffで写真を扱った結果]] なお、今回作ったImageDiffクラスは対象の画像形式はPNGだが、該当箇所を変更すればJPEGとかGIFでもできるはず。 ===== やり方 ===== 対象となる画像ファイルと、その画像ファイルの__背景のみ__の画像ファイルを用意する。\\ 後は下記のようにImageDiffクラスに任せる。 require('ImageDiff.php'); $dir = '/tmp'; ImageDiff::$colorThreshold = 1; ImageDiff::draw("$dir/src.png", "$dir/background.png", "$dir/result.png"); ImageDiffクラスのpublic変数の値を調整すれば精度を変えられる。\\ また、draw()の第4引数に画像ファイルのパスを指定すると、取得した前景の画像をその画像の上に載せることができる。\\ ===== 手ブレ補正 ===== 手ブレした写真など、比較する画像の位置にずれがある場合は、ImageDiff::$maxSlippageにピクセル数をセットすることによりブレを補正できる。 ===== 明るさ補正 ===== 蛍光灯の明かりで撮影した写真などは彩度は同じでも明度が変わってきてしまうが、ImageDiffでは彩度の違いにより背景かどうかを判断するようにしているので影響は少ない。 ===== ソースコード ===== 0 && $sum < $record) || $record < 0) { $record = $sum; //記録更新 $best = $slippage; } echo "$x,$y => $sum\n"; //途中経過を報告 } } echo "[Best] {$best[0]},{$best[1]} => $record"; //結果報告 return $best; } /** * 全ピクセルについて処理する */ private static function _forAllPoint($srcImg, $difImg, $rect, $slippage, $canvas, $record) { $sum = 0; for ($x = $rect[0]; $x < $rect[2]; $x++) { for ($y = $rect[1]; $y < $rect[3]; $y++) { $diff = ImageDiff::_calcDiff($srcImg, $difImg, $x, $y, $slippage); if ($canvas) { //前景のコピーの場合 if ($diff > ImageDiff::$colorThreshold) { //閾値を超えたら前景とみなす imagecopy($canvas, $srcImg, $x, $y, $x, $y, 1, 1); } } else { //差分合計の計測の場合 $sum += $diff; if ($record > 0 && $record <= $sum) { //もう記録更新が無理な場合は中止 return -1; } } } } return $sum; } /** * 特定のポイントについて2つの画像の色の差の偏りを計算する */ private static function _calcDiff($srcImg, $difImg, $x, $y, $slippage) { $srcColor = ImageDiff::_getRGB($srcImg, $x, $y); $difColor = ImageDiff::_getRGB($difImg, $x + $slippage[0], $y + $slippage[1]); $diff = array(); for ($i = 0; $i < 3; $i++) { //RGBそれぞれについて差を求める $diff[$i] = $srcColor[$i] - $difColor[$i]; } $sum = 0; for ($i = 0; $i < 3; $i++) { //RGB間の差の偏りを求める $sum += abs($diff[$i] - $diff[($i + 1) % 3]); } return $sum; } /** * 色情報(RGB)を取得する */ private static function _getRGB($rsc, $x, $y) { $rgb = imagecolorat($rsc, $x, $y); for ($i = 0; $i < 3; $i++) { $color[$i] = ($rgb >> ($i * 8)) & 0xFF; } return $color; } }