管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图) C3 W1 D+ G# I+ h3 b
3 z! k& I+ s% |( A
! u( J- c! N4 b由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。2 Z& q8 }$ z/ T% H8 T; |
- <?php ; |& j) m2 d1 L+ U+ G- X
- /**
, y6 R2 I3 f" h* G. @3 s - * 图片相似度比较
7 ~( W) F7 S+ ]3 U1 X) c - *
# r, z: i3 b- F - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
; u2 g/ `& G" g3 c& \) P9 z - * @author jax.hu + |5 M* g" T; x; A
- *
! R6 s& i2 W: r6 h& x - * <code> 4 d+ U5 s0 I) [3 W4 n
- * //Sample_1 7 z. `6 K" C+ w% Z' h
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 3 d- G1 v5 Y8 y; U
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); / ]. ~5 F/ m% b1 ?6 ]3 r/ i, G& A
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
# d2 g9 g1 f8 U - * a, @. p: B/ y( L
- * //Sample_2 - w( F% U8 `# Q, n9 v! O
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
9 P+ O$ @5 G+ n - * </code> 6 L( v, Q: g& r8 F/ B, p1 p
- */
2 \$ [" ^, Q2 r9 }' z+ R: l9 ]; ? -
4 P- S2 Q% Y: @' |( w - class ImageHash {
9 A0 x3 V$ ?* l# i- k - ; K& h$ w4 f5 k" T' W
- /**取样倍率 1~10 3 S! p3 W" V; ~, T, i
- * @access public
2 `. b3 r1 u# o# x, M4 C9 r! K" a ? - * @staticvar int ' |% w+ S$ n2 R: u; |2 m; A
- * */
+ E0 s/ y; v/ j$ u; ?7 g% D - public static $rate = 2;
- e9 j8 y" S& D -
" L. V6 `, R3 D# F% m - /**相似度允许值 0~64 9 t2 X2 t( I9 h: w6 w5 w
- * @access public 1 N8 n( G5 y b! u7 y+ n- i
- * @staticvar int 3 s9 D5 ^( w1 ]) j
- * */
! N+ b+ R A* l+ T - public static $similarity = 80;
# E$ q5 B" l! ~; P: D - & O1 M/ l0 m3 r9 c) c
- /**图片类型对应的开启函数 . A2 |6 R9 R' A( \1 K
- * @access private
3 ^+ E2 u; N4 R$ ~. J - * @staticvar string # D3 o2 v% W& p2 l: a- j+ W
- * */ ' O; G. Q: I* e5 O
- private static $_createFunc = array( 2 b, P V2 E$ ?; r
- IMAGETYPE_GIF =>'imageCreateFromGIF', " n5 E6 j3 x& q
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
4 I) h/ ~( j9 ^ x" [8 r - IMAGETYPE_PNG =>'imageCreateFromPNG',
9 C' C4 {' J" H, k7 O6 ~ - IMAGETYPE_BMP =>'imageCreateFromBMP',
1 g2 u. V8 ~0 ~$ [ o - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
* {/ b' N; |8 W' D4 [ - IMAGETYPE_XBM =>'imageCreateFromXBM', " P& P9 C* N4 L$ J" b9 q
- );
1 A$ l; j& R! q: Y, M2 m0 u - / g% g. k& |3 }9 n. s5 ?: n
- & A' m: ^/ `+ |6 h& e( B
- /**从文件建立图片
2 V2 h( F6 |) H* U2 ~- C - * @param string $filePath 文件地址路径
$ s7 s; g$ t0 x2 t2 P0 m2 @ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
# c# v. S2 G% M - * */ 8 K) l4 I4 j2 c( Q2 l
- public static function createImage($filePath){ 6 W& K- B9 b" M9 H. N2 J! r* i4 e- |
- if(!file_exists($filePath)){ return false; }
N( A# [4 L( w7 Z# T2 ~% B9 Y$ _ -
" ?5 e3 X0 O: b& I3 c( ]. k - /*判断文件类型是否可以开启*/ 3 ?0 O9 d5 m$ h7 B& P% `, G/ ]
- $type = exif_imagetype($filePath); 8 Z& T( E1 o' r$ r# [
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
) q" `7 t% z# Q$ r* t - 9 @( D$ ]* b' R
- $func = self::$_createFunc[$type]; 9 m' ]6 E5 F! V! n7 ^, Y4 k
- if(!function_exists($func)){ return false; }
4 j% O# c; a" e0 r - # X% O! O5 i* ~3 A
- return $func($filePath);
" j" `& S% _9 N, p6 @) t - }
7 G& n5 @2 {& ] - % ~7 @3 h2 [) A4 k. t, G3 N
- 3 S9 |* w# A; k: B
- /**hash 图片
/ t; \( M7 M7 @1 a/ C9 x - * @param resource $src 图片 resource ID 4 T* G2 y8 h) v+ z( S2 ?% H( M2 b
- * @return string 图片 hash 值,失败则是 false
. e+ b+ k6 c% Z# X4 }" n4 p0 C - * */ + C0 p% Z8 ~5 m3 H, g1 M" e2 Q
- public static function hashImage($src){ $ z$ c6 Y2 @8 V
- if(!$src){ return false; }
1 r% L6 J0 e1 M2 ^ - $ `" @8 K& t9 M! P: Q# q
- /*缩小图片尺寸*/ 2 m# E2 ^4 v3 V1 K9 c
- $delta = 8 * self::$rate; h0 \; C+ f/ I3 K# E$ B$ r5 g8 J
- $img = imageCreateTrueColor($delta,$delta); 7 d5 l+ n" |6 |0 Q+ Z
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
1 p4 G5 R. B2 I' w - & O% @/ t& T V8 o4 V
- /*计算图片灰阶值*/ ) d! y$ A. U. L* o! r
- $grayArray = array();
- H+ m2 ?% ?* C: E$ o8 v# ]( j - for ($y=0; $y<$delta; $y++){
4 \' _5 i# H6 j7 Q, B - for ($x=0; $x<$delta; $x++){
3 B& h8 H9 H1 C% Q9 H - $rgb = imagecolorat($img,$x,$y); / g, T# ]- w% |. L# W4 O: d2 d9 m& d
- $col = imagecolorsforindex($img, $rgb);
2 R7 r& c8 D$ G3 u/ i2 Q! F+ d - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
6 o9 B \/ j- ^) [3 ?2 y) m6 R6 } -
' w( E4 t- ?1 d8 y) u* F- R - $grayArray[] = $gray;
v+ o6 T/ u( ]6 x1 I - }
( S3 J6 x4 e; Z' F% t1 Y% g - } 3 p$ j- s( A- M% t$ G. Z# T
- imagedestroy($img);
" `% | z3 ]4 n - " ~. Q0 f1 Z/ E! B0 S
- /*计算所有像素的灰阶平均值*/
9 C$ s6 }. N. J* g0 q. {: C - $average = array_sum($grayArray)/count($grayArray);
( h! B. ?0 f+ b0 Q - - ]: S# D; n9 c! O+ \! T$ j# Z
- /*计算 hash 值*/ 6 f+ y, I" w. |
- $hashStr = '';
* k" g! Y: w6 X+ ]' N - foreach ($grayArray as $gray){ ) J# T) [7 B! F3 l0 S
- $hashStr .= ($gray>=$average) ? '1' : '0';
]4 q) h: ?. @! l+ F" o; s: k - }
' T- W) H9 J8 p, @5 l - " D( s9 [1 t' J. q
- return $hashStr; " x2 ?2 y$ C, y! p" ~' O& J) l
- } - a4 F q' n" B. z/ x1 v
-
5 x: ~; {# p: ^" X - 7 d2 P: o0 ^, Z. c/ ]
- /**hash 图片文件 1 b$ `8 e1 E) m& z2 L
- * @param string $filePath 文件地址路径
4 \/ L5 c7 e v' ]9 |7 a3 q8 a* T - * @return string 图片 hash 值,失败则是 false
* @8 X7 H# x8 r9 \. M7 z% z3 n - * */
$ p- @1 s. d. S6 `% G: S- I/ M8 U - public static function hashImageFile($filePath){
1 A1 S0 y5 J% L: n8 { - $src = self::createImage($filePath); 0 q+ v6 b: f8 a4 s; q5 X' ?) C
- $hashStr = self::hashImage($src); 6 ]/ @0 F9 C% l2 ~+ K0 e. `0 z
- imagedestroy($src);
! `) O2 w; N9 @0 w2 V7 X - $ Q9 ~( [. {+ I( ?& N: M6 _
- return $hashStr; 8 Z! c% t2 @& }' T. _
- } ( t; @( [( n' X3 U
- # ~9 h* e( n" y! r' }
-
8 Z( ]: v8 N) X# X( Y$ M - /**比较两个 hash 值,是不是相似 : ^9 }3 c; Z: [3 Z& b. k
- * @param string $aHash A图片的 hash 值
0 e4 M1 @3 T1 p- t1 W3 M - * @param string $bHash B图片的 hash 值 c& `7 e0 e1 V! n7 @) I
- * @return bool 当图片相似则传递 true,否则是 false
5 d. v, r) v% g6 S4 M - * */ . ^9 X e) y! t/ e' S F: D. k" {5 { E' Y
- public static function isHashSimilar($aHash, $bHash){
. k$ n. i! d3 S, w; ] - $aL = strlen($aHash); $bL = strlen($bHash); 2 j Y% G' e, _: }. Z
- if ($aL !== $bL){ return false; } 2 f6 p. D* g6 u; |
-
* D/ O# E' y; c - /*计算容许落差的数量*/ - r8 U: d+ j6 V4 ` p4 C$ p
- $allowGap = $aL*(100-self::$similarity)/100;
) N: Q/ W# S. [& w/ f' Z4 o - 9 L% e7 [- {# W( k `
- /*计算两个 hash 值的汉明距离*/
+ o+ X# n8 G5 e: q' I - $distance = 0;
' i) ?8 j$ O+ L5 k) p @, _$ p - for($i=0; $i<$aL; $i++){
/ S1 k* U! K9 ^# l. |4 o# c7 Z - if ($aHash{$i} !== $bHash{$i}){ $distance++; } : J; ^2 k" k4 R6 K: d
- } 8 N$ c5 t- T9 y
- 5 W+ k+ M' d; y& n8 F) V+ I' n
- return ($distance<=$allowGap) ? true : false; ; o9 F. t8 ~, n( f
- }
7 A0 d, U" V5 _, U' A -
1 I( D3 T1 y; t# A - , j; ^" L0 d- \0 m+ `4 J) Y$ v' B
- /**比较两个图片文件,是不是相似 # ?0 y3 \3 L2 Z0 J
- * @param string $aHash A图片的路径
4 o2 n7 o- N# {- r. x( L0 H9 ~ q - * @param string $bHash B图片的路径 ' ^! ~* m% @) U$ F
- * @return bool 当图片相似则传递 true,否则是 false
' Q! m7 E- Y+ j6 J1 O6 @ - * */ : p' W# Y! V C
- public static function isImageFileSimilar($aPath, $bPath){ $ E7 Y# Z$ p0 e! M4 U& c9 R: \0 F
- $aHash = ImageHash::hashImageFile($aPath);
, r$ g/ u: n" c* ~- R5 y" E - $bHash = ImageHash::hashImageFile($bPath); 2 ^6 y; y! [0 ^5 V- ^6 X2 }
- return ImageHash::isHashSimilar($aHash, $bHash);
# p; A! ?5 W- l7 Y# b: c' X7 k. U, W - } $ ^, [* ^- ^# E- T' r a K
- 0 @7 U7 W" g* |+ u0 |$ F/ t
- }
- F0 I( G$ f+ F' f! a
复制代码 " W. s& I7 l E6 ^' p
# k* i% r+ O/ y |
|