管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图3 `- c( s8 _6 a$ \
( Q k' C' s& v7 ^6 l% z6 s. u+ G6 U) [+ F* s
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
+ _* \3 N/ Q& [0 v- <?php % j5 h( s+ }/ M/ @
- /**
6 ?% m1 Z% }2 i - * 图片相似度比较 9 u+ z& l( v8 w4 _5 H, h
- *
. h4 L% G! }$ X8 L3 W" g2 Z% } - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 7 r8 }: c% Y% O# I9 ]9 V. |
- * @author jax.hu 6 i \: J+ T: ]* N8 {
- *
" j6 q; W: N. w& S# o4 \ - * <code>
! h6 @6 m1 r' A- }0 c0 @6 A - * //Sample_1
. |& b! I- J$ Q) m$ F6 b/ D - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); + v! ^4 A8 X' N$ l
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
7 @0 f7 V& f9 ]% | - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); # o6 p; t' Y! g/ ?2 U" d
- * ' ^, E* Z( [2 \$ G& X7 @
- * //Sample_2 , r7 ^' H G r5 K5 \0 e$ p( _# E4 B& n
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); $ @5 j% W3 w6 V' e( E% f/ k) T* k
- * </code>
% G! _ o/ _5 j% L9 i - */ & h& ]2 }+ k* O0 {
- 9 Y3 v+ L2 W$ g
- class ImageHash {
, ^; _/ `: W" }. ?& X" I - ) w9 U6 f1 \$ ~. P. u
- /**取样倍率 1~10
2 y% s' P& C! B" M! B3 v/ F) p - * @access public . K4 ]# |& z; _; V2 v
- * @staticvar int , n3 I" T1 L0 k n5 y
- * */ ( h- X9 |0 d) |5 Q" q& \0 j
- public static $rate = 2; `0 u( V* \5 N `) n8 K
- Y( P5 C. A7 E) b; H4 S
- /**相似度允许值 0~64
/ a7 Q; x; {! Y% Y3 m, x - * @access public
& R3 x5 v6 i$ `1 i* N: a - * @staticvar int
% E( ?& J* ~5 o9 c - * */ . n8 j4 d7 g6 s, Y+ D5 d
- public static $similarity = 80; + j2 ]4 h4 T) u2 H5 I$ C
-
! ?) s3 E5 c% { - /**图片类型对应的开启函数
! g* K: H( L \, B3 \8 U9 P9 T - * @access private
* _; ? e5 _4 S) Y - * @staticvar string ) W( k1 c8 i9 t Z: [. D9 C
- * */ # T/ }6 J: x2 q
- private static $_createFunc = array( ( J* o }$ r: ]& i1 L
- IMAGETYPE_GIF =>'imageCreateFromGIF', * c7 U" E0 a# }7 x ^& t) d
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', 5 S+ O; R2 @+ |: K3 i
- IMAGETYPE_PNG =>'imageCreateFromPNG',
8 P X/ Z* `3 ~, W - IMAGETYPE_BMP =>'imageCreateFromBMP',
; o6 u, U! K# [: N' C: g, | - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 3 d$ t$ G5 y* @0 Z9 F7 d( u
- IMAGETYPE_XBM =>'imageCreateFromXBM', 7 g# [ p- M7 y, J4 x* j
- );
! P1 o$ k6 q. d7 w/ U' y - ' c9 h4 U/ T4 ?" B% t
-
" W+ s/ J! ?0 s3 o - /**从文件建立图片
9 k; _7 [ P# Z - * @param string $filePath 文件地址路径 1 h: `! Q9 W9 N' i l" n& F, s$ V
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ) k/ B5 I$ k3 F6 N8 _, K( l
- * */
9 Z" z. }" h7 A/ P( Z6 X. k - public static function createImage($filePath){
/ j8 J& }$ D& v7 h0 z5 x - if(!file_exists($filePath)){ return false; }
6 r: |9 V% p5 T8 s1 q$ N- X* H -
5 V: Y5 U0 J7 p. M0 i+ I/ H - /*判断文件类型是否可以开启*/
* p" L& Q. A) P1 I& O% x* O0 J; m - $type = exif_imagetype($filePath);
& i% g0 c9 p" I$ q; H" ]' d; ^ - if(!array_key_exists($type,self::$_createFunc)){ return false; }
: d0 @4 V4 ~7 V -
7 y8 y% d9 H4 G' h, g) O - $func = self::$_createFunc[$type]; + C# [( n+ y# C9 e
- if(!function_exists($func)){ return false; } : q* |* r2 T: Q+ |& o1 R3 w
- . I; O/ _- M% ^9 x# s$ _6 B) p9 K: [
- return $func($filePath); 3 K% b6 U* X4 P# Y
- }
0 m& U p# K! H8 O0 { - 4 M1 _9 F! s' e6 }$ U* m8 Y+ a
-
; r$ D8 N% J" B* J - /**hash 图片 $ U0 c# p+ \- n' [# ^, H
- * @param resource $src 图片 resource ID ( o2 @# u$ Y. k9 B* w5 C8 ~
- * @return string 图片 hash 值,失败则是 false
- V" Z6 N5 s5 q9 I8 g - * */
9 W; }- v9 B' U% j" Y0 x - public static function hashImage($src){ $ _3 L' h1 r, z9 F. A
- if(!$src){ return false; }
3 a2 i1 w$ W2 |) t0 c O4 w - 8 S9 U; n! v5 }* O5 [
- /*缩小图片尺寸*/
, \/ J3 ]( B; m! j - $delta = 8 * self::$rate; 4 o9 b4 K* Z% _, k
- $img = imageCreateTrueColor($delta,$delta);
- H; g0 q( x4 g6 E - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); . m- Y- F9 m1 ~# Z( A, k
-
# L) N% P( }8 |" ?4 x' T - /*计算图片灰阶值*/
# q! B( c) ^" ~8 s - $grayArray = array(); # J7 z, O& [( z* |8 D8 Z5 d& R
- for ($y=0; $y<$delta; $y++){
& [0 I% G' j( k4 U+ a- G - for ($x=0; $x<$delta; $x++){ 0 a& _+ I/ s4 W; E7 X
- $rgb = imagecolorat($img,$x,$y);
% s6 c+ F0 {/ b% [" g& A - $col = imagecolorsforindex($img, $rgb); 2 R# v# z# b G) [5 H
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; & e' X& b, F2 Z
-
% A+ p3 k( A' w" D1 w2 i - $grayArray[] = $gray; . N% l- s4 M$ E/ i9 q, l3 N- k& h# t
- }
4 L2 ~$ b; H! a- w0 H - } 2 k2 Z. {! p9 d1 ^4 x5 X
- imagedestroy($img); , V6 m3 ^7 T- t& c+ u1 \3 t8 c
- $ L; p& X; a9 n4 `3 V
- /*计算所有像素的灰阶平均值*/ ; B% ?* X% I* N/ k8 {! Y S. H0 s
- $average = array_sum($grayArray)/count($grayArray);
5 l9 n2 m% t+ _: w, e. R) @ - ( }1 k7 t- C: H( p3 D& J
- /*计算 hash 值*/
: |3 |/ Y! u) \; e- j* k* Z - $hashStr = '';
1 h2 N; W5 v! k1 e - foreach ($grayArray as $gray){
& D0 c; U; a4 F9 k5 u2 s - $hashStr .= ($gray>=$average) ? '1' : '0'; * f& h' t4 M; x$ e
- }
" F$ }0 ~7 g" Y3 h6 Z4 \" c -
( m- w8 e- R, A$ m - return $hashStr;
) |. @; i2 g4 V: a7 s; g* n! A - } # } n; e& {' E8 V v7 }' _. S
- 2 p+ L' W: p% o+ ]/ y
-
4 T* d. q" c4 `* s6 r) ~ - /**hash 图片文件 : ~# [3 D: V& `+ P- E$ h0 b
- * @param string $filePath 文件地址路径
p8 N9 n9 G, k* N - * @return string 图片 hash 值,失败则是 false
" W, S4 f1 Y. n5 ]1 c9 a - * */
7 q. l5 F0 i' K8 Q. y- z# K/ C0 _- x - public static function hashImageFile($filePath){ 8 T2 P$ J& L) e4 `7 M( s
- $src = self::createImage($filePath); ; L) G7 {' _. A, T
- $hashStr = self::hashImage($src);
) b+ ?2 \! d$ V! g9 `5 ^6 w. m - imagedestroy($src);
+ t/ ?/ j, [! O: ^0 K/ H3 b, `2 ] - 0 A) V% }! ^# T
- return $hashStr;
) G: e( D& R' ^, r6 v: u- z - } + |8 v A: d- A" s: z0 g0 B2 E
-
3 C& X+ R/ Q; `# x" N - $ {9 u2 L+ \. v- g- z
- /**比较两个 hash 值,是不是相似
6 g4 j" v8 }. l2 |$ f7 k - * @param string $aHash A图片的 hash 值 2 P; \1 u3 x' z: Y. U) m3 s4 o% Q. E3 X
- * @param string $bHash B图片的 hash 值
, b1 a- d" o# s6 ]( M1 e - * @return bool 当图片相似则传递 true,否则是 false
5 C% c) d7 P& k7 T9 D2 }5 S - * */ 6 b% q" _) Z/ }2 e$ p
- public static function isHashSimilar($aHash, $bHash){
2 ~0 t8 o; M- n$ F3 Y* w - $aL = strlen($aHash); $bL = strlen($bHash); ( |% h3 z: b3 A; ]" N! t" W
- if ($aL !== $bL){ return false; }
* o/ L, s a6 \ } - % d5 c9 A3 v4 g0 I; o& r. j5 C
- /*计算容许落差的数量*/ 1 X; m2 {/ t# x6 ~5 P/ q6 y7 G
- $allowGap = $aL*(100-self::$similarity)/100; - q7 \: v, r8 d' h; S
- ' J9 ?; K* o: Z2 x; i
- /*计算两个 hash 值的汉明距离*/ 8 \6 K& g9 a9 w6 L; e
- $distance = 0; : G% Z7 N% |; D i% X3 O
- for($i=0; $i<$aL; $i++){ & ~; P4 U" m. \8 _
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } ( `* B2 H( v |; ^! N; K% F7 W
- }
9 b2 u( Y8 I3 _2 R2 M+ V# V - + z6 ~/ p& ^% {( u8 L) J
- return ($distance<=$allowGap) ? true : false; 4 \; p/ s6 r/ `5 [/ @& k3 i# d
- }
3 Y. f5 t: s4 q - 1 e# Y0 J& A$ `' I) _' P
- ' V# C; @: W, Q7 \5 @7 t( w
- /**比较两个图片文件,是不是相似
) p( l+ w, i7 v7 }1 ? - * @param string $aHash A图片的路径 # X! q1 i$ J+ }8 t
- * @param string $bHash B图片的路径
$ o/ [8 M* w" R( A' |6 H( {! X - * @return bool 当图片相似则传递 true,否则是 false
$ v! ]' n; q. F% ^% `: u. L$ n - * */
& Q4 R `2 P3 a. Q - public static function isImageFileSimilar($aPath, $bPath){ / q. U6 }9 ]& x1 |$ D2 j
- $aHash = ImageHash::hashImageFile($aPath); : ^3 p( [ ^: k! @) Q, w- n( D a8 V& u! _
- $bHash = ImageHash::hashImageFile($bPath); 5 q) g$ @" M; L( W( x
- return ImageHash::isHashSimilar($aHash, $bHash);
& C; H, a; J$ p$ D4 w9 `- G - }
6 |% H1 q) E4 \1 O: T; w9 H- T -
0 r9 R( N% T4 S/ |( Q8 E - }
: b6 \+ f1 K, X% U0 Z: \
复制代码
* |$ C6 x% m" Z) H9 ]2 e5 J5 \9 S/ _9 B
|
|