管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图, F% u) N4 P2 ^
, `1 y3 l% g* ^7 Y$ \
" `7 S! L" g' p5 Z由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。' Q u" j: _9 ^6 }
- <?php
8 h. a# \, V, W0 |# l* K/ m3 r - /** , o: l5 f( m8 m0 t3 g
- * 图片相似度比较 ' l& a/ J/ w' z/ @2 U2 w8 f
- * 0 I7 u2 C; j. W: _$ C# \" R$ [7 ]
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
1 Z- ~: P9 ~* X b% ? d - * @author jax.hu ) ~1 c( K* o' J/ ^/ W3 R; w& c: r
- *
7 g L2 d% w6 n, {5 f9 O$ r - * <code>
" z- [2 t+ n5 {. O - * //Sample_1 2 |! J. B$ \: a! e" [2 d
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
/ {. e6 y5 H! A1 i1 A - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
6 M0 U3 l8 |6 F9 E3 j4 t1 m6 t! ] - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 3 W. K1 s$ L: P- _
- * . M6 L( [+ V* J5 y/ G; P0 c
- * //Sample_2
" a9 H5 ^) | D [0 f- ?! _/ O - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
- C) y. u# H1 Z4 ~% J- m2 k - * </code>
7 g5 [! G4 u* o: ^% ~ - */ 0 Z# H" C$ }, t0 d8 v0 B& e2 O
- . O/ o1 h4 z7 N1 ^$ C T
- class ImageHash {
* M& J( \, ]) |- B& H& m* g -
7 b; M6 a$ K- y! x$ a - /**取样倍率 1~10
2 s$ K8 L- l% o4 Y+ r& ]# C7 ~8 ^+ [ - * @access public ! R W) c5 o6 I
- * @staticvar int
1 z$ q( \0 s' z& j- {, i - * */
/ a' h" ?5 N2 K- `1 S - public static $rate = 2; ' U: g7 Q+ o; y6 |" v$ I5 I8 ^
- + v4 u6 _: H6 [( F
- /**相似度允许值 0~64 4 k9 c" t) T* G4 a2 m
- * @access public
" K m5 z; l1 {$ G0 d8 o$ I" w9 @ - * @staticvar int
+ u% _+ h& g8 B' `6 {7 J8 o) R7 G - * */ 0 C u( o& Q! \1 n `4 V+ `
- public static $similarity = 80; : N B( M, W, E B# L) A8 F. v
- 0 G9 L- P3 o" {# t4 Y: U- M
- /**图片类型对应的开启函数
: x" I) D: c* L8 i+ m8 r# z - * @access private " e+ d9 l9 l$ u
- * @staticvar string
) M! Y- C7 m( L3 h - * */
! F+ h9 r) D& }1 X0 C - private static $_createFunc = array(
( ^4 a9 o/ o4 O7 m - IMAGETYPE_GIF =>'imageCreateFromGIF',
% c- x' D! W( R! @" N - IMAGETYPE_JPEG =>'imageCreateFromJPEG', # `% H1 h* Z, U1 n% [" M# I
- IMAGETYPE_PNG =>'imageCreateFromPNG', 8 J8 t0 u+ P3 R4 | b
- IMAGETYPE_BMP =>'imageCreateFromBMP',
& z2 F; X. D Y, Z8 f a" e - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 5 O2 N! m; a, L5 @& f$ H" c
- IMAGETYPE_XBM =>'imageCreateFromXBM', 3 `5 r& l3 v! Y. ~/ _
- ); : e: t9 e% a; k! G& L
- $ I# Q# y/ p/ T W5 G. w
- ' ^4 X, X" C. q, L( }7 o- L
- /**从文件建立图片 8 ?$ I6 J! O! d2 H
- * @param string $filePath 文件地址路径
( W6 y& {7 }6 j! U5 u, | - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 5 d W; I: m$ U- \
- * */
. ~! P% m3 H3 b( d" t - public static function createImage($filePath){ 8 ]1 x# @9 u+ y* I/ j9 P. z
- if(!file_exists($filePath)){ return false; }
$ d; H/ S& N9 F2 t7 L, } -
$ l+ N" Y- E4 c/ l' B2 G$ q - /*判断文件类型是否可以开启*/ . o( x/ Q$ `& V# f9 |' g# N
- $type = exif_imagetype($filePath); 2 u, ]7 n, [2 s4 ~
- if(!array_key_exists($type,self::$_createFunc)){ return false; } $ k( k9 Q3 G; e: L
- 6 i" v( ]9 i. _* P5 Z
- $func = self::$_createFunc[$type];
4 g o% X% p; d9 n/ w - if(!function_exists($func)){ return false; }
|) z: D9 c" p3 f8 ?9 n1 \* |" }/ [ -
4 q7 p X6 Z1 [" V - return $func($filePath);
9 T' y, `9 C# d1 L - }
+ l6 _6 n4 h# u3 {2 {. F$ S, a1 K9 i -
+ ` c& A' B- S x# ^- | -
% K8 [7 ^: |: R. T5 c- l8 v: `( y5 V - /**hash 图片 " b2 g' ^: O, \3 n
- * @param resource $src 图片 resource ID 6 }1 E# E3 D( i7 u! }/ D
- * @return string 图片 hash 值,失败则是 false
& D8 M7 F a; r4 C8 n - * */ ( E N, `9 K8 m j9 ?4 I) z
- public static function hashImage($src){
5 @# t' e" n9 I - if(!$src){ return false; }
# D8 R* z! S; f/ t7 K -
% j5 O# U `3 k7 f) H - /*缩小图片尺寸*/
3 P4 }4 d+ t& A/ ^" l& Q: t - $delta = 8 * self::$rate; 3 q4 _& P. u$ O- y! h6 L
- $img = imageCreateTrueColor($delta,$delta);
0 C: p. }: B$ d. N: d - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
8 J% ^0 ?5 U% ]0 k - ' s& z7 `1 Q- A
- /*计算图片灰阶值*/
C! o6 w! v0 ?3 S - $grayArray = array(); : Y) G4 |9 H. I( b# m
- for ($y=0; $y<$delta; $y++){
. L9 N* [8 [$ B$ i, T: s! \0 {- ^ - for ($x=0; $x<$delta; $x++){
% j4 w2 ~3 F" q. k, R# i - $rgb = imagecolorat($img,$x,$y);
% }7 j. F" Z6 ^7 V) W - $col = imagecolorsforindex($img, $rgb);
8 Z8 T3 m* Z" Z- g, g% Z - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
# `2 D; \3 f+ L5 g -
1 k( \: p2 O4 J3 U) L- F; z - $grayArray[] = $gray;
: K+ a) Q8 ]4 x' e! n& Y, E- U5 j - }
. H9 V$ l t' m: @ q( [# Z/ \7 h - }
' [4 F% C! R0 Z) M - imagedestroy($img); . ^0 j+ `8 a, v4 Y
-
3 F& t7 d: J9 B8 b: t5 e* b - /*计算所有像素的灰阶平均值*/ : y4 g. }) q2 [( v" |3 G
- $average = array_sum($grayArray)/count($grayArray); , ?/ q ?+ |6 J k" ]
-
* x* r, M+ @# \) Q& |" ~. x - /*计算 hash 值*/
4 P' O, v: s, U. l% p, L! Z) Y - $hashStr = ''; & i4 p7 S+ a( v
- foreach ($grayArray as $gray){
( e/ U7 p, j2 G* t- J - $hashStr .= ($gray>=$average) ? '1' : '0';
; ^- ]1 M0 E# [ t - }
! V! P3 L \6 P3 u ~2 D6 x - + ~7 O3 ^, }( l# y1 q6 K! j9 M
- return $hashStr; ; u. L/ l* p/ }1 X/ u' K: W- i
- } ) C/ J" X( C2 L4 k5 z3 h6 s& p" [
- 0 s6 p4 Q" E$ e# m# D; |/ i8 q" u
- ; q* `# B2 |1 h" m
- /**hash 图片文件 2 M6 z1 |7 g% x+ Z; W) J
- * @param string $filePath 文件地址路径 ; b7 T, H+ d* [8 Y6 t
- * @return string 图片 hash 值,失败则是 false
1 }5 f4 y2 R* d - * */ 4 k1 ?# u1 ^# h% f* K5 w$ Z& G0 ?
- public static function hashImageFile($filePath){ l- [8 R8 I/ I- f: T* N8 y! T
- $src = self::createImage($filePath); % B' `- h: |( }. C t
- $hashStr = self::hashImage($src);
+ k/ e: x+ k& Z - imagedestroy($src); ' M7 v( m% r8 ]$ L4 r7 p9 [& S
-
# U, u" Z: m; v5 `2 Y - return $hashStr; ( \0 }2 ~# ]8 P* C4 J! R% b
- }
, ^9 S; _8 ^% f* T8 x -
1 w, i$ I4 D" J4 M -
+ C! e8 \: K) S0 C( v - /**比较两个 hash 值,是不是相似
8 X! T8 n9 Q- Q' ^8 n7 T - * @param string $aHash A图片的 hash 值
+ [5 \, Z- n+ g - * @param string $bHash B图片的 hash 值
1 y5 ^% |- Q. V) x( f - * @return bool 当图片相似则传递 true,否则是 false 4 R" @5 z6 B& w: t5 O
- * */ ' H1 w% e1 b+ Q3 S: ~% o
- public static function isHashSimilar($aHash, $bHash){
9 I% l, V. I% h6 l; z* d# `( P! @ - $aL = strlen($aHash); $bL = strlen($bHash); 1 k7 d4 ~3 P, e0 i' i/ ~
- if ($aL !== $bL){ return false; }
. A; \1 @, U! m# y f- {4 y5 E( F - 7 q: \) x2 Q7 e
- /*计算容许落差的数量*/ 8 e- X: S% L6 v; i' U
- $allowGap = $aL*(100-self::$similarity)/100; 8 T6 T* |) C7 D* f# i1 w9 k
-
& [4 t+ p% }& r: ~2 ]- o - /*计算两个 hash 值的汉明距离*/
: _8 i1 E6 M% r: Y - $distance = 0; 2 [/ X- b J/ B: ]
- for($i=0; $i<$aL; $i++){
( T6 v$ q* P6 ?4 X b - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
, _$ d- t: P0 N3 m - } + k7 P" n% h; ]% t5 \ ?, F2 Q' ?
-
- ]. P i3 v d5 \( O; b - return ($distance<=$allowGap) ? true : false;
# {* P2 i" Q" }" x - }
) J5 b. e$ N7 p0 ]" B4 K: T' ]! W' \ -
) R0 i6 Y5 e/ z8 r -
0 X) q3 J" K4 F0 Y1 O) | - /**比较两个图片文件,是不是相似 : Q7 z9 R5 M. W' F
- * @param string $aHash A图片的路径
4 a6 B0 N0 ~* x) S2 w - * @param string $bHash B图片的路径 ( ~) v7 l0 a7 A' c& h" h: d6 h
- * @return bool 当图片相似则传递 true,否则是 false
a$ r/ p. N' q O - * */ 5 B( \) d' D# @! [
- public static function isImageFileSimilar($aPath, $bPath){ $ ~7 I7 [# p5 {" f `
- $aHash = ImageHash::hashImageFile($aPath);
( h0 p3 v. A2 B# p. v - $bHash = ImageHash::hashImageFile($bPath);
( R F9 p0 f; A1 L; a - return ImageHash::isHashSimilar($aHash, $bHash); . b+ C0 l) L/ U
- } 2 w" l/ Z+ l; A, _! l
-
/ W; R1 F- N: ~' p - }
- o' y" S: q. r7 M) h* y. r
复制代码 ( k3 s6 h) J9 { Y2 `$ \, U
! g& L8 u# Y# }3 |) a
|
|