/**
* 生成指纹
* $filename 图片文件名,可以是URL。只能是GD支持的图片类型
* $k 采样点阵数
* $retmode 返回格式:0 二进制表示 1 数组 2 十六进制表示
**/
function produceFingerPrint($filename, $k=8, $retmode=0) {
$sim = imagecreatefromstring( file_get_contents($filename) );
$dim = imagecreate($k, $k);
imagecopyresized($dim, $sim, 0, 0, 0, 0, $k, $k, imagesx($sim), imagesy($sim));
imagetruecolortopalette($sim, true, 64);
//imagefilter($sim, IMG_FILTER_GRAYSCALE); for($x=0; $x<$k; $x++) for($y=0; $y<$k; $y++) {
$c = imagecolorat($dim, $x, $y);
$p[] = array_sum( imagecolorsforindex($dim, $c))/3;
}
$avl = array_sum($p)/count($p);
$r = '';
foreach($p as $v) $r .= $v>=$avl ? 1 : 0; if($retmode == 0) return $r;
$p = array_map('bindec', str_split($r, 8));
if($retmode == 1) return $p;
if($retmode == 2) {
array_unshift($p, '%02x%02x%02x%02x%02x%02x%02x%02x');
return call_user_func_array('sprintf', $p);
}
return false;
}
算法描述是从网上找的,只是做了 php 实现
仅供参考
* 生成指纹
* $filename 图片文件名,可以是URL。只能是GD支持的图片类型
* $k 采样点阵数
* $retmode 返回格式:0 二进制表示 1 数组 2 十六进制表示
**/
function produceFingerPrint($filename, $k=8, $retmode=0) {
$sim = imagecreatefromstring( file_get_contents($filename) );
$dim = imagecreate($k, $k);
imagecopyresized($dim, $sim, 0, 0, 0, 0, $k, $k, imagesx($sim), imagesy($sim));
imagetruecolortopalette($sim, true, 64);
//imagefilter($sim, IMG_FILTER_GRAYSCALE); for($x=0; $x<$k; $x++) for($y=0; $y<$k; $y++) {
$c = imagecolorat($dim, $x, $y);
$p[] = array_sum( imagecolorsforindex($dim, $c))/3;
}
$avl = array_sum($p)/count($p);
$r = '';
foreach($p as $v) $r .= $v>=$avl ? 1 : 0; if($retmode == 0) return $r;
$p = array_map('bindec', str_split($r, 8));
if($retmode == 1) return $p;
if($retmode == 2) {
array_unshift($p, '%02x%02x%02x%02x%02x%02x%02x%02x');
return call_user_func_array('sprintf', $p);
}
return false;
}
算法描述是从网上找的,只是做了 php 实现
仅供参考
简单的说就是要钱,趁金价跌的时候多收购一点吧技术方面可以搜索opencv关于图片比较方面的文章
$im2 = imagecreatefromgif($fn);//imagetruecolortopalette($im1, true, 64);
imagefilter($im1, IMG_FILTER_GRAYSCALE);//imagetruecolortopalette($im2, true, 64);
$orange = imagecolorallocate($im2, 220, 210, 60);
$string = 'abcd';
$px = (imagesx($im2) - 7.5 * strlen($string)) / 2;
imagestring($im2, 3, $px, 9, $string, $orange); //所以在第二个上加入写文字,这样两个就不一样了imagefilter($im2, IMG_FILTER_GRAYSCALE);
//计算直方图
function GetHisogram($im) {
$histogram = array();
$mx = imageSX($im);
$my = imageSY($im); for($x=0; $x<$mx; $x++) {
for($y=0; $y<$my; $y++) {
$t = imagecolorat($im, $x, $y);
@$histogram[$t]++;
}
}
ksort($histogram);
return $histogram;
}
//计算相似度
function Similarity($G, $S) {
if( count($G) != count($S)) return 0;
$r = 0;
foreach($G as $k=>$v)
$r += 1 - abs($v - $S[$k]) / max($v, $S[$k]);
return $r / count($G);
} $h1 = GetHisogram($im1);
$h2 = GetHisogram($im2);
echo Similarity($h1, $h2);
得到相似度为 0.99405047874224
还是有问题 在计算相似度的时候
if( count($G) != count($S)) return 0; 如果差异是两张图片上都没出现过的颜色 那么count($G) != count($S) 结果是0
应该是我的表达有问题 my code:
//计算相似度
function Similarity($G, $S) {
if( count($G) != count($S)){
if(count($G) > count($S)){
$m = & $G;
}else{
$m = & $S;
}
$c = abs(count($G) - count($S));
asort($m);
$m = array_slice($m, $c - 1, -1, TRUE);
ksort($m);
}
$r = 0;
foreach($G as $k=>$v)
$r += 1 - abs($v - $S[$k]) / max($v, $S[$k]);
return $r / count($G);
}
从计算精度上考虑函数 GetHisogram 没有做标量化
你该这样理解这个函数
function Similarity($G, $S) { //传入的数组中键是颜色,值是改颜色出现的次数
if( count($G) != count($S)) return 0; //如果颜色数不相同就返回0
$r = 0;
foreach($G as $k=>$v)
$r += 1 - abs($v - $S[$k]) / max($v, $S[$k]); //计算两图同一颜色数量上的差异率
return $r / count($G);
}
极端一点:一个彩色图片黑白化后颜色数量必然不同,你认为他们是相同的图片吗?如果是,那么这个算法不适合你,而#1的算法才适合你,因为他首先就将彩色图片黑白化了
如果不是,那么连颜色数都不相同,就没有必要比较了。直接就可以认为不相似
一个 1x2 的图片 有2个红点
另一个 1x2 的图片 有一个红点一个蓝点
从相似度上说,相似度为 0.5
如果连 0.5 的相似度都可以通过的话,那你的系统的误判率也是在太高了
按普通机械工程的最大容差±10%来说,相似度至少应在 0.9 以上
所以,若颜色量不同的应直接放弃计算
应先用 #1 的指纹算法做海选
对定长的数据做汉明距离的计算是很快的(统计二进制0、1的个数)
#6 的算法因为计算量巨大,只适合对初选结果做小范围比对考虑到转来转去的网络上相同图片很多,而且尺寸相对规格化。还可以先用 md5 过滤一下
如果连 MD5 都是一样的,那就没必要比对了(尽管MD5有碰撞,但毕竟是小概率事件)