<?php
/**
* ImageDiff.php
* @version 0.1.0
*/
class ImageDiff {
/** 違う色と判断する閾値(大きいほど大きな色の違いを求める) */
public static $colorThreshold = 10;
/** ブレを修正する範囲(単位=ピクセル数。大きいと時間がかかる) */
public static $maxSlippage = 0;
/** 画像を滑らかにする度合い(大きくすると画像が鮮明になりノイズが増える) */
public static $smooth = 3;
/**
* 前景のみをくり抜く
* @param string $srcPath 対象となる画像のファイルパス
* @param string $difPath 背景のみの画像のファイルパス
* @param string $dstPath 差分画像の出力先のファイルパス
* @param string $basePath (省略可)差分画像を埋め込む画像のファイルパス
*/
public static function draw($srcPath, $difPath, $dstPath, $basePath = null) {
list($w, $h) = getimagesize($srcPath);
$srcImg = imagecreatefrompng($srcPath);
$difImg = imagecreatefrompng($difPath);
if ($basePath) {
$canvas = imagecreatefrompng($basePath);
} else {
$canvas = imagecreatetruecolor($w, $h);
imagefill($canvas, 0, 0, imagecolorallocate($canvas, 255, 255, 255));
}
$s = ImageDiff::$maxSlippage;
$rect = array($s, $s, $w - $s, $h - $s); //対象範囲の長方形
$slippage = ImageDiff::_getPoint($srcImg, $difImg, $rect);
ImageDiff::_forAllPoint($srcImg, $difImg, $rect, $slippage, $canvas, false);
imagefilter($canvas, IMG_FILTER_SMOOTH, ImageDiff::$smooth); //ノイズを減らす
imagepng($canvas, $dstPath);
imagedestroy($srcImg);
imagedestroy($difImg);
imagedestroy($canvas);
}
/**
* 画像間のブレが一番小さくなるずらし方を探す
*/
private static function _getPoint($srcImg, $difImg, $rect) {
$s = ImageDiff::$maxSlippage;
$record = -1;
//X軸、Y軸についてずらしながら探す
for ($x = -$s; $x <= $s; $x++) {
for ($y = -$s; $y <= $s; $y++) {
$slippage = array($x, $y);
$sum = ImageDiff::_forAllPoint($srcImg, $difImg, $rect, $slippage, false, $record);
if (($sum > 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;
}
}