管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
! |& H3 t5 l0 R9 d9 N" q" b ^* R3 Q
2 B3 }( o$ d6 i由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。/ F4 W% r0 O& {6 [
- <?php ; d- @( K8 o8 N9 e. Z, o
- /** ) {$ D6 n0 \7 r8 @/ t7 c9 u
- * 图片相似度比较
, a4 \9 W9 W( R- U1 w - * 2 V! ]2 A4 b; b# ]' D
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ! A, S: Z0 g/ ^6 N2 P
- * @author jax.hu
, y' `# x5 D& ~: G; g: w' D1 H - *
3 M1 n. A5 [& [! ], a2 D4 G - * <code>
% q9 Z5 h! }2 y- G! x9 D. H - * //Sample_1 5 G4 Z# X" S9 D- l% Q' n
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 8 }( m/ }' `3 N+ g+ N
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
. r$ B3 q. L- k6 y x( a* G - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
2 x$ O( L) t; V5 M6 L5 ? u - * 6 `2 C. q% E+ k2 h- T# B9 B! h1 d
- * //Sample_2 4 h7 G2 Z5 y/ D v
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); $ ~ M1 M; c/ q* c* K1 a1 J q4 @, J1 G' v/ n
- * </code>
% B; x/ P, R% Y- u8 [( B4 E# E' d0 X - */
1 r. s7 N# Q0 l! [9 n - ( v* M- \4 k* P B
- class ImageHash {
) V5 C+ ?6 E. ^2 {9 q# E3 c - ( N* |8 _0 U+ l& f
- /**取样倍率 1~10 " k7 K# X& m0 B9 g
- * @access public / i7 @' w2 n; N& ^6 |4 e: ^& R
- * @staticvar int $ r! Q6 J) H+ r4 s) r
- * */ ! e% p( \- V% ]/ I6 [1 V
- public static $rate = 2; ; P( R. O/ l9 D( o
-
0 h+ a3 q) Q" _' G" A - /**相似度允许值 0~64
) d4 _( c8 q- ?4 g2 m - * @access public
1 K- z4 `5 M; t8 S - * @staticvar int
" f1 S% a" Z7 o; k1 [ - * */
* }. G# d/ I) [2 N! w - public static $similarity = 80;
" F; [ H) Y9 u -
, _7 u: `% U5 S( z! w - /**图片类型对应的开启函数
8 ^! N0 l, q; F - * @access private
0 I6 e3 G: l/ q2 S _* _. _ - * @staticvar string % @; y. x0 C! E" ~9 e9 i
- * */
5 y: H5 n2 w3 B* v( o2 \& _ - private static $_createFunc = array(
& V2 }' V$ v- H) q, b) Q - IMAGETYPE_GIF =>'imageCreateFromGIF', 5 @( O* R, Z+ {% k
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', * D2 m3 E _6 H2 @! P2 z! r
- IMAGETYPE_PNG =>'imageCreateFromPNG', ; O- Z( C( d" X/ ~* a8 _
- IMAGETYPE_BMP =>'imageCreateFromBMP',
% Y0 }! V u- H$ ] - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
/ l+ q3 r# Y5 Z9 u - IMAGETYPE_XBM =>'imageCreateFromXBM',
0 ^* w$ S! n/ o4 X( O! F$ L - ); $ J* l9 b" n' ~2 l
- & y" _( Y, b$ D7 v; a+ o
- {4 r" [/ D0 P- C
- /**从文件建立图片 * r7 N+ _7 h2 q( d; R
- * @param string $filePath 文件地址路径 5 `! D/ J. i+ N- y9 b& ^
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 7 R7 U* Q' N% n& I
- * */
- ~7 M {: _9 z& p& J/ S$ M - public static function createImage($filePath){ $ N% @ g1 G( w9 T9 O
- if(!file_exists($filePath)){ return false; } 4 m [& z: J+ {8 q3 v
- & D5 v) B* D7 g3 r2 Y) ]( R
- /*判断文件类型是否可以开启*/
E1 P" k! j6 D5 H - $type = exif_imagetype($filePath);
6 |* Z! L* V$ f, U+ h+ m - if(!array_key_exists($type,self::$_createFunc)){ return false; } 2 Z; L4 n9 V, @4 E- ^4 N
-
& |: E' O6 h, @3 Y - $func = self::$_createFunc[$type]; 9 V5 H/ b0 I0 v1 \$ B4 v
- if(!function_exists($func)){ return false; }
( W3 I7 \. P$ b1 b - 1 {/ w3 ~# y+ B7 V
- return $func($filePath); * T0 |& W+ F: l% s
- } . S: b n2 |- c: M b+ v1 L
- # a5 v% v) T1 k9 c, i* R1 E- u
- 0 e, M' w9 n8 e: h4 s
- /**hash 图片 4 O) R' O3 D, a1 ?# N3 s
- * @param resource $src 图片 resource ID 1 l* s+ @1 L% g: K: r) b/ v. L
- * @return string 图片 hash 值,失败则是 false
4 f6 Q3 z1 O. t; b. n& c8 B - * */ 9 D- G4 C- K- _ ~) S. O3 K5 k5 A
- public static function hashImage($src){
, _. W( Z ^: E4 Q) }8 [+ m - if(!$src){ return false; } ' ?8 I% l- ?2 `% z$ [ Y
- * C9 B0 s4 H$ ]7 D" B. U
- /*缩小图片尺寸*/
, v9 u8 w/ W4 c( M - $delta = 8 * self::$rate; # u- }2 p: V) Q$ U' w, [0 r
- $img = imageCreateTrueColor($delta,$delta);
) O. Z7 h+ f0 [; _/ f - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
# m- H$ r; r, d* e1 @ -
* M% _' L- V" n - /*计算图片灰阶值*/ 2 F- |# {1 g; [7 _
- $grayArray = array(); 2 L& h; \. L6 }" _" ?; i% j
- for ($y=0; $y<$delta; $y++){
: l! Q/ D& `5 E N3 ]. A9 ` - for ($x=0; $x<$delta; $x++){ - B6 R+ O3 s7 I0 Y7 N" K7 ?
- $rgb = imagecolorat($img,$x,$y); - o) t- Z. x9 Y
- $col = imagecolorsforindex($img, $rgb); 9 B |3 `5 V8 h& m1 n
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
- |" s5 V' V \4 x - ' A" E4 [0 x+ Y
- $grayArray[] = $gray;
2 k z( e! \+ j2 T! I% }8 [ - }
, c$ e1 y& z- Z. p4 g+ W$ D - }
I6 b6 `* d2 S3 C6 i2 _ - imagedestroy($img);
* n& o7 F& V, s2 n9 h, @ -
$ n9 u: u1 U! n; [. @) W+ |5 y - /*计算所有像素的灰阶平均值*/
- O3 B2 w( j4 E. f; c* Q6 K. U! ], K - $average = array_sum($grayArray)/count($grayArray); 2 I% _0 F+ x( s; @, r
-
# Z2 [& P7 g" Y0 K" F0 ]) Y - /*计算 hash 值*/
* h, p6 B5 r1 h - $hashStr = '';
/ k! @( h. K, Y X; h - foreach ($grayArray as $gray){
2 c4 j; s/ Z- ^& c) X - $hashStr .= ($gray>=$average) ? '1' : '0';
$ ?- U. {5 z' X3 q7 P, \ - }
' m/ D& G( R& Y9 n1 \# K6 p - + H1 L/ s4 e& L; N, s9 P# C
- return $hashStr; ) j/ }) E. e+ j9 w; T
- } - h# a4 i0 B7 m2 w6 I1 F
-
" [- o$ ~3 {+ ^' i - ) P4 S: {9 R0 m: k* q, U
- /**hash 图片文件
' _: G. C# o) P6 E% K( [) {8 r - * @param string $filePath 文件地址路径
0 Y* o! M3 _" l) M& E: K - * @return string 图片 hash 值,失败则是 false ; S; z4 i `# C) l4 q) e; l, q
- * */ # X. P7 O! S* n/ U' e9 K; u
- public static function hashImageFile($filePath){
' ?+ f t( t3 k7 F( B2 i0 o# k - $src = self::createImage($filePath);
% ^# F$ r# Y8 i - $hashStr = self::hashImage($src);
& T- R* X$ z" F0 f" l - imagedestroy($src); 1 i, {0 c+ t: ]+ r8 b4 \6 C- g5 V
- 6 \' t* t: S6 F! O- z* i' c, X
- return $hashStr; 7 q8 n3 W" M" C i0 D2 }8 a- H: \
- } 3 Z( O# ]3 J- @+ V
-
# p: h6 ^1 ?0 [ - {, k, _# I7 Y
- /**比较两个 hash 值,是不是相似 ; A, G/ t& t# b+ F3 L O
- * @param string $aHash A图片的 hash 值
5 k( Z% {, e: z% u - * @param string $bHash B图片的 hash 值 1 a8 z3 _; j4 K2 w1 h3 Z3 A9 ~
- * @return bool 当图片相似则传递 true,否则是 false . X- t& A; K. a' d+ J
- * */ 4 }9 ], R/ Q) W+ w t9 @4 Y# i# |1 F
- public static function isHashSimilar($aHash, $bHash){ + k% |. M8 n" `+ |* z0 }# ?; _9 M( U
- $aL = strlen($aHash); $bL = strlen($bHash); 9 V/ \6 l: r) a8 b4 p* |, `2 m! H
- if ($aL !== $bL){ return false; } 7 @3 R- J$ ^$ o3 R3 U
- " i; J! W6 D4 X$ \
- /*计算容许落差的数量*/ 3 J9 m! [7 U" i" Y
- $allowGap = $aL*(100-self::$similarity)/100;
9 W5 f- ]0 O+ S/ N) p2 P4 S+ y - n9 ]2 k$ u ~ h! I# s t; O
- /*计算两个 hash 值的汉明距离*/ ! t, ]1 K5 @! f6 @* x
- $distance = 0;
7 l, e2 ?. R3 f5 ~. v8 A3 Y6 V - for($i=0; $i<$aL; $i++){ " E& L0 e% T9 l9 a. K) T6 `9 e
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
& h* {' n6 W7 ^4 A3 C5 m - }
& I6 N: B4 ^- O -
8 ]2 G# `* V- j6 y3 z; \1 S - return ($distance<=$allowGap) ? true : false; 4 t) y$ n# Y+ G
- }
% Z# Z: e2 [& G5 X* ^' ^' u# ~ - $ S8 j2 S) Z% P! r$ l
-
# g) l; S+ M& _9 E - /**比较两个图片文件,是不是相似 ; b& z! M# o2 |& c3 M7 s
- * @param string $aHash A图片的路径 1 a+ \& r3 W1 Y5 a& t
- * @param string $bHash B图片的路径 ) c Q+ K' q) b9 ^' ] g r/ M
- * @return bool 当图片相似则传递 true,否则是 false
/ U# [( o! [5 t6 P" Z - * */
0 Q4 T( d" }* N - public static function isImageFileSimilar($aPath, $bPath){
2 K7 P$ _! G" E7 h) T7 z* w - $aHash = ImageHash::hashImageFile($aPath); & V8 y) x m9 f t ~) R
- $bHash = ImageHash::hashImageFile($bPath);
) u5 n) P: ^/ p6 z6 d2 l+ w - return ImageHash::isHashSimilar($aHash, $bHash);
- L9 i1 o( ^: [4 b1 E - }
8 }2 H+ V; d2 C5 T -
: n# }5 c" `$ X0 V - }3 m: n- d2 x5 `- H* z7 T9 k$ b
复制代码 & O& i' I- W2 G3 }
& Q5 n1 Z2 x! I0 D/ ?) F
|
|