管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图* Q+ i; H) e J
$ P9 ?3 s& S/ b- ?1 V- u5 Q
3 y/ ]/ i6 _' ?5 v/ X由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
* s# k) z% @6 G% L" Z7 B- <?php 7 A2 Z9 p9 y& ^: \
- /**
; \& G1 b( ^! ` - * 图片相似度比较 9 c( u& C; ~( H, R
- * / P& I5 q' e7 j# C, y7 D# P* K
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
, z8 Q! B- ^6 c - * @author jax.hu
/ d) E ^7 W9 A$ m5 W - *
( s& U1 P6 R* G% m: F - * <code> ; x) v+ d! h2 |- Y; w
- * //Sample_1
( L" `2 b* w5 }; T( U - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
4 U! m4 a' z: R& p- x) h7 n9 f - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
6 _( G7 K1 m: Y& q- P5 T( R - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
" ]9 w7 p+ W" c* P* x1 T - * & |2 z) M. A5 y$ C Q B c, k* S
- * //Sample_2 . K g( i9 E0 V* T
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ) }9 f5 S! {+ m. m( n) f
- * </code>
6 f$ i0 B% x3 l" Y - */
% j4 D% y% p! R9 J& X5 o- ] -
) I! y: `- N: q4 Y. j9 K( g0 } - class ImageHash {
% c- {3 D# m1 D- ] - % T8 V. H" J$ k8 p/ m3 k$ ]
- /**取样倍率 1~10 4 Z$ ~( W& k3 ~! c# u6 O
- * @access public / p1 O% {/ z. o% M- O) W
- * @staticvar int 0 C; ]( K1 ?* K4 l' K
- * */ 2 ^2 T4 ^( n; f# q0 k
- public static $rate = 2;
# O* h, n* y' M8 w# o! a1 m6 v. w9 U -
( j+ s0 p) U0 r9 D2 B7 R - /**相似度允许值 0~64
" A; Z( d# D7 S+ J4 p - * @access public
_# Y! M5 T9 M* {% J. W$ x j - * @staticvar int 2 o* w: l. P0 w6 ]- l+ L. g. D, f
- * */ / [+ b* T k) G
- public static $similarity = 80; 7 E T& V7 T( a1 Z
- 1 ], Q6 I% {1 d1 J \- {' O
- /**图片类型对应的开启函数 / t/ A: {- @) U8 }8 B; M
- * @access private 4 ~; A8 k& o1 S2 R4 }) d+ v
- * @staticvar string 8 g. e# n/ j$ @! b4 U5 T
- * */ 6 R9 j" A" b! z% z0 D
- private static $_createFunc = array(
( H5 n+ R3 i# A. n - IMAGETYPE_GIF =>'imageCreateFromGIF', H$ j: l/ \& Z( [. O* ^5 M5 z
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
$ ]" ~/ f& Y/ X U - IMAGETYPE_PNG =>'imageCreateFromPNG', : y A# `+ p8 O5 }
- IMAGETYPE_BMP =>'imageCreateFromBMP', - ], ?# o* G* G$ H2 }* J, @9 m- E
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
1 Y" y" b9 y3 f) F. _ - IMAGETYPE_XBM =>'imageCreateFromXBM', 3 ?' ?' ~& L# }5 y* q% k
- );
0 ?7 `2 e3 n* n! i+ x s" j! K -
7 ]" _+ c/ x# N -
* C8 i/ |: ]; I4 W) G - /**从文件建立图片 + ^& q; t2 [. {8 Y8 Q# X
- * @param string $filePath 文件地址路径
+ J0 Y. y4 P! k5 s8 S+ u - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
3 K5 w- [6 n7 i T/ z1 t - * */ ( o9 p5 V' ^. K
- public static function createImage($filePath){ ' Y& L: w( q3 f. W F" s' S' U0 w
- if(!file_exists($filePath)){ return false; } ' d9 u3 i- p- R% ]# S
- . I0 _; p( ^+ d; U
- /*判断文件类型是否可以开启*/ , r: y5 y1 i. T" V
- $type = exif_imagetype($filePath); . G5 O5 x! T' X" A6 R/ d6 g1 p
- if(!array_key_exists($type,self::$_createFunc)){ return false; } 9 a+ |9 n2 E$ E5 A) M' W
-
! `9 V% M% R q5 ^% A - $func = self::$_createFunc[$type];
/ w8 D/ V, o( T8 T - if(!function_exists($func)){ return false; } 8 a. Z8 i4 O: @$ ^3 }3 [% q
-
& ^0 o! G, i: \$ g - return $func($filePath); # f" d% c+ A; ]# l, N
- } / ?: \$ m0 Z8 P( t
- , g4 M3 n/ |! _& ~4 v( h; W
- - F9 O3 s! f1 ?6 j0 q6 ]
- /**hash 图片 ; i! v X0 v0 |' |7 ~
- * @param resource $src 图片 resource ID
2 Y) c) n0 {2 E* ]& r$ m2 [5 V - * @return string 图片 hash 值,失败则是 false 6 K- J, G' l% l+ A
- * */ : a* f( {- G5 \
- public static function hashImage($src){
" |9 s* |4 p3 S/ U- H |( ] - if(!$src){ return false; }
' p2 a) D! _/ R( w) O4 X$ q7 J - : m1 Z! \% d: }% C. v: g' u
- /*缩小图片尺寸*/
" T# f/ y$ T- C$ `$ p - $delta = 8 * self::$rate; 4 V) C I6 ^. K, U" E9 g4 O
- $img = imageCreateTrueColor($delta,$delta);
5 i! ]8 n! X& Z& Q- o- N8 p) C4 A - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
& I1 A; m; E( J - ( ]/ {) R3 Q4 H z6 q
- /*计算图片灰阶值*/
: ]2 [" j9 R p4 p5 C/ J - $grayArray = array(); ! w5 y- S8 V- P! t1 H0 p9 Q: E
- for ($y=0; $y<$delta; $y++){
+ D. h5 I3 K0 n2 x1 C1 ~* X - for ($x=0; $x<$delta; $x++){
! d4 z: b- \6 `( Z) {+ b1 H- H - $rgb = imagecolorat($img,$x,$y); - U5 t4 k' p! H$ w3 k1 h& Z
- $col = imagecolorsforindex($img, $rgb); . [" H+ K/ n- @- j! G( a- j
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
& z1 ?5 @& t6 `# h! f& Q7 l - ; N( S0 I& {' G# e. p
- $grayArray[] = $gray; $ i# J" E+ g. b& |
- } - t9 \2 ]) F% q
- }
6 C8 x7 e' o/ d3 C9 `( |$ x2 x - imagedestroy($img);
) h2 @: |; S% x9 n8 L8 O. X1 p -
$ N# P5 G5 l! n. E - /*计算所有像素的灰阶平均值*/ R- \: D8 k* E) {; @
- $average = array_sum($grayArray)/count($grayArray); 7 I, ~4 _' Z* E/ e& X
- 6 v5 F% c0 W3 Z' X' M
- /*计算 hash 值*/ % i9 ?7 u! o0 R9 @
- $hashStr = '';
+ q: Q- F/ V+ q4 v9 ]6 u - foreach ($grayArray as $gray){ . M5 d/ l8 x+ q1 l
- $hashStr .= ($gray>=$average) ? '1' : '0'; 2 }, w3 _8 o9 E; l
- }
3 I5 i$ X* w2 {- b -
; u$ d9 p; d% F6 N! {4 I - return $hashStr; 6 Y5 m, B/ R( B0 M0 [
- } - n9 \: Q) B) W- Z N$ ~" z1 e3 U. |" z
-
8 o4 B" N) ~. z" c& y - * u# K; A. N! _6 i
- /**hash 图片文件
# ~. d, g8 T. G, c6 x2 y& b" [ - * @param string $filePath 文件地址路径
" f& `& u% f3 b - * @return string 图片 hash 值,失败则是 false ' Z' ]* s0 X$ N2 H! T3 y
- * */
. f, ` k* }6 p# A2 m& x - public static function hashImageFile($filePath){
7 u6 ]# c/ W, D/ t! w - $src = self::createImage($filePath); " T5 B# k9 g2 r
- $hashStr = self::hashImage($src);
1 g [7 e9 |, f! _ - imagedestroy($src); , c' L2 J, G2 Q! f0 \
- ; {) h" [% J, h( F. {# v' _
- return $hashStr;
s* Y6 x8 Q2 I. G( Y - } 2 f; F5 n$ n4 J/ \" w4 N* V
- 4 m$ |' y: D2 ^! O6 `
- - @3 h# N2 E& q B3 l/ A
- /**比较两个 hash 值,是不是相似
' m6 f2 Z" X6 p/ X& {8 d; v `- f - * @param string $aHash A图片的 hash 值 8 K7 U! e) s. B1 |1 x' f
- * @param string $bHash B图片的 hash 值
! b: f h& D$ p+ A5 d2 G - * @return bool 当图片相似则传递 true,否则是 false : @1 h7 u7 z1 H7 `" x7 [: L
- * */ ! C: u+ m( g, z/ G: I/ D% O
- public static function isHashSimilar($aHash, $bHash){ ' U, |: p/ G" a' a7 c
- $aL = strlen($aHash); $bL = strlen($bHash);
! s4 @4 g) S% F2 g+ ]' V4 ` - if ($aL !== $bL){ return false; }
& }- t- }. q7 l1 h2 I7 n+ Y/ ] - 2 ]. t C6 U& {4 _
- /*计算容许落差的数量*/
! W. \# Q) v) l& m t9 h9 u - $allowGap = $aL*(100-self::$similarity)/100; ; e ?. I- ^# Q4 f
-
9 V3 z. t2 m$ u8 k* H: T; l9 N - /*计算两个 hash 值的汉明距离*/ * e# k6 ^& E+ z2 j' V7 v
- $distance = 0; 9 m) P9 ]$ v1 S* u6 J6 w
- for($i=0; $i<$aL; $i++){
* ?; G4 I1 I5 T( ?9 K& V - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
$ }. `, [2 Z2 l8 s. {! ?) h - }
8 p% M$ H8 A4 F! c& W# \ -
+ s. ~' ?$ W E- ? - return ($distance<=$allowGap) ? true : false; : D+ W% i8 n' h9 M
- } ( m; k- W- p$ \! r l
- ( C* H4 C( W$ ?
-
6 g) M2 Z1 V! s - /**比较两个图片文件,是不是相似 v) k7 l1 \% U8 M# w
- * @param string $aHash A图片的路径 $ z8 m6 r: b+ c' z: p, G" Q
- * @param string $bHash B图片的路径
2 `$ q. ]6 q0 F - * @return bool 当图片相似则传递 true,否则是 false 8 G+ K- m( u9 X, [6 C' f1 W9 U1 E+ ~" i
- * */
- ^. E% t2 Z0 W% k8 ~6 Y - public static function isImageFileSimilar($aPath, $bPath){ . E: |- _# Y1 ?# V* {0 D" D9 K
- $aHash = ImageHash::hashImageFile($aPath);
1 @3 {! e+ |; @" q8 |1 p - $bHash = ImageHash::hashImageFile($bPath); 0 J2 g* R6 a% ~/ p/ ]+ C
- return ImageHash::isHashSimilar($aHash, $bHash);
3 s0 ]! J3 D- g* B/ g- f - } ) N4 E; D5 m7 _* I
-
! X6 d2 y2 F7 p- ?! L, ]4 A4 V+ |5 R$ b - }
! j6 i% {; T$ h4 j4 a3 J
复制代码 ' x) B# {2 l: M/ f6 l- w8 d
?9 K3 d; g! Y2 z |
|