管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图$ f$ S( Y3 Q q
" K5 e" c$ y P
5 [" v/ a% L @7 i* N由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。. M& C8 b9 v' P+ ^9 `9 u4 J
- <?php
8 @1 R: j) f1 \' d7 l. A5 ~0 j - /** - j1 E( L6 s, U; I: m- T; U1 f& E
- * 图片相似度比较
" b6 a* Z6 H9 M/ k! B8 I; {9 k8 i - *
1 ]8 D% L; J6 [1 K - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 1 d$ a' A @# {0 K3 N
- * @author jax.hu 2 G$ T% g6 R# h) z$ g% v
- *
6 D! {% x- i: v - * <code> " I& K; d/ q* n% u2 k' F1 T5 _
- * //Sample_1
1 d/ p) `2 _+ A2 w9 T, V - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
5 _0 |3 ^) b: n( b A8 b3 U3 c - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
. D3 K! W# W B, S+ O4 q - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
3 `, S) d$ U% M/ ^ - * 0 Q( o( V0 h6 c3 W% N% q. I# i
- * //Sample_2
! L7 t6 u2 |+ R7 D9 h9 E- ? - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); % M1 I3 a2 l0 m: H/ f+ s
- * </code>
7 N* _4 P9 K, x2 K7 i - */
% p2 r2 z# B+ ] V! E0 o2 I% T -
* U+ [$ ^- L, A* k - class ImageHash { % X& H' Y6 B* @
- c$ I- K- e& Y }( q0 x' j/ ^
- /**取样倍率 1~10
. J% _* y* q# ~% J7 I( R - * @access public
" }. X; f: d$ K, D$ v - * @staticvar int
# d5 Z* g7 D G7 ]* Y* S - * */
; V m8 |! s* @! U) i - public static $rate = 2; 0 m+ R# R6 U( }8 ^1 ^. F
- 8 T; F2 \; L3 \9 u: [: \* D
- /**相似度允许值 0~64
1 H7 t, i; I: T6 g( e( E: G% m; b' T - * @access public
4 z) x; e+ w+ @; a - * @staticvar int 5 f, U* `/ a) m
- * */
; a! Q6 g' H3 z/ x: z- j: J - public static $similarity = 80; 6 \& `% j2 O; X }5 f) M
- , \! f# v: p5 c# ^) ^; Y
- /**图片类型对应的开启函数 % v; a8 p" \% O) X+ }1 y1 W8 d# E
- * @access private
) y7 U! U- T: S - * @staticvar string ) f y: H% |4 E/ P% C! M
- * */ & M3 H5 B) l& E! ~
- private static $_createFunc = array( 3 E5 v6 m8 G% ^! \8 S- `! P) |
- IMAGETYPE_GIF =>'imageCreateFromGIF',
- X7 \; i# R% L" U - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
" ?: {% @& s8 U - IMAGETYPE_PNG =>'imageCreateFromPNG',
% D4 W' L( L7 A O+ [ - IMAGETYPE_BMP =>'imageCreateFromBMP', . l, ` v1 E; t2 B+ G* a% O" r
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 4 t' h. A- @+ K$ {+ t
- IMAGETYPE_XBM =>'imageCreateFromXBM', ) F4 c2 D! k) X1 A
- ); 7 J4 A% Y* p( \0 \2 O4 g
- 5 S7 d' T. H5 L1 R% i' `
- 2 S, d( P+ y# D2 D8 h, o
- /**从文件建立图片 2 r# k& m; t6 ~8 Z$ C
- * @param string $filePath 文件地址路径
\2 ^) t5 u7 H; x4 a - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ' H4 A7 L" ?0 d; T4 `. z, q. X9 {
- * */
$ N7 E- W. C; x( Y4 _ - public static function createImage($filePath){ ! _3 j3 m) E* Q: h. f
- if(!file_exists($filePath)){ return false; } : F8 ?' Y* e$ A% q/ B: W
- 5 x0 K! P5 U) {; _, U# C9 D$ a
- /*判断文件类型是否可以开启*/ + e" O2 A; a4 \3 r* u# B' P
- $type = exif_imagetype($filePath);
8 o: h$ @, h% _. ^% x9 m# p* ^5 i - if(!array_key_exists($type,self::$_createFunc)){ return false; } % G2 Q) }$ `3 O
-
/ k( p; N) Z3 i5 t" W - $func = self::$_createFunc[$type];
& [% z0 q: b6 S# B - if(!function_exists($func)){ return false; }
* p4 d/ C* a& [, s; a2 t, T -
* H; o! H! P" P - return $func($filePath);
& q0 @9 J8 x- i& s - }
. m: V" H6 z, c+ ~8 D( l; | -
( [! Z( _- Q m# }$ Y& I, \8 x -
' t) C' J) e0 t7 F5 i - /**hash 图片 3 D# \$ I( R' L
- * @param resource $src 图片 resource ID 9 G' C3 G' X+ s% O! P
- * @return string 图片 hash 值,失败则是 false + {( b! e i8 q7 n+ p# X$ u
- * */ C2 `, I. [: W. B, ~
- public static function hashImage($src){
( D- n( }- T4 V - if(!$src){ return false; }
& u. I) A1 O. y8 Q -
/ s- [ t) V J, U+ z - /*缩小图片尺寸*/ ( I# K L3 s$ q n+ b9 g+ s1 P
- $delta = 8 * self::$rate; 3 H k) i/ t/ y% ^' B/ ^5 t# M* q
- $img = imageCreateTrueColor($delta,$delta); ; ^9 p9 e3 I; o" J! E' X1 Y' M
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 8 N8 n5 _8 v0 L1 ]) `( s
- 9 W. q0 ?! b1 W5 _; [! ~2 @
- /*计算图片灰阶值*/
& D0 ` g9 [: S, K7 A% P - $grayArray = array();
~' m/ e; F) n) n1 S% N - for ($y=0; $y<$delta; $y++){ 1 P2 L) u! H3 ^. L6 l; B# [ g
- for ($x=0; $x<$delta; $x++){
5 k2 j/ o. T# z9 g9 N# ^ - $rgb = imagecolorat($img,$x,$y); , ^: I$ o# r! n; o- L; I& g
- $col = imagecolorsforindex($img, $rgb);
. r, c) j4 `$ p% w6 D" z - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 3 a5 K, E0 R$ |# n! b+ g
- : ~8 Z( X4 O( g$ P% t/ ?
- $grayArray[] = $gray; ' ^2 X$ K/ }" P, M- O9 s) _
- } % a6 o( F# u& e8 C! a8 i. |
- } ( w& l& s- V6 Y/ w" \
- imagedestroy($img);
2 @4 [( [2 {2 ]. _( ^! s - 6 w) ~- w, V0 L* M
- /*计算所有像素的灰阶平均值*/
' h$ o0 h9 B. L; O/ B* y( h - $average = array_sum($grayArray)/count($grayArray); 5 o% l. f4 J. q8 |+ Q/ L. }$ P
- * N }/ x# i8 L% L9 s
- /*计算 hash 值*/ 1 J! |9 ?4 f- z# }7 ^2 l
- $hashStr = '';
4 v* f" m5 s1 W# Q - foreach ($grayArray as $gray){ 4 p8 Z7 s3 y0 i# B6 v
- $hashStr .= ($gray>=$average) ? '1' : '0';
2 M5 _( i1 x8 ?9 e$ ^ - }
/ e, d( N. B! v. Y4 O4 k8 Q -
3 Z/ Z1 D* Q; B: w5 N0 H4 g - return $hashStr; ! f1 H% o4 Q0 }; L# _ T2 S
- } # z5 R# g% p/ L, j6 z
- 9 ` B/ |8 ^$ r! l
-
3 Q) O% |% ?6 y7 M9 q0 ` - /**hash 图片文件
7 w5 F# Z* i! ]& }& i - * @param string $filePath 文件地址路径
5 b$ ~) |2 W u/ E# u, K - * @return string 图片 hash 值,失败则是 false . \% @9 \1 u" {' W# O" D1 a
- * */ . \2 G$ V* P r& h
- public static function hashImageFile($filePath){ $ _0 ~) b2 w- [- b8 y4 q2 i! \+ N% P
- $src = self::createImage($filePath);
! b4 b1 d0 O$ z: K, z - $hashStr = self::hashImage($src); ; {9 k# T9 m9 D2 M$ t) @9 [8 V
- imagedestroy($src);
( [: N4 ~: `/ E, y% I+ M% J -
+ H r1 W: _1 f5 u/ j# \# s- T3 P+ J& V - return $hashStr;
0 s% L% @) z4 y4 {0 U - } " \% Q# @8 b& C6 n H
-
, y9 @* g3 @. a; r0 k0 r -
& p6 l2 y2 \# M4 K3 x1 O - /**比较两个 hash 值,是不是相似
/ Q: R i6 v5 k: q! h" t - * @param string $aHash A图片的 hash 值 4 i" D7 U" B* _- |/ ~7 ^" M4 Y
- * @param string $bHash B图片的 hash 值
- { O& S- ~$ h- K& C7 O# I$ p - * @return bool 当图片相似则传递 true,否则是 false
5 F" _3 ?$ s$ M' t b h - * */
* q! D. ~! L: P+ Y, s; P v - public static function isHashSimilar($aHash, $bHash){
1 n6 `8 Y; B3 Z9 a" X1 R# E. j: H - $aL = strlen($aHash); $bL = strlen($bHash); & c3 `( n2 U& `/ b. J( }3 y
- if ($aL !== $bL){ return false; } # _7 |0 |& o" H0 M
- # [& c P0 I6 ?
- /*计算容许落差的数量*/ 7 G: |4 O8 x8 X& m
- $allowGap = $aL*(100-self::$similarity)/100; % [/ H( f3 F& c0 `% \6 ?- J/ Y
- 6 m# }7 ]# m5 k2 r
- /*计算两个 hash 值的汉明距离*/
5 R$ x f+ g) ?+ X7 l- d& Z6 G - $distance = 0; % W9 r; Z2 ?) ?' x, p
- for($i=0; $i<$aL; $i++){ ' o+ ~9 d0 W0 Y0 J4 Q, E! e& a' z. Y
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } / ^8 w+ ` `. e( E( X
- }
3 `+ }. [0 Y8 } e. [0 Z - ( @9 ?9 B% R u0 I
- return ($distance<=$allowGap) ? true : false; ; A- ~+ G, D, \6 S* v
- } 2 J& v. k6 v$ B Z2 ^ I
- 5 Y: s7 f$ n s( k
-
$ T2 i6 A9 |1 i7 L/ _7 ?, d! C - /**比较两个图片文件,是不是相似 : [! U4 U6 I+ L0 N0 O$ y
- * @param string $aHash A图片的路径
2 t; B* H& R6 p: } - * @param string $bHash B图片的路径 # W m. T0 E, A% V7 G6 X+ v- G
- * @return bool 当图片相似则传递 true,否则是 false
/ A& p, u# E% a& R C5 `# ^ - * */ : |6 _! w1 O2 E; e u0 }1 `" h
- public static function isImageFileSimilar($aPath, $bPath){
/ I* g' I6 W3 x" W8 K) W, h) { - $aHash = ImageHash::hashImageFile($aPath); 5 e! _, v) _# ?% _ c. J8 E
- $bHash = ImageHash::hashImageFile($bPath);
; h7 J; t6 A+ a( o# n; R - return ImageHash::isHashSimilar($aHash, $bHash); 5 d- |+ N J! ]2 _. V( ]( C
- }
0 C2 A7 a& q, y/ h, a. p, p% q -
- R {9 c% Y3 o% f# u - }
! f# C- E4 W/ U# x
复制代码
1 L2 q9 f/ o( |! C1 s$ c/ ?" M* \, V: W" R! I. K. |: _& L
|
|