管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图% U3 G- O) s M& g/ R
. n) q* I) j6 V
' X* k9 p; x( q- f3 m由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
6 O& o2 q3 P* G8 S- <?php . v5 Q$ E5 J1 K( m
- /** & s/ j9 t1 l/ h# q: Q. F! k
- * 图片相似度比较
/ j) x2 E& o8 r D - *
+ i7 Y7 k6 I8 h; ~5 B# ` - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
/ C7 w4 D% O- W, A3 U - * @author jax.hu
4 ?! j& W8 G# u4 y, a5 r - *
' L( E% X) i( Y2 g6 K - * <code>
1 b. ^5 r% n3 X4 m* { - * //Sample_1 $ ?4 \' H4 x' J' m
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
: j1 G0 y7 D5 W; s( O5 `) h- O - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
9 ?" ]) I0 L' b) u- T8 L - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
1 F. F7 o1 J- k& B+ I# A - *
; A6 j2 W8 C8 A/ {) G8 s, v - * //Sample_2 + C' L2 X& y& R/ H
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
1 V0 w9 f+ a1 D9 P7 ]* y - * </code> 7 w* j7 g. h2 y2 u8 r. D
- */
9 H) U/ b7 w' p1 \. { - [0 R# `3 E$ r8 |% B
- class ImageHash { ~- \5 x0 E, M+ m/ [
-
+ s2 ~9 u! V2 p8 f' {6 G3 } - /**取样倍率 1~10 7 x Q, c( \) G; @+ w
- * @access public
9 N$ |# x) L" f$ {! r, e* b - * @staticvar int _* a) F4 v& ?- B
- * */
b5 F% x Y9 ` A+ f - public static $rate = 2; n4 B8 `3 I; t6 W
-
' z+ @. l; u! h0 ?7 E- l$ Q4 c5 {4 } - /**相似度允许值 0~64
1 N# q5 R. V1 t2 Y, L - * @access public U. F5 e/ Z! Z2 B8 d
- * @staticvar int . P) {& }1 Z% M" c) O
- * */
, Y" M- a" J4 B) V9 z6 N - public static $similarity = 80; 9 y% L! y; O/ Z$ ?9 k, _7 I
-
6 v' |# p7 c8 t6 g6 I6 k - /**图片类型对应的开启函数 3 q5 t" A3 X7 K# H' F
- * @access private 3 ~+ y: B8 e) N
- * @staticvar string 2 X# s8 A0 o+ b
- * */ 5 @4 t |- U9 Z# g3 X2 T
- private static $_createFunc = array(
4 m) ?* [( V' q( M2 |" g& b6 k - IMAGETYPE_GIF =>'imageCreateFromGIF', * x3 b5 J1 I1 z! D, I* G3 Y
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
! j8 ^. G9 W6 L - IMAGETYPE_PNG =>'imageCreateFromPNG', ( D6 R5 D/ q2 P& D8 L4 v) m
- IMAGETYPE_BMP =>'imageCreateFromBMP', * O& g: @! U7 d# G' X
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
7 ?6 t) v6 |% f7 S3 I - IMAGETYPE_XBM =>'imageCreateFromXBM', # D( N1 Q& m4 r
- );
" |, Q# Q( p3 Y _0 |$ [ - 5 [8 }7 I# O. }. o9 f# I
-
) z: H' ]! z6 u' V g! X& } - /**从文件建立图片 4 d) {+ U- o3 T. y& _
- * @param string $filePath 文件地址路径 ' y5 s" i% l6 j2 z$ i
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 8 h( t& ?& d J9 I/ N( P8 U. K% N
- * */
( }* Y1 P1 p0 b1 b; B ?7 T0 [+ | - public static function createImage($filePath){
p- ]! T3 [; t' e! ? - if(!file_exists($filePath)){ return false; } 4 ` _$ G4 e! a
- ! e$ [, Z/ \- C0 V+ h3 \
- /*判断文件类型是否可以开启*/ , |7 U* o) G# g/ q
- $type = exif_imagetype($filePath);
* x( C0 F/ q1 e5 u/ V - if(!array_key_exists($type,self::$_createFunc)){ return false; } ! l6 M! B D) e% c2 h
- - r0 q/ O- ~; E. k. {) A* @/ T* n
- $func = self::$_createFunc[$type];
" @6 ?; u6 @! f& D ? - if(!function_exists($func)){ return false; } 5 X9 b# u3 v4 o2 Y6 Y5 | H8 {
-
4 L# U- }7 g4 u& \% F( ^. c+ ` - return $func($filePath);
$ S9 u% l7 B- p; { - } " T5 q! f# o( [; H/ T
- # q8 N) ?1 P7 C
- / B" }2 M: r& P3 s8 d
- /**hash 图片
9 ]& ] D* s& C+ a; Y9 V( U - * @param resource $src 图片 resource ID 0 T/ r C5 y( ^# K7 a
- * @return string 图片 hash 值,失败则是 false
5 H4 g( p+ Y0 S/ k' S% a - * */ ; `# n% G: R ]$ i; y" F/ o
- public static function hashImage($src){
* r" u) r8 Y4 m. s- T" A, B - if(!$src){ return false; } , l, Q0 L; O4 S1 a. q& H0 d- w
-
8 Z% t* _3 v8 O2 V y9 H | \7 v6 D - /*缩小图片尺寸*/
: k8 Q/ W) P. M- S" ~3 k - $delta = 8 * self::$rate; ( ]' o. W! k% S, Y
- $img = imageCreateTrueColor($delta,$delta); % ?- @$ g' ?% G. S) [
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 1 V! h& E: Q' _
- ( u. U9 D/ R& W" | F2 V/ F# Y8 I
- /*计算图片灰阶值*/ " g( ~8 ~& \) C; R1 i h/ c2 ]0 F
- $grayArray = array();
* i/ l8 ^+ e; C) ^. C1 L1 e - for ($y=0; $y<$delta; $y++){ ( s H1 p- a( {8 `
- for ($x=0; $x<$delta; $x++){ 8 s1 R- [4 l1 n, f S0 z2 e
- $rgb = imagecolorat($img,$x,$y);
& H$ I' f- f" a; Q - $col = imagecolorsforindex($img, $rgb); ) {# g* P: _% d: v7 \& f/ r+ |1 C
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
" U" `' q* I; ^ -
! N# K( ]. U e; T/ W& _ - $grayArray[] = $gray;
" C) V; [8 d. O" E& G - }
7 z; T. U3 Q8 I! c8 |7 {% ~ - }
( H \, ]; Y- e. x2 i5 ^ - imagedestroy($img); ( k0 w9 N2 s- t5 b S7 b$ b2 V
-
) k+ Y2 U& j# n# D0 g: Z - /*计算所有像素的灰阶平均值*/ - `& Q9 y* j# `' n6 {5 V! f
- $average = array_sum($grayArray)/count($grayArray); - x' Z! S4 i) [+ g: q2 ?' ~( N" m5 B
- ; [8 O( ~6 Q+ j& ?( J4 n
- /*计算 hash 值*/ # o- N v! W! K2 z$ v6 C
- $hashStr = '';
. d/ J1 ]: x8 Z. [" h5 @8 o t - foreach ($grayArray as $gray){ 1 k1 |. O2 a6 |+ r9 U
- $hashStr .= ($gray>=$average) ? '1' : '0'; 3 ~4 H7 P6 i) B6 X6 y1 o
- } - \4 x7 x4 p6 M3 G }3 Q) ]
- + V- x( H" d6 i( g( J$ z- v3 p
- return $hashStr; - i d/ z* `* [8 o( V% \
- }
: O3 A2 d# [, w( d' R) i8 o -
; {8 S" j$ S1 p# S8 u ` - 9 B9 B" J8 Z% Z9 J
- /**hash 图片文件
& d0 X6 `$ Z+ F0 L! z - * @param string $filePath 文件地址路径
' Q7 F" G6 A) O1 i6 P$ C3 V4 m8 o2 n - * @return string 图片 hash 值,失败则是 false
2 U" f" v! P# c1 w ?! J6 x3 P! H - * */
3 M! n7 Z) p9 |3 Y - public static function hashImageFile($filePath){ 8 y( M( `* P* u E3 d! o
- $src = self::createImage($filePath); : M7 ^2 Z3 |2 h, a% \
- $hashStr = self::hashImage($src); $ [8 H( \) X* X0 X; I0 N
- imagedestroy($src); * Q6 L! K, I# R
- 6 s( a* m7 a- V
- return $hashStr;
! {- J9 e9 j. @ - }
3 y9 K) |$ K* V& m- R2 l* M - 2 ?$ C5 H" I0 M, l! e, L; m
- 2 _; q9 V; @* b2 ?
- /**比较两个 hash 值,是不是相似
/ T0 f" }% L& c7 ]0 q2 j8 s# c8 q/ { - * @param string $aHash A图片的 hash 值 4 r% x- e" t9 d. B1 s/ q# q
- * @param string $bHash B图片的 hash 值
! d2 C6 Z8 X( `: a' M+ r - * @return bool 当图片相似则传递 true,否则是 false , p8 D7 C) \& K5 q G2 K
- * */
" g7 R4 q! e" b9 l4 T) c0 P - public static function isHashSimilar($aHash, $bHash){
1 O, J- u" _8 l' T8 G& j - $aL = strlen($aHash); $bL = strlen($bHash);
% _ k, o9 x* M. M% q! J D - if ($aL !== $bL){ return false; }
& m4 k* P+ b" T/ g% { -
1 \: G% v* V4 c6 S0 m' {+ A/ B0 x" H! [ - /*计算容许落差的数量*/
% x* V" p9 Z& w6 J - $allowGap = $aL*(100-self::$similarity)/100; 6 T6 a2 b' X$ _+ e8 C7 A
- 9 |1 }: U. R0 ~' Y/ L" J( x0 Q. ]
- /*计算两个 hash 值的汉明距离*/
. ~+ f$ r! a, }+ e! ]6 J: [$ ] - $distance = 0;
$ X1 E: Y, m0 i1 k6 m - for($i=0; $i<$aL; $i++){ ! R; }6 `7 N3 H1 h1 c; V
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
3 k" q. p( T5 x' m' V - } ; G+ U$ X+ K5 ?# P2 L/ m
- / ] Y- _$ i* U9 |% V
- return ($distance<=$allowGap) ? true : false;
& i8 \ @" p/ Q5 k9 E - }
; u3 q6 J: U. U6 P -
3 e* i+ |9 B7 v7 { - ' w: P8 N6 g- N
- /**比较两个图片文件,是不是相似
7 @2 l. a5 H6 L- j - * @param string $aHash A图片的路径
& a( f! ~& j. q }' ?! r5 ^' X - * @param string $bHash B图片的路径 3 h& }5 X$ O. _1 k- ^
- * @return bool 当图片相似则传递 true,否则是 false / R' _% A4 g% Y6 }# G" B$ h+ l
- * */ , {# U( m/ m# S% z7 `/ a
- public static function isImageFileSimilar($aPath, $bPath){ / U1 \ J7 \! P; G' v
- $aHash = ImageHash::hashImageFile($aPath); # M6 J; F+ Q5 v( n
- $bHash = ImageHash::hashImageFile($bPath);
; J, h, `8 ~: `3 @& i, ~ - return ImageHash::isHashSimilar($aHash, $bHash);
6 L% M3 J* @! m) `% [ - } ' Q2 {% U" a6 u
-
% {- x- I0 d U; P - }- g5 B6 X. F ]# |
复制代码
r+ L! }- b; b( G& W8 ~% j1 g+ l
|
|