管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图1 j% o3 p, o' q( e) z/ O
3 @% G% w' o- {8 M; @) R5 [+ B
1 t3 Y* X9 c5 ]2 h" O* D' s) {- k' Z0 y由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。! ~$ b! B, \* ]7 P4 I
- <?php
; ^, U* p! J6 F6 V6 p; L+ ~ - /** 8 B* E7 ^7 C6 j& {0 H+ S3 V
- * 图片相似度比较 6 q: l% w+ e& A! ?
- *
. P* [7 T' n: d+ A* _9 Y; L - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; % [5 H9 G; h1 Z: s
- * @author jax.hu
4 ]3 c. u5 T1 ? L& F% V - * : T" w3 ~3 n" b! _: Q+ m
- * <code>
* ~# ?: A: Z! {+ n, } - * //Sample_1
7 p" O8 f% ~+ G/ V( R - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); / m' h6 x5 `2 s- f5 Q, K0 i
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
" `0 {, t6 }/ z) {0 X: [ - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
% t% n( b# s2 ]+ q& H7 r4 Q o - * , w( j- h$ F" z1 u. e3 G& j
- * //Sample_2
" W( ]. W, ^" l8 V - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 5 s2 f) [' |1 N. i) R
- * </code> 2 F/ B# s7 n: {; ^1 Q& X2 _& ]
- */
2 G/ H) n: a0 H - 3 r" V/ f% `8 W6 ^1 C: u1 v
- class ImageHash { 3 x* f* X7 j8 R. u
-
, V8 N( g" d; U. c' y/ u% b - /**取样倍率 1~10 1 T' F. H4 f: D; U# _
- * @access public
+ Z2 G" L+ V2 @: f - * @staticvar int 4 d/ j* a6 l6 y4 c) s
- * */ - T9 q- @' _! I3 l3 a7 e
- public static $rate = 2; 7 p& ?& d* m4 t, V3 f. i1 A
-
9 b3 H7 G: y- N2 \3 o - /**相似度允许值 0~64
* d) M5 b, U; Q& N1 o/ Y - * @access public ; ~* P8 k L5 N! \# T8 z: ?
- * @staticvar int " U! O( n# a$ `) Z. L
- * */ 4 @* B0 [* K7 {3 r# F# D
- public static $similarity = 80; x" y; E& ^0 |3 k6 l2 `& t
- * u' C+ ]. D1 ]& F2 Z
- /**图片类型对应的开启函数 * h1 |# ?. ]) b7 \: F
- * @access private 7 y i) T; T. D2 u6 L# J7 x
- * @staticvar string X- D; w' ~$ x) \* M$ b7 n* M+ J& m
- * */
; Q2 ]! V1 j) Y3 g0 Y - private static $_createFunc = array( 4 W# i8 q7 p) O: w6 A; N
- IMAGETYPE_GIF =>'imageCreateFromGIF', " M7 T: W e/ M* \
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
3 f) C. u1 j2 i9 p m% D3 k - IMAGETYPE_PNG =>'imageCreateFromPNG', / g* { I+ G; R4 l7 P
- IMAGETYPE_BMP =>'imageCreateFromBMP', ( w" D2 P, C$ ^
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', * M; {+ i% K! W% q6 G9 B' u& [( X
- IMAGETYPE_XBM =>'imageCreateFromXBM',
# a4 L9 H3 O/ [5 p$ B* h( V - ); + n7 H6 ` o: t
-
1 b* O5 K% j: ?; a( r9 K, K - 1 d! \( [ K$ q1 S
- /**从文件建立图片
! ]2 w, x# ~7 k" a, F - * @param string $filePath 文件地址路径 2 I; @4 } V; Z5 U; O/ u' Y
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
/ W) i- J3 y7 x, f4 Q( u - * */
: Z! v: R* B! X. i- {( g8 D/ ] - public static function createImage($filePath){
3 q* k& ~5 e$ |( F) ~" c! c - if(!file_exists($filePath)){ return false; } - _6 Q3 j& U' r: H8 V7 d; R: X
-
* \9 [8 C' g2 E, x: |# ~8 G }! E - /*判断文件类型是否可以开启*/ : M+ W7 p7 q( j, P) Q
- $type = exif_imagetype($filePath);
6 j; p# G& v) X; w& J, J - if(!array_key_exists($type,self::$_createFunc)){ return false; }
5 F5 `% I2 e8 s( C5 B - $ L U F+ _3 [ ]/ I0 T
- $func = self::$_createFunc[$type]; . G" n. a! i5 {9 A& x \
- if(!function_exists($func)){ return false; } # I* @5 ^# j( Q7 T; q/ t
-
% p E- k1 K6 E. |/ \ - return $func($filePath); 9 d: ^1 @$ r( z4 x. ^# x
- }
1 u" D7 J$ t1 X( W* p& ?2 i -
3 f1 q' H: t e - , g% Q8 @& s! J( Q
- /**hash 图片
8 w7 }1 ~* a5 t/ D! i - * @param resource $src 图片 resource ID
+ M( |+ d6 t* }7 M/ d - * @return string 图片 hash 值,失败则是 false 2 Y0 q; F% U" T( `6 i n5 P M
- * */ 1 u5 K+ F9 p9 O5 m
- public static function hashImage($src){ - z2 D v' _4 X/ U" k T
- if(!$src){ return false; } 8 g: g& i) z4 k! n
- 2 F/ o8 z% ?- P8 u
- /*缩小图片尺寸*/
8 D$ c9 R% e8 E5 `" I9 a - $delta = 8 * self::$rate; ( p" s$ S& ?1 s% G$ D( L, l
- $img = imageCreateTrueColor($delta,$delta);
' P8 n" t2 v: s6 r& q - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
( J0 x V" U0 T# N! N! t% V - 6 k% Q0 e( c2 I; b' M* Q$ X
- /*计算图片灰阶值*/ % g" y% \3 D( |% {
- $grayArray = array(); : N; e& A8 s: R$ x/ b, A) b1 B
- for ($y=0; $y<$delta; $y++){ * x: q" H. o7 k! Y% ^
- for ($x=0; $x<$delta; $x++){
! ^7 x5 u- g3 Z# y s# W! ^ - $rgb = imagecolorat($img,$x,$y);
) j8 X" R, I2 E8 h - $col = imagecolorsforindex($img, $rgb);
) ^ Q5 a0 J$ t8 S& e - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 9 }: X8 i/ w$ @$ q& c9 \
-
# Q0 F1 s( ~0 r4 d+ K. W4 }6 v - $grayArray[] = $gray; 4 `4 E+ ?7 L1 `( A/ y3 V+ j2 V2 c
- }
/ ^+ T. W1 P S/ [ - }
6 g; Y" B& J; d# K& Y' O+ G# a - imagedestroy($img);
; q2 F& X8 m8 A: N -
" S* `& ~" g0 ]; h - /*计算所有像素的灰阶平均值*/ * w3 u- J/ h/ A- r
- $average = array_sum($grayArray)/count($grayArray);
+ i$ _* y" j; }. w. C1 ?! e - $ {1 C$ k) F* A( ?" u W
- /*计算 hash 值*/ 2 @' i* X" B' G- ?, Q: Y
- $hashStr = '';
1 f/ d" N( X8 w8 c4 }' b* a' L! W( B' P - foreach ($grayArray as $gray){ - y- U) `; @4 t2 P& Q2 _$ ^+ p2 v
- $hashStr .= ($gray>=$average) ? '1' : '0';
+ p& \0 I) w' W! a- R8 f+ N - }
$ F% f# {5 r" F5 L: L - 1 t& ~/ v, a: H) ?# N2 M' M
- return $hashStr; " |4 g% y% Y9 R' [2 P2 R, E
- } \" F, x) k2 r, k
- # Q( J- T7 N/ x1 A
-
6 c8 e. h! q n1 X - /**hash 图片文件
2 ^( S! \4 S5 H& z& V - * @param string $filePath 文件地址路径 2 b$ ]9 U7 r+ L. j2 {+ e
- * @return string 图片 hash 值,失败则是 false 4 X `' m2 g2 q5 R3 |( k
- * */
+ r# L4 _/ z! Z& p- S; k$ X - public static function hashImageFile($filePath){
1 @# R0 T0 x& O$ _ - $src = self::createImage($filePath); 5 L* y/ N% H9 R; W7 o5 {6 v
- $hashStr = self::hashImage($src);
$ Y$ N7 [( B. j: { - imagedestroy($src); 5 ]5 Y3 V5 w! S
- 3 N8 U; k G% [: `6 I0 l
- return $hashStr; + v( ~' x( b% E0 S" j, a: v! l* s
- } " A; A% h( v9 ]; {; D2 X9 [
-
. _( r5 H) w6 ? - S- S; \6 d- z4 r) X; I
- /**比较两个 hash 值,是不是相似 - x+ t3 L. j) v
- * @param string $aHash A图片的 hash 值
N; s7 }, r, x: i* q - * @param string $bHash B图片的 hash 值 ; w' O u1 z- e6 ]& }
- * @return bool 当图片相似则传递 true,否则是 false 6 j. F7 M/ h- ^& ?! y
- * */
6 E5 {* ?) O8 b% h - public static function isHashSimilar($aHash, $bHash){
% p3 R( E! p: R - $aL = strlen($aHash); $bL = strlen($bHash);
3 a2 O4 T2 `6 l - if ($aL !== $bL){ return false; }
! _+ X- e( z# X7 @* O' c - 7 F0 Q: D8 f: V, c# j! L6 z
- /*计算容许落差的数量*/ - Z: b- v) a1 @ k
- $allowGap = $aL*(100-self::$similarity)/100; ) @4 T' U: Q! H& W2 N) X$ y7 S
-
7 B9 c; b% E- ]0 R. o - /*计算两个 hash 值的汉明距离*/ Q' a$ }4 @ {+ R, L
- $distance = 0;
( @) t3 P, j4 O. w5 m U- H2 a# j - for($i=0; $i<$aL; $i++){
9 d/ M+ ?$ S7 b: H( L5 @/ |+ V; P - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
. J+ ?8 e' m' v" \% \. Z/ M- ] - } ' C; E8 {( R2 @3 \) X7 `$ X6 g) B' O% A
-
% g T) S" }3 S7 j" n P - return ($distance<=$allowGap) ? true : false;
! k* H- b: O" T; n - }
* J7 L8 D0 e" f# r! f4 f% I -
- s. R, W( n" l8 S. _9 K8 K1 m - # |/ d& S* D, A6 x+ K; A, b
- /**比较两个图片文件,是不是相似
) C3 i& g- a1 _7 r# b - * @param string $aHash A图片的路径 / F; R4 i- ?- W* w4 }
- * @param string $bHash B图片的路径
7 q4 p) \( V0 f' N - * @return bool 当图片相似则传递 true,否则是 false " M) U0 U6 x3 X: w9 ^
- * */
& d. T; ]9 G5 G2 A9 g. g - public static function isImageFileSimilar($aPath, $bPath){ # W4 \9 i" q$ e% a
- $aHash = ImageHash::hashImageFile($aPath); * z& }; P- ~! j0 M: @
- $bHash = ImageHash::hashImageFile($bPath); 0 z1 M& H& B( L4 D, y1 ` p3 c
- return ImageHash::isHashSimilar($aHash, $bHash); ! j' W e/ J4 R2 P
- } V( ~4 P. I8 G& f j
-
% L5 i. b' M" z4 O2 G* Q% ? - }
9 B1 C; W5 c2 ]/ \. w- m7 x1 b) f
复制代码
. ]! d. k. \9 h d* @- k, `
- x* x* s, ]! z. q. H |
|