管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图 @1 J3 q& r0 S7 M1 N, b9 k6 ^% `
) p9 f4 F! s2 }+ ]: {3 N- n3 t- l" U J: t
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。# l& m5 y$ F( g2 l7 G
- <?php
' z8 H$ K( y1 Q( s# \7 K - /** 0 g0 q$ W( Z* b% H0 @. g+ X* X0 g: ~
- * 图片相似度比较
, k" `+ ^5 ] U+ Q! h( d: C - *
3 W3 ~, X6 v( a1 d# d3 z1 E - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
% D0 |2 G l6 l* q - * @author jax.hu ^5 i; g$ C, F3 U# L F' B
- * 7 T9 @/ r" K' T5 R3 t. F
- * <code>
( L4 d1 f5 u# B% }# O( d1 K - * //Sample_1 , J* B6 k: f7 A, `
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
8 ] I9 n7 S0 L1 {% [, d - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 0 q2 X/ e9 Y& A+ r% E9 i
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
. X' b' l. T+ {' }# W. C - *
3 N% C; `( Q. w1 W4 P7 C - * //Sample_2
# m- X5 T3 N8 M! t6 [* S - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
: t% X! C N+ G# t - * </code>
% c- q7 P, o3 I1 I! s/ d7 J! A - */
: j+ e) w7 K6 D% }3 c -
5 _6 F6 W7 X' E3 M: y - class ImageHash { : R2 i: K% y4 J
-
: g2 k4 K+ _ B" Q0 g - /**取样倍率 1~10 . J Y% M) C9 V* |3 S
- * @access public
( g( k; A8 i5 r - * @staticvar int
% [3 p9 N! o' T - * */ # j+ P/ I+ u7 j; r$ M3 P( T: q- ]
- public static $rate = 2;
4 P8 Q$ p3 H/ q0 L" t! _2 ^6 X - ' ^6 h: y# l, {3 G: _- f E
- /**相似度允许值 0~64 ! S! t( `5 E- j& p, V+ w. }8 W8 Y
- * @access public
7 m/ U i/ W8 {. g( Q- j - * @staticvar int ' @' a7 n% z( }: T- _5 n
- * */ # Y1 \. H8 T) S( P. _" p
- public static $similarity = 80; " ?5 s" h4 l2 S- }3 ~
-
8 `& ~( _& Z& O! d9 Y+ L - /**图片类型对应的开启函数 ! r$ w5 `& v8 ~. x2 d
- * @access private - ?9 E& q- x8 R: s0 N9 g) X
- * @staticvar string
: S4 }: S5 ~8 [; E) |2 U8 D2 t - * */ 7 W0 R# T3 p' B. o
- private static $_createFunc = array(
5 p4 O- t1 E, P4 v% C: [) |% ^ - IMAGETYPE_GIF =>'imageCreateFromGIF', ' n4 P! E/ S- o' U6 g
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
) |. t- W6 F- O - IMAGETYPE_PNG =>'imageCreateFromPNG',
# m3 M$ @) M9 E& y/ Q, B0 X; V* } - IMAGETYPE_BMP =>'imageCreateFromBMP', 4 |+ B" E" g% u+ `8 V5 Y
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
J, g* @# f; i/ U - IMAGETYPE_XBM =>'imageCreateFromXBM',
- H4 _8 Z1 K7 u% p9 ]3 a3 E! ? - );
" J' {7 e% m9 M/ \ - 6 }* |" x8 m( B2 H+ w. f# U0 x: x
- 1 q6 X* `- @7 W& b. T
- /**从文件建立图片 - T, e2 K. u: m3 \; J
- * @param string $filePath 文件地址路径
! o7 k0 C1 V+ d6 q/ W/ D! v# K4 x! H7 e - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
" W, Z7 x7 k2 Z5 P2 D - * */
, w8 G/ Z' }8 l9 m7 y' j - public static function createImage($filePath){
# ^7 J$ h3 ^$ j6 v - if(!file_exists($filePath)){ return false; }
0 n% n- L! t5 R7 C+ u/ Y! U - . B1 `: N: h) X- Z# H
- /*判断文件类型是否可以开启*/ ! b4 y; X( q F: x% S5 d2 H$ Y! L
- $type = exif_imagetype($filePath);
0 @$ a* H5 i) D: ]. ~ - if(!array_key_exists($type,self::$_createFunc)){ return false; } 5 T4 T% w- A' X; O4 ?" w
- $ e3 @: e) i3 |. Z9 Q4 u! V, y
- $func = self::$_createFunc[$type];
" p. C2 G, @& G2 s - if(!function_exists($func)){ return false; } 2 h: X0 W4 P/ ?. b. Y% }
- 8 U2 }" |( _6 [+ K! L
- return $func($filePath);
) O, V$ Q" G- o+ H' s" Z- z - }
4 f& K1 L1 g" }7 v* y9 W3 H8 s -
& w# _, b* O5 W/ ^1 Z - * U4 t! W: F' _3 i. }% `
- /**hash 图片 ( U, e- o% R" w( Z
- * @param resource $src 图片 resource ID ) L. b1 L9 v) _- N% x$ ~; |
- * @return string 图片 hash 值,失败则是 false 9 E+ T; Y. l+ N7 l0 x$ v; @
- * */
9 K; E% C- o+ X7 T# j+ S) ?1 u - public static function hashImage($src){
3 k* S9 i' s$ l x; ~ - if(!$src){ return false; } 8 E+ r& }& Z4 N4 A# [) ~
- / Z4 q2 k3 Y" |3 V, i7 H
- /*缩小图片尺寸*/ # e5 F( k, Q3 t* I3 t+ H& M) P
- $delta = 8 * self::$rate;
4 _7 K0 R( I4 a3 \ - $img = imageCreateTrueColor($delta,$delta);
1 B4 k% Z9 I* Z) ]2 }2 i* ~ K% O( ^ - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); & y, h9 r V* A3 G o, f2 ?8 \, V
- 1 T1 I/ I+ W& k9 Y8 P; p1 ]' |
- /*计算图片灰阶值*/
( C5 ^# k, i+ a1 a - $grayArray = array(); 8 E# M; R. U- n/ U# a9 n/ J; B7 b. c
- for ($y=0; $y<$delta; $y++){ " [& _* c, U+ t
- for ($x=0; $x<$delta; $x++){
Y/ O8 a3 C& w - $rgb = imagecolorat($img,$x,$y); # ^/ T$ Q- s9 u- }2 p% O
- $col = imagecolorsforindex($img, $rgb); 0 |2 \9 j. G2 z" V- ^
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; # p( K* u6 @& m6 u7 O4 `& J
- + g% G: @& I5 |% Z; S4 b- d, }' Y! b1 T
- $grayArray[] = $gray;
( a2 \3 }% {, i% p5 [ - } - Z5 h# A. k" H" e
- }
) \4 c: q! t+ ?. J/ p8 I$ v+ V/ l - imagedestroy($img); + c/ A7 l7 ~, d$ a* v8 n- o: j9 h: U- [
-
: a& I/ Y! q* Y. j0 ] - /*计算所有像素的灰阶平均值*/
5 j# B, m9 t; J7 J& u - $average = array_sum($grayArray)/count($grayArray);
9 f! r3 ]7 ]! q -
( H/ d; y9 C2 i- O0 y( I - /*计算 hash 值*/
1 n# @! Z* s8 J - $hashStr = '';
% ^2 y2 r( \2 D+ _: A+ A - foreach ($grayArray as $gray){ 9 {# S- ]- e: w a1 p% i5 h
- $hashStr .= ($gray>=$average) ? '1' : '0';
4 ]2 z8 R0 X5 C/ r# P. m9 A - }
+ {8 H8 }$ w/ A. z- ` - 6 y' T; M3 [) \+ g# C% \
- return $hashStr; ; N i2 y& @1 j9 _2 @/ V2 P, i
- } % @- k: T. ?1 T
-
: ^( H6 u+ E! }( x7 I q -
: p! S3 S2 Q6 T' X - /**hash 图片文件
9 e Z/ u, c( @+ _ - * @param string $filePath 文件地址路径
+ t4 T7 ^* G6 Q0 x& `3 L - * @return string 图片 hash 值,失败则是 false
' D& g3 ]" O7 N" o+ ^ - * */
% z6 Y1 e% _( I0 W# z+ ?' c - public static function hashImageFile($filePath){ 7 K4 f) Y( N$ E# ^2 q* T2 v
- $src = self::createImage($filePath);
& C& L1 v7 ]) R, ?5 t. j - $hashStr = self::hashImage($src); % P7 ^8 l; w+ b" E
- imagedestroy($src); ( O! q7 {) r! v5 u5 D+ }# C [
-
6 n5 o! I. N0 G, c6 Y5 g# v - return $hashStr; ) X& Y1 v4 P) _: }
- } % C/ Z/ d' Y% r* s
-
1 \" x* w6 r" a' `/ l - B7 }6 p, K& {- ?; D
- /**比较两个 hash 值,是不是相似
: h- Q, }" F, o# ^1 o, M - * @param string $aHash A图片的 hash 值 2 i# @) }4 y) I2 w! u; b4 d, b: Y! }
- * @param string $bHash B图片的 hash 值 6 p+ \. _4 j" }, C+ G+ u' A1 G
- * @return bool 当图片相似则传递 true,否则是 false + R9 H9 @/ D- @% ^- J# x5 Z1 t
- * */
* n; ?' |: k! R# U- X+ T - public static function isHashSimilar($aHash, $bHash){ 6 ~. z0 W7 Q' ]" v" a
- $aL = strlen($aHash); $bL = strlen($bHash); 2 [* [: {' w7 y: A' s
- if ($aL !== $bL){ return false; } " I: b. o" {) }
-
2 t' T7 g. H W( g - /*计算容许落差的数量*/
0 H3 L" O/ W! v - $allowGap = $aL*(100-self::$similarity)/100; / E+ T: s4 l: v1 j8 ~2 H. a; n
- + m' H' v9 F6 P& K! P/ C- n- C
- /*计算两个 hash 值的汉明距离*/
' q: p; H' D0 e3 h2 q+ y - $distance = 0; 0 S+ }. z( w- n7 w
- for($i=0; $i<$aL; $i++){
& T- T# E* b! k; z; M% q5 [ - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
# L" w! G p) A* z - }
$ G% @. n! G9 q* Y2 O/ L9 o0 j5 J - : a6 @8 c/ p2 d( B3 L
- return ($distance<=$allowGap) ? true : false;
, y3 q7 g- w) E3 P, f - }
I3 }4 h/ [/ k/ ~ N! J7 R. ]- S -
6 D8 b6 Y- P3 N0 H - 0 p* Q' J# h0 s0 U6 e
- /**比较两个图片文件,是不是相似 4 K) i7 w' T6 n: z7 Y
- * @param string $aHash A图片的路径 6 u4 G* v. b# d6 S
- * @param string $bHash B图片的路径 4 k, Q3 d2 \; {6 K2 v, ~
- * @return bool 当图片相似则传递 true,否则是 false
) }9 X7 m( ~/ p - * */ 3 _ K7 A) B- F) \' t! ~
- public static function isImageFileSimilar($aPath, $bPath){ . q+ T1 g5 ]% K8 |
- $aHash = ImageHash::hashImageFile($aPath);
, b* t" r/ {- w3 A) M' V: Z - $bHash = ImageHash::hashImageFile($bPath);
' Z+ C: L. B( T! z8 B4 ` - return ImageHash::isHashSimilar($aHash, $bHash); I5 ?* N0 u2 a% J; p
- }
b9 J- E" `6 J# D, D% q - ; B3 [, [9 [& M' T5 o
- }. \. M [3 P" ^1 _ h% H" p5 @
复制代码
, U) T3 f! ?: V7 u% m7 D' m3 E! h6 j5 ^. l
|
|