管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
6 w- h; m* E; \6 R
1 \) H0 J& m v: q* }8 r. ~
7 c+ W& u: {3 ~, @5 `由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。3 C- R$ D6 r9 A5 ~
- <?php % o% Q) Q% t! G: |
- /**
1 f1 @: F' X! S" {$ ]: ~ - * 图片相似度比较
" y' v, [4 S7 N! o. q: x - * " n6 Q/ V' l; q8 g5 y2 T
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
2 _9 P' d# b9 f* N) y/ O5 G& l - * @author jax.hu
6 ~2 D# v0 X* M6 ^. ~) |4 b8 ] - * 3 \9 \ u w1 t/ x
- * <code> : P7 d5 Q& U8 O6 u n" R7 f) U' X0 X# T
- * //Sample_1 * S R' A( V( @6 b
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); . n& K, a( E: H% t
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
) G$ j& b3 r8 J$ S& v; S6 \ - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); " y c0 M# w6 Y* h3 j
- * / W* ?( N2 w8 s* H* \. o* ^
- * //Sample_2
: p# F2 `, |2 `. q" @; m) S6 Y - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); # C! T/ [( K. N
- * </code>
2 v; o0 e( _" k - */
2 `2 p: Q! O9 _& m -
) d) x* T: x$ s, _) R+ h9 t9 { - class ImageHash { 3 i' S* g5 S+ C, h7 R2 ?
- ) C# O/ `# E; b% L$ [
- /**取样倍率 1~10
3 `7 y* K2 o; v }: [( \- y$ i - * @access public
- L( e+ \' T: `& V4 R1 V$ L' q9 G) r4 G. n+ y - * @staticvar int 2 ]8 C4 u% i9 ~$ ~. W# m+ o0 {
- * */ ; F+ m( J3 U; |2 J3 T1 k% c
- public static $rate = 2;
, W. Z/ [, I! Z -
2 m6 E% m$ C2 u/ ]# S$ V) H8 d# }, w - /**相似度允许值 0~64 ; {; Z( v6 W/ h" x
- * @access public
& n |, G* _5 Q2 Y' r+ M. c - * @staticvar int
/ I+ n' s* R$ j/ t0 G8 v8 l9 M - * */
- K, x) r$ K5 w, J - public static $similarity = 80;
9 Y% k% \4 b/ F) I3 D D2 D4 n -
( w5 ]$ ]$ @: G7 E" n5 s; p t - /**图片类型对应的开启函数 ' V3 M/ m9 T$ Z' c: t4 x0 ]
- * @access private , K9 n- |# e( ^ ]( P k# x
- * @staticvar string
( X/ @/ |2 r' O- e+ ~$ B! y - * */ : K' o2 @6 U& d9 w) l" p
- private static $_createFunc = array( & v1 j P/ i% m! b
- IMAGETYPE_GIF =>'imageCreateFromGIF', & F/ ?6 p( e4 Y
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', 4 }6 y/ b) K3 g1 l5 e/ G5 ] b
- IMAGETYPE_PNG =>'imageCreateFromPNG',
/ @& {4 M5 t: c5 c6 K0 r - IMAGETYPE_BMP =>'imageCreateFromBMP',
" e- \# v" T; B- H3 ]/ W( J - IMAGETYPE_WBMP =>'imageCreateFromWBMP', " O K/ @ a5 `+ C' J
- IMAGETYPE_XBM =>'imageCreateFromXBM',
E. @8 z- y+ N: L9 A+ e - ); $ G. Z$ G% \% C: N y* X
- + L# X Y5 V3 V3 ~# c
-
' \, b5 c# W0 j! A2 o* L - /**从文件建立图片
. n8 X y- {' W% v4 ?: J - * @param string $filePath 文件地址路径
! Y5 f- l/ R$ v3 ^! ? - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
9 a) m6 g- a4 I( o) Q/ ` - * */ 8 O5 j* G" h/ Q8 }$ t8 v
- public static function createImage($filePath){ ! `. i: L* t# `! W# y
- if(!file_exists($filePath)){ return false; }
5 l% }% a k0 X6 f1 Q& y - 8 \! {* e$ W; o5 a5 s- h5 _
- /*判断文件类型是否可以开启*/ 0 g; p5 a' a$ i/ C
- $type = exif_imagetype($filePath);
7 k# K" z0 O$ \% ?7 l - if(!array_key_exists($type,self::$_createFunc)){ return false; } * ^$ [+ J7 Q A/ Z4 |6 R
- ! R7 q# K8 f, b& e& b
- $func = self::$_createFunc[$type];
; q5 l; _; M- F6 A5 m2 Q- ` - if(!function_exists($func)){ return false; } - C$ S; \. [8 M: h4 K/ S
-
' {# m s* B: l2 S8 r( _ - return $func($filePath); 7 z( n; I5 V, h* A
- } ( b% X. C9 G6 K! n: v
- / o$ ?8 P$ j a
- & X, a* q5 y8 a- W& v( r
- /**hash 图片 0 _+ r* i* X! \, L, a- G# H8 s
- * @param resource $src 图片 resource ID 4 V6 B' q+ a+ t O9 A: l
- * @return string 图片 hash 值,失败则是 false
# \! x# E/ W7 ^1 j; O - * */ * B: n& b# h5 h/ d( | F
- public static function hashImage($src){
, ~0 q( D: ?) W2 _$ A) J, a+ H# }: y - if(!$src){ return false; } 6 z; N2 x6 Y1 o; O
- ; r3 F! {$ u4 x
- /*缩小图片尺寸*/
* h& v( P2 ?' k! k# o2 c3 P - $delta = 8 * self::$rate; 7 J& |' ~4 w1 w
- $img = imageCreateTrueColor($delta,$delta); 2 Q0 N W6 h; E5 B; _6 ^
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); * g5 c! I; t5 a& P# i" g
- 3 I: `3 q$ Q, Q! c B
- /*计算图片灰阶值*/
' u, g5 e. X' G+ P - $grayArray = array(); 9 ~) [/ n) n7 a
- for ($y=0; $y<$delta; $y++){ 8 m% o, m2 R2 B4 H" [( w9 r4 h
- for ($x=0; $x<$delta; $x++){
- f. ~5 K0 @6 k( d( k# e - $rgb = imagecolorat($img,$x,$y); ( H S5 v7 T& A, U0 u
- $col = imagecolorsforindex($img, $rgb); ! v% K) F! d! a# i
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 1 t2 ^7 d; ?9 U( J9 o
- # y7 l6 v, C: m7 i |
- $grayArray[] = $gray; ) `6 z: O. X# F+ f
- }
1 J" P0 F* Z' @4 u - } 5 U8 u5 K0 V# F% {( M0 {& L0 ]4 G
- imagedestroy($img); 6 _' U- N( x0 X' l+ @, K4 V% n9 Q2 D
- $ ~6 M- Z; r- ?0 `
- /*计算所有像素的灰阶平均值*/ - t( W7 l0 V8 f, p' I# \
- $average = array_sum($grayArray)/count($grayArray);
2 z* I; x5 n, u$ P; F2 v -
& U7 \, S2 D: g7 p- P- h9 G - /*计算 hash 值*/ , I) L5 j: J6 l/ W' ]% V
- $hashStr = '';
/ T0 r! F8 x7 U0 _! z0 V - foreach ($grayArray as $gray){ ; B4 ~7 P# ?( T# ~
- $hashStr .= ($gray>=$average) ? '1' : '0'; + F/ ^! Z4 E* K) m3 C/ d
- } 9 M. S( M& ]' g G
-
4 S0 Q* \2 Y% D3 H! K2 K - return $hashStr;
# t( p' q/ a* O: V7 W8 w - } + p; C" g: |! r" D: ^9 @" H
- 6 @0 z9 ^2 M3 o1 D: y. c
-
# _) a$ C3 x; `: f3 X% a - /**hash 图片文件
/ I0 e) F, ?! S; C, M1 T) I - * @param string $filePath 文件地址路径
0 w8 m' Q/ B9 b8 X - * @return string 图片 hash 值,失败则是 false & e8 [& u& ^! r" J' c) X, m
- * */ $ [8 s4 G5 z2 M
- public static function hashImageFile($filePath){ - k" V0 [* S8 N& T3 d/ A+ ?. c, O8 C. f
- $src = self::createImage($filePath); : h) y i: U8 {, ^) ]
- $hashStr = self::hashImage($src); 0 [( e' q% z y( N) [6 w3 Y( _0 R
- imagedestroy($src); ' H8 D$ k$ t$ Z# A0 A
-
+ x. y V. g: }" h+ x - return $hashStr;
: Q3 ]0 B8 U/ u( j - } C$ A* a6 N5 Q% }
- 0 S) H, u* r4 Z: T8 f
-
+ ~$ O# @' H8 Z0 D: X# ^ d; Y - /**比较两个 hash 值,是不是相似
& h1 x6 h0 s8 w2 k1 R6 o9 n% d - * @param string $aHash A图片的 hash 值 1 {1 G* @7 B6 V+ r
- * @param string $bHash B图片的 hash 值 " K; D1 d& j3 o; f/ ~
- * @return bool 当图片相似则传递 true,否则是 false
" G9 O1 g, m; E8 R; V6 g - * */
1 X& l/ X. P: c' p+ ? - public static function isHashSimilar($aHash, $bHash){
& K0 v. X/ J$ }* m; J8 ^* s - $aL = strlen($aHash); $bL = strlen($bHash); 5 i6 j' Z1 d$ `/ m0 U4 F' G
- if ($aL !== $bL){ return false; }
' m% D* Y! F: t3 y4 l1 D - : A; y$ _ Z, Z' \/ a
- /*计算容许落差的数量*/ , z4 X' i* s8 W; ^6 _+ l
- $allowGap = $aL*(100-self::$similarity)/100;
6 Z' k. O1 E& T& |4 E* N -
5 ]4 C6 L2 ]0 i; ~, [ - /*计算两个 hash 值的汉明距离*/
+ a# D& V! `6 Z3 V' F - $distance = 0; % `; S, j+ N, r* h3 \) W8 Q
- for($i=0; $i<$aL; $i++){
8 m- R. ]! s$ |0 b - if ($aHash{$i} !== $bHash{$i}){ $distance++; } U6 z6 g$ F4 G% c+ ?
- } " H6 g$ y- T( T. H* p `
- 2 h. c; D$ i# u2 K; P/ G6 o4 y" ^
- return ($distance<=$allowGap) ? true : false; " h% \; _1 S/ f {& _
- }
4 h& H* A& }8 v. M -
0 ?: y% A* q/ v3 @8 a - ' a- ]# D- n9 S1 g: \2 |; }
- /**比较两个图片文件,是不是相似 ) ^! `+ e. b r2 G: x
- * @param string $aHash A图片的路径 q( a2 [# Q% N% ]6 ~/ ^* ^
- * @param string $bHash B图片的路径 % p0 T9 j0 Y2 G6 ~" t# Z, u
- * @return bool 当图片相似则传递 true,否则是 false ' v* n; _- _) _( F. a1 `
- * */ ) e$ G+ ~! [2 Y, q! M4 p
- public static function isImageFileSimilar($aPath, $bPath){
. N+ Z. i& q3 r6 {6 }( w' v% o6 T4 b - $aHash = ImageHash::hashImageFile($aPath);
4 V* I1 E) T* j3 ]. E - $bHash = ImageHash::hashImageFile($bPath); - Y# N3 u$ r x
- return ImageHash::isHashSimilar($aHash, $bHash);
4 B/ v: C! H7 A# v6 h - } ( h4 A0 _( e c: @8 V" m
-
8 Z5 D# [4 }) Y$ v0 ~7 {5 U - }
: d# t4 c) i* y- w" A+ ?, l& U
复制代码 & g7 j) Q( B1 J0 ]+ G- V
* S6 F+ U* G* f) D |
|