管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
J- ?, p( G7 Z9 r4 f" I* j5 ?" @6 y- u. n" s' Q t! j
+ a7 F9 ~4 A9 \4 p! O, \
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。: K( S3 c% u6 R1 J& O
- <?php 0 s9 b; w1 N- o! q R% j2 n2 e
- /** : V6 b: @' I6 [3 M, n
- * 图片相似度比较 6 \5 P E4 Q3 j* d% c
- *
5 m7 P3 W6 `4 g8 J - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
, T n& @* Q9 R5 [+ ~( a - * @author jax.hu
, V0 t4 L6 b8 ]7 D5 K - *
$ z& L g' ~7 E; B - * <code> - P# S, e7 S' v
- * //Sample_1
# r% r2 S2 n0 T$ d0 D8 d - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
e7 @0 l+ y: e+ @+ ]! A1 r - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
5 i$ S* Q% y5 y( S - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 8 D* h# K, A* E; C; F( E" \& ^; X, R
- * ) C$ k8 {- z2 A! A, V6 h* d
- * //Sample_2
+ @; N; ^. l% l& ^ {4 o6 I - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
+ n+ W7 E* O; ?8 u1 Q3 E7 I - * </code>
' ^( v) O" V, ]- o - */ ( |) Z0 x; T0 H: v! _* }5 S
-
) i. J7 G/ H9 P2 B' K3 u6 \ - class ImageHash {
% r5 A7 R) H' W( { V+ @ - % e3 E4 t0 y: w8 l- }& a
- /**取样倍率 1~10 : f# \4 I s V# C3 L: |# S0 x# u
- * @access public
& }) f2 Q. ~$ `6 }0 Z' R - * @staticvar int
/ i2 @; e# X* [8 X - * */ 2 E9 _ z6 ?6 n% v
- public static $rate = 2; 1 ?1 `( Y* u; T9 l w# {! c$ `1 O
- ( @4 \4 B' u8 Z
- /**相似度允许值 0~64 " Y- Q" C/ O# v; ?7 C) H6 W
- * @access public $ j! ]4 X) @. B% ~4 Z
- * @staticvar int x" K& F* b, i
- * */ 3 U* V1 ?# O, k. B u/ @
- public static $similarity = 80; 5 E$ V7 ~# x, z% D+ ~- }3 M
-
6 q" S# T4 L7 n; K+ G - /**图片类型对应的开启函数 + ~# g- f. [7 q9 G! f. Q( M" ]$ u( ?
- * @access private
, i+ b$ z3 ~( ]; m9 W" P - * @staticvar string
' M$ d6 ~# V+ j" w+ }( h8 t9 \3 U - * */
' E0 O2 K* [3 Y4 O% k1 P - private static $_createFunc = array(
$ t8 `; M9 E( d3 U' o8 v9 o - IMAGETYPE_GIF =>'imageCreateFromGIF',
* A! f' n- p. K( l) u% h2 a - IMAGETYPE_JPEG =>'imageCreateFromJPEG', " U: D: T' J, f" ]
- IMAGETYPE_PNG =>'imageCreateFromPNG', % I1 Z) V/ J7 z, l1 j
- IMAGETYPE_BMP =>'imageCreateFromBMP', 1 c+ ~9 c/ \! @, s/ G
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
) J6 u* C9 p# I2 j0 _3 m" s" w5 O - IMAGETYPE_XBM =>'imageCreateFromXBM',
" z, L- g2 I- F - );
6 H1 K- U2 a( L6 M& j& Y- Z - 4 p/ B5 d7 ?5 j1 T8 t
- ; H/ v/ Q1 _7 f- Z+ k
- /**从文件建立图片
& o4 t% Q- X( X' F; w - * @param string $filePath 文件地址路径
; r' O- \8 O+ Q7 T9 K4 L" [; n G1 L - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 1 D/ r5 s0 w5 R7 p
- * */
/ K0 j& D; j* F; }$ D - public static function createImage($filePath){ & ?/ v0 R4 S E
- if(!file_exists($filePath)){ return false; }
1 g4 Y+ ^( q) U1 r! M - 2 L0 v9 @% G8 C/ p" h) g: Q
- /*判断文件类型是否可以开启*/
" ^$ K8 ^+ {' @1 I9 b' u+ y# ~, T - $type = exif_imagetype($filePath); 7 }+ V$ N, [5 V/ h: |" e
- if(!array_key_exists($type,self::$_createFunc)){ return false; } 3 q' f R/ J5 k! W# P" K
- 1 q; \5 ~$ x" ]3 J/ a
- $func = self::$_createFunc[$type]; . `! {$ z8 t5 w& n% I# I
- if(!function_exists($func)){ return false; } 9 Z9 P4 v+ Y7 q& R+ y, ^: l
-
$ H7 p, v5 F, R% z) L: m. | - return $func($filePath); 7 |, e5 ~9 O, n
- }
: h* |8 ]* m8 p2 A -
7 @1 a( m3 r1 s, n, G0 `* ]+ j - # c; ~/ n1 _; G v0 j
- /**hash 图片
) c% s( L; U V& F t4 ? - * @param resource $src 图片 resource ID 2 u' \) }# Z/ r# D7 ~0 @2 s) F
- * @return string 图片 hash 值,失败则是 false * b$ r8 p7 O) S3 V
- * */
* r* C& C8 Q* V3 x( u0 @6 x - public static function hashImage($src){ 1 b1 T: {( ~* d' s, ~
- if(!$src){ return false; } 5 U: `8 x( R. l) m! @5 Q4 q
- ' c, [ e* V$ G* K( u0 {& B3 z
- /*缩小图片尺寸*/ ) I6 M5 J& D3 R* d. @+ l4 a# G
- $delta = 8 * self::$rate;
( [7 N: o' ^, {) x: K5 a - $img = imageCreateTrueColor($delta,$delta);
3 g( g x( F) p w* k3 Y - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
8 `9 e6 t1 A; |3 D! l3 ?4 I/ J- y -
# Y+ O: W: L3 x( K' \; {# o - /*计算图片灰阶值*/
; q9 f* s8 g) d) D! a* ?9 j - $grayArray = array(); 9 b6 d* Q7 k+ C4 Z! e- u
- for ($y=0; $y<$delta; $y++){ & I( t6 F0 s) `( B% P) s3 T1 @
- for ($x=0; $x<$delta; $x++){ M- v/ d) S! e6 I
- $rgb = imagecolorat($img,$x,$y); % j6 s O2 V; d4 Z2 T$ V7 B" X
- $col = imagecolorsforindex($img, $rgb); " y& D1 A$ \' Q: t! g# I
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
$ [5 M! e2 C; r" @) d -
2 ?/ N9 ~/ w7 X. @8 o" f' x - $grayArray[] = $gray;
; f4 `; c" c8 E$ @" Z - }
' E* ^2 @ g4 y8 R g% F. [ - } # m' K3 k$ H( I0 w
- imagedestroy($img);
: W* X+ D6 K4 i S1 T -
3 a1 k5 I7 _ x! B( l# w0 Z7 L8 v - /*计算所有像素的灰阶平均值*/ & @. R1 f( s- n7 N+ _' b
- $average = array_sum($grayArray)/count($grayArray);
& b8 t3 ?" q. x4 i" ]8 i; P' j2 h- S - ! k# Z# V( {6 k+ X+ |
- /*计算 hash 值*/ + _# n" Q) O8 j% x6 ~0 I- w
- $hashStr = '';
; ]9 W3 Q( }1 c4 v! o3 w8 a5 g3 @ - foreach ($grayArray as $gray){ $ E0 V/ g! d; k+ s
- $hashStr .= ($gray>=$average) ? '1' : '0';
7 A: M) s U; F+ u% Y8 B- } - }
+ ^' K! H2 U; s% t7 ?2 a - ! d$ M G9 ^/ w5 U
- return $hashStr; + z$ x5 C* W( B
- } + q: n" ]1 V% R4 R+ V
- 3 S8 R9 C2 F7 p1 q* a% @
-
- K6 Y+ a# P! } - /**hash 图片文件
* a1 n0 V/ H" p* | - * @param string $filePath 文件地址路径 3 `# B8 i) u# T$ Q+ e. e# v
- * @return string 图片 hash 值,失败则是 false $ V \ O, Q# L' U
- * */ 4 j4 h8 p# r' D! @
- public static function hashImageFile($filePath){ ; \5 g% |: O9 s6 d! V: n; \/ ^- l9 h2 q
- $src = self::createImage($filePath);
6 k5 R# ^2 x1 @4 u8 j - $hashStr = self::hashImage($src);
/ w2 W- L0 C- A# R% X8 v7 O - imagedestroy($src);
' \& C# C- M+ { - * j ~1 K% H/ t6 o
- return $hashStr; 0 Y: k" e; u+ q# G; U
- } 7 ?# w; n* @/ v( _5 A l
- 9 r# |0 |! c" x' _, ~0 v" I
-
; J Y3 y$ @! [ - /**比较两个 hash 值,是不是相似
# ~4 P! B( ~, {! ]! W% m - * @param string $aHash A图片的 hash 值 ]2 e$ N) c4 g9 U, D9 A
- * @param string $bHash B图片的 hash 值
# o8 W1 w/ n8 h* J% V6 ? - * @return bool 当图片相似则传递 true,否则是 false
% T/ @) K/ I$ i* \ - * */
# C* o& w7 {+ q - public static function isHashSimilar($aHash, $bHash){
4 \& N: {, a8 T- _5 H9 D+ o9 v- ? - $aL = strlen($aHash); $bL = strlen($bHash); ; O* s6 x, L2 v% {4 s* S/ C. ^
- if ($aL !== $bL){ return false; }
5 ^9 b5 X5 H8 K8 f! z x/ E - ; p) J7 j/ z( P Y( p0 M& V3 l- Z
- /*计算容许落差的数量*/
* f% r$ @8 b1 d5 G5 S - $allowGap = $aL*(100-self::$similarity)/100;
' ]1 W8 N r) s2 J -
# `7 {2 h3 f; N) }* N; X& q - /*计算两个 hash 值的汉明距离*/
0 R8 S( K# ]! ], |) i - $distance = 0; ( w% X5 C/ c4 T2 R. Z/ g
- for($i=0; $i<$aL; $i++){ ( P5 J4 v7 e( l
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
1 `7 U, H# M( v - }
% y0 Z; {, ^* i9 g6 J -
. g' ^1 S) B% A/ W4 ?9 x5 L1 [5 g - return ($distance<=$allowGap) ? true : false; # U' i( O! x6 H7 [! J
- } 3 P7 w! e$ ]3 t$ v
-
2 [4 l# C z; T* r2 t - 9 c- P5 L! ?' M8 M' t# c$ ?
- /**比较两个图片文件,是不是相似 % B6 v1 a: X$ E
- * @param string $aHash A图片的路径 4 v" x/ f+ n5 }1 N
- * @param string $bHash B图片的路径 . T2 G, V' j6 R y
- * @return bool 当图片相似则传递 true,否则是 false
& A; z0 r) E/ q2 l+ d5 K: K! ^# O/ q I - * */
0 L% H+ A- ]( E# u3 o - public static function isImageFileSimilar($aPath, $bPath){ % A: q @: G2 S$ Y
- $aHash = ImageHash::hashImageFile($aPath);
C) l. {# ?3 v9 @ - $bHash = ImageHash::hashImageFile($bPath);
& M/ P( i9 O& z0 B4 W- v - return ImageHash::isHashSimilar($aHash, $bHash);
5 ^& x0 `! {( Q7 s h2 q - }
" g) `4 x( {- h( T( v+ v3 _& u -
; `; H. I2 v5 R% m& W2 t1 [ - }! U- c+ t* p- y: H) ^
复制代码 ; H% o, ^3 }5 M; l$ k, o- o
: s0 A I& e2 w3 {1 g
|
|