管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
3 v, g$ V4 A; y2 q: @1 R+ F, ~' R, D
: c) B7 a9 z1 O9 w( b- X由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
% R: J0 I8 M Z$ x: P- <?php . y6 p; o0 L( D1 O1 r4 p
- /** . H( k7 w0 w3 w4 c' l
- * 图片相似度比较
" W( W: E( R5 G( D - *
, Q7 {# J$ m% s - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
) E# a( C5 O% f. j2 L. k1 D - * @author jax.hu * |( |+ b7 I* i
- *
0 o: } ~! [9 i - * <code>
) t* ~% m/ C( y4 |$ b - * //Sample_1
5 h. P8 Q: e! v+ |4 D- o - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); ) f/ J% Z( g4 P0 V# r6 R
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
, A% g2 @% P A+ @6 O - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); $ ^0 M+ l5 _% U- u3 V K
- *
" j# f: b' ?- D6 y - * //Sample_2 8 t) p3 z' Z# Y4 {5 S
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
. ?5 j. ^' Y: N( _3 _ - * </code> 3 m. d5 M8 Z2 k) `* h+ y' e) I
- */
# z3 X" O4 [. V# k- s2 \8 z2 L - " o/ O5 G' V j: H9 K, F
- class ImageHash { ' Q: Q9 y- g+ a, U- M d( h
- + V# b. Q. s, w6 e
- /**取样倍率 1~10 * X, h2 ]. z$ S' B- g' y( W& v
- * @access public _0 p# h: Q, D0 G
- * @staticvar int ) M3 y, ~! j, X
- * */
3 E% i& H m/ T7 R - public static $rate = 2;
% Z, j1 `9 [+ j: g7 F -
7 t9 u* ?. x" @" W4 w - /**相似度允许值 0~64 1 s0 N7 l2 {$ L4 P f
- * @access public 5 E2 Z! | z+ G" G. @/ ?
- * @staticvar int
' |, H. N6 [7 D2 n- E( G1 b - * */
# T# P- w' C1 ]; B - public static $similarity = 80; # B" O! l+ g, v
- ' Z, l' g9 J- o1 ~( u* U
- /**图片类型对应的开启函数 $ a. m9 }7 ?+ L% ~% f
- * @access private
2 F# G, d4 f% j+ ^: { - * @staticvar string
& ?8 b8 w* ^! H2 N# K1 p* g - * */ " i; M% h! T% [& R4 S; D) }
- private static $_createFunc = array( 0 k+ |0 d, n2 H+ R `( g
- IMAGETYPE_GIF =>'imageCreateFromGIF', 0 O) P3 d* l( P! U+ t8 S" i( _
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
' I+ W" }2 [0 C# { - IMAGETYPE_PNG =>'imageCreateFromPNG', * H; A% I% T2 b/ q; ~
- IMAGETYPE_BMP =>'imageCreateFromBMP', ( e& X9 m; \* L9 U1 v+ q, a
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
- E/ o6 _) Y/ ?: d3 F( ^) P - IMAGETYPE_XBM =>'imageCreateFromXBM',
# M. s7 J* q; ?1 Z - );
6 D1 f' B4 f9 p( A5 L+ V/ Q - - ^4 y4 |5 b4 O. |4 A* ]: y0 n0 @* Q# m
-
5 g& d# Q- B, b0 m' _1 \: w9 m - /**从文件建立图片 + |9 T- A/ \8 y/ Q2 ~
- * @param string $filePath 文件地址路径 / L ]( x9 Y9 q& A
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 6 o& t5 `, n* g8 W
- * */ 0 P5 ^+ p% A$ N
- public static function createImage($filePath){ 6 }* e+ A/ h% Q9 ^# c
- if(!file_exists($filePath)){ return false; }
4 j) B8 w8 g3 | - " G9 y7 ? w( H' X/ S, W
- /*判断文件类型是否可以开启*/
. x4 e( b3 W/ G* m% G% y5 `, ` - $type = exif_imagetype($filePath);
6 c* |* g4 ^# e" \ - if(!array_key_exists($type,self::$_createFunc)){ return false; } , I7 S1 n' U3 [" Y9 t* A
- # ]5 S7 a, \9 P$ Q
- $func = self::$_createFunc[$type]; 8 @. v( o# G N' i
- if(!function_exists($func)){ return false; } 9 i7 ]" u* x$ G
- 1 G" M- E6 ]4 ]( m: h- F
- return $func($filePath);
( M$ B' |) ~, [0 M/ R - }
# h8 E, F; O6 u$ ?! T- Y- I -
" G8 b3 J, k d - 8 }4 y& P0 }. p1 Y1 M# H
- /**hash 图片
6 L7 m9 D/ k+ K& Z3 Z - * @param resource $src 图片 resource ID , |7 R/ [" ?! \+ A- g* }6 Q: t
- * @return string 图片 hash 值,失败则是 false
; R( U; ?$ `0 r* G9 z+ N - * */ 6 q! g- U% h f2 x$ q4 U
- public static function hashImage($src){
5 x+ B% M! x4 u" } - if(!$src){ return false; } % _. Z* v; E L* }5 p" G, _; f
-
* ~) v* y7 W6 J8 l! v - /*缩小图片尺寸*/ 3 W; q$ X& ~3 c/ [4 Y2 H% j
- $delta = 8 * self::$rate; & _2 j" A, ]% D) v$ L1 W
- $img = imageCreateTrueColor($delta,$delta);
. ^) P' A) Z* V - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
: E" S0 N7 s8 {- B* y - ' Y% v: S- h$ A: s9 k$ {7 l7 e
- /*计算图片灰阶值*/
; _, S0 @9 e& w5 ^) a - $grayArray = array(); 3 t n( H4 K4 R' H7 e4 S7 _ e
- for ($y=0; $y<$delta; $y++){ + n* F6 R! k8 i) ~: k8 x
- for ($x=0; $x<$delta; $x++){ 7 {& {- h' i3 u' p% w
- $rgb = imagecolorat($img,$x,$y); 8 o0 o1 g3 Y: ]( T/ z! s
- $col = imagecolorsforindex($img, $rgb); 2 C Z; c& i5 }; d4 {# Q
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 1 v# e3 k( w0 u M/ J
- / L/ M0 A' k# M1 R+ i
- $grayArray[] = $gray; 1 y% C7 M& T! U/ {
- }
8 c( v0 b/ n3 c1 P( @$ } - }
9 M6 P' I! ]: t7 V - imagedestroy($img);
7 X8 o* o6 F2 e% _3 A: h) Y -
- D; D {% c2 i( n: P M" x# V - /*计算所有像素的灰阶平均值*/
u( q8 ~9 k3 ^9 C4 @ - $average = array_sum($grayArray)/count($grayArray);
$ v! u4 g8 s5 @( P - ) v1 D# |; _- g5 B0 `
- /*计算 hash 值*/ ; c2 ~: |; }3 \6 M, U; W
- $hashStr = ''; 4 Y* U: @) z( U$ C, z
- foreach ($grayArray as $gray){
+ M6 O* S+ Z4 M) S7 u" u! n - $hashStr .= ($gray>=$average) ? '1' : '0';
$ i3 r, R, z, w" y7 p. O - }
0 g8 p- X9 s$ n5 J- S -
/ F' B @) {' t! C - return $hashStr;
' s& `, C1 u" Z& M - } ! [9 X2 x, @/ x+ V
- 4 {; a! T1 J- K
-
7 X5 N% ^$ n. c4 } - /**hash 图片文件 ! L1 ~- U2 ?. _4 Q) z! v/ o
- * @param string $filePath 文件地址路径 ! `6 m, ]3 r* W5 i# N4 `
- * @return string 图片 hash 值,失败则是 false " a2 s2 z* ^* W$ c
- * */ ! ]2 c$ k9 u, d, J2 @
- public static function hashImageFile($filePath){ - j0 q0 E0 o( I; }6 W, X# \- @; p
- $src = self::createImage($filePath);
1 V7 d |+ g9 a9 }; { - $hashStr = self::hashImage($src); 0 @3 q" w% k( I$ t" G( p! v+ q
- imagedestroy($src);
, o9 F0 p$ n! K& w: d/ q! Q' k -
4 t$ Q5 }% p+ l' q4 |% D - return $hashStr;
6 W5 A$ B) ^4 Z& F" N, x) N9 c: f6 K - }
, [$ y, L' O$ ^# u+ w -
- m' o4 x4 ?' ~% z' S - 7 M# m8 C4 s) K9 x
- /**比较两个 hash 值,是不是相似
# A- F3 o# W2 l2 s0 T, v \' J - * @param string $aHash A图片的 hash 值 - i8 X7 H7 ~2 G
- * @param string $bHash B图片的 hash 值 7 e ?: E# Y% U0 X% Z& E) l# b
- * @return bool 当图片相似则传递 true,否则是 false
; f: A8 L/ N# @( g. {- ~0 F7 n - * */
7 t: Y# E9 v: U K - public static function isHashSimilar($aHash, $bHash){
4 b2 x. R. v% @' f- U! ]7 R - $aL = strlen($aHash); $bL = strlen($bHash);
3 x9 T7 B i, k" F: | - if ($aL !== $bL){ return false; } 9 z9 Z. q/ i; P0 K) }) X! l4 _
- ) W5 @0 ~$ e& R0 ^
- /*计算容许落差的数量*/ 3 ~, O; d1 F, M% `, @2 M7 ~) Y
- $allowGap = $aL*(100-self::$similarity)/100;
& k4 S; O2 F" v - + B$ G- `' Q- I
- /*计算两个 hash 值的汉明距离*/ 8 {0 ^* Y4 z0 ~1 h& E0 r
- $distance = 0; ; G' L: E1 n& ]2 N
- for($i=0; $i<$aL; $i++){
3 Q2 o! i" y7 o9 Q4 `5 X O! m - if ($aHash{$i} !== $bHash{$i}){ $distance++; } , a* }; v6 i# r, P9 E1 }
- }
; R8 u5 T+ D3 S5 n5 o: e -
8 t* J; K# e. a* k - return ($distance<=$allowGap) ? true : false;
4 R9 Q4 u, v8 i( F8 ?) ? - }
. N8 O! ]- _2 I - ' p. K( J8 A: j
- . H# e' ?7 x; t
- /**比较两个图片文件,是不是相似
. U0 t$ d5 f8 N( Y - * @param string $aHash A图片的路径
$ n" t2 d# U. M u2 k9 X - * @param string $bHash B图片的路径 ; d; F+ I4 e' r% W" c5 U3 C6 r
- * @return bool 当图片相似则传递 true,否则是 false + J& ?5 P- Y( N$ n i
- * */ 8 v z! [& j% f4 X0 e! u$ g
- public static function isImageFileSimilar($aPath, $bPath){ - ]/ j9 }* S* e0 K7 n+ [
- $aHash = ImageHash::hashImageFile($aPath);
, R% r% C. [6 q+ t4 U7 e% l - $bHash = ImageHash::hashImageFile($bPath);
& t# }0 a1 {6 ~& Q4 a - return ImageHash::isHashSimilar($aHash, $bHash); + R4 W1 j1 T, Y$ e0 l
- }
, X" B8 y* X# L+ Q# j0 O" d- N6 ` -
$ F. |1 }- ?, {- B - }) O$ O9 _) U! q
复制代码 % w9 F T, V& u5 U
# _5 T( w' s- P( p |
|