管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图) ^3 ~2 B) }+ ~5 _ {1 q: e: Q2 s
# Y. e$ a n" L) w# y; K& x4 Q* K2 Y6 k
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
6 ~1 b) c! e- x. i- <?php
1 y$ J) _4 t- v - /** , g* [# J2 J6 J9 l: S7 p: A5 T
- * 图片相似度比较 1 u1 m. b" e7 `' a) @6 w' F A
- * 8 k9 k/ Y0 v" x0 m# P/ E
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
- D+ Z7 ^, B: M3 C - * @author jax.hu
/ ~ g% Q3 y0 U9 t, O* m& v% T - * 1 b- y; Z/ F' t- g# x2 t) h5 K
- * <code> 8 n) J6 P7 R/ |0 I' L. q4 _
- * //Sample_1 : M: g4 l4 V! \+ M- V0 }6 P* Q
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
& H' \. W. M- x9 m3 g# I( Z - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 7 j; m; t: ]3 F8 w/ y
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
# m# b$ y1 P9 z; S* @! P6 e- \1 g$ B8 d - * 3 K6 s: t0 H/ E& a1 V* B0 \8 }& ]
- * //Sample_2
. F. ]- O3 N* F! l, m2 a/ I1 T3 p& C - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ) T: [ ?4 [+ K0 m. W
- * </code>
9 _9 z0 Q3 z# b" u - */ , f1 e, y3 ?+ s2 y, H7 d
-
7 B% Y* r3 r; b9 @; T; r# l( X - class ImageHash { 7 O) x+ k! E! a9 W6 a# m1 A g
-
& N$ [$ A Y0 I' F# a' u - /**取样倍率 1~10
5 [# v9 U& N: e( t - * @access public
; J. N) S/ o* m/ j - * @staticvar int , r* H" b# z0 F. ~
- * */ 9 e% c% X. ?) w4 U" @6 a. [1 n
- public static $rate = 2; 8 x- C1 Z" a: q+ e! F
- u" E0 `* C& \8 F* j1 K. }
- /**相似度允许值 0~64
% }, @6 F. O3 H7 Q, X$ s - * @access public
?) l# O6 E+ E( i, x/ ^) ~ - * @staticvar int ( Z, H1 \7 J* k& i
- * */
- r' x3 g0 E- y! A( f X U4 c - public static $similarity = 80;
9 Q0 I6 V6 r( H -
" b, U) H" N r% z - /**图片类型对应的开启函数
" X# Z, P: z/ a - * @access private + [/ ?) E: n' ~* k' h0 d
- * @staticvar string
2 S! S: ], y$ W - * */
; z3 P9 V( q2 J: v( A0 R7 P. q - private static $_createFunc = array( ) J5 `' q s2 c) V& W+ A
- IMAGETYPE_GIF =>'imageCreateFromGIF', 3 F" j2 e, E* Q" `& O( u! s6 X. N5 u
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
+ b! a$ c$ P# {. L* F- _& x - IMAGETYPE_PNG =>'imageCreateFromPNG', ' h# [! i B0 w# D3 y
- IMAGETYPE_BMP =>'imageCreateFromBMP',
9 j/ n8 W, d# {1 g. B - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
7 O s+ N0 T9 c- ^ - IMAGETYPE_XBM =>'imageCreateFromXBM',
2 t" |) {6 v7 `# P+ V/ I" [ - ); 1 b- b* ~( @8 F; H- w' m+ r V+ Y4 J, }
- 5 \' v% q$ S& {. y
- 9 C5 H) _2 m% o4 t& j
- /**从文件建立图片
( t2 { U) x3 z: K6 Y& _3 _% x - * @param string $filePath 文件地址路径
) @8 n+ q8 u9 j& z* r - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
8 d9 Q2 g) @2 _" s3 z- E0 Q; @8 ] - * */ 7 c% J, ~4 A" l+ V$ f5 t- _7 L2 f3 d
- public static function createImage($filePath){
8 ~) j9 H- p' T" T7 O, W& d/ \ - if(!file_exists($filePath)){ return false; } ) D' Y5 o+ p; ?: {
-
6 C+ f! i, d v. J5 J - /*判断文件类型是否可以开启*/
& g+ K8 d2 }; `4 f% e2 r - $type = exif_imagetype($filePath);
7 x+ y; l, I$ _! }7 I - if(!array_key_exists($type,self::$_createFunc)){ return false; } ! ~3 u- k) ]8 Q# s7 L/ D
- % w3 d3 e1 _5 e. {" {0 E1 o1 U
- $func = self::$_createFunc[$type];
; S9 `4 {! [" u" k; Y% _0 J - if(!function_exists($func)){ return false; } . Y- ^* j9 E) D% Q
- ) h% d" n7 u& s0 y' n3 A# y1 h; k
- return $func($filePath);
8 \* N% \, G6 m8 |% k; J - }
6 ` D+ R0 Z, V. B/ E -
- M3 z5 l; Z. C# x -
7 ~8 z' ` S4 S' C6 O; V - /**hash 图片 + _( @5 a! [% X5 h' Z {
- * @param resource $src 图片 resource ID
( N) G% l6 Z. W/ m2 l# a" u8 @ - * @return string 图片 hash 值,失败则是 false
! K+ [: ]8 l4 o6 { - * */ - ?( v+ Z% E, a( U, R
- public static function hashImage($src){ - g8 M, a+ p# R i
- if(!$src){ return false; }
0 e) }7 @9 F! ?) `: i+ ` -
8 u) F% E0 e; ?" v% D. \+ A - /*缩小图片尺寸*/
, z8 @% Q% j! W" v0 g2 @( ]! s! r - $delta = 8 * self::$rate; $ m5 F/ i2 |4 x9 _; ^
- $img = imageCreateTrueColor($delta,$delta); 2 L8 X! O. R! c5 e
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 6 K8 [" v `3 p$ X3 v( d
- 9 C8 s, e( ^5 _4 J$ Y
- /*计算图片灰阶值*/
4 \+ r6 ^9 J: R$ a( z+ ` - $grayArray = array(); 4 v$ U% c1 _6 V: ~2 R
- for ($y=0; $y<$delta; $y++){ 5 x) ~0 _9 g$ |: S! Y* ^# B, p, E+ Y
- for ($x=0; $x<$delta; $x++){
z* o- K6 s- v2 h/ } - $rgb = imagecolorat($img,$x,$y); ( W' p0 f( s$ J* |; x0 B
- $col = imagecolorsforindex($img, $rgb);
4 G8 O" L) j& S - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
! T6 Z5 K. }$ V, M5 }: D - , }6 Y8 C0 g9 z& N E6 ?- X
- $grayArray[] = $gray;
% J/ R: }( W8 _' X4 D2 Z4 b - }
; ~+ w; m6 I& s- t& ^9 t$ s - } & e) U$ o0 b7 Q" N2 L' ^" [
- imagedestroy($img); ( n6 S0 m, O% K a; h
- , I& m" n9 H/ p( i: q
- /*计算所有像素的灰阶平均值*/
+ H( m. `9 Z4 |9 L9 Z4 s8 L - $average = array_sum($grayArray)/count($grayArray); 4 {! x& N& q' x
- ; I+ R# o8 B, g
- /*计算 hash 值*/ 7 Z3 L4 l" ]/ p x0 ~" ?
- $hashStr = ''; ' m" `8 k9 N# C) ~$ K( b0 I$ i
- foreach ($grayArray as $gray){
; Z0 j2 X" X: \0 q& H: [; b - $hashStr .= ($gray>=$average) ? '1' : '0'; + b; l1 j q' K. q$ N# w" R
- }
1 h0 v5 z* P, g -
$ z& N/ N* b4 {; i" H - return $hashStr;
, D4 w7 W, A+ M; @ y4 o - } $ `% F2 R5 s* P$ ?2 x
-
9 p% l) G# G) C5 B# C - , o' G! g i! \% [+ K
- /**hash 图片文件 + q. h7 q v2 U; @! A' D$ V
- * @param string $filePath 文件地址路径
, e+ O0 m9 `% @* x( J* _! W$ K5 T - * @return string 图片 hash 值,失败则是 false
- p# [, Q5 Y% b8 l - * */ 6 C) F% |" o, B7 n% L
- public static function hashImageFile($filePath){
/ u2 l3 k5 J6 ^% f5 i& R/ T - $src = self::createImage($filePath); 4 E+ E7 R; I2 Z( R9 q* k. x
- $hashStr = self::hashImage($src);
5 @2 C0 r4 Q% q4 `& B - imagedestroy($src); . L) j% K! ?, S7 F& Q- X
-
( W! F5 E! J K3 Z. `, v" d! m - return $hashStr; * j$ k6 d' r$ i$ n" l
- } 2 d, n/ P7 D9 x. p2 d
-
( u/ H8 E9 U! w! Z - + R( W$ C. t. O" e7 p
- /**比较两个 hash 值,是不是相似
! A# P. X6 S, T% |2 N - * @param string $aHash A图片的 hash 值 1 K: E1 r/ D4 b- \
- * @param string $bHash B图片的 hash 值
& q* { k# [' Z. e7 x' P/ O$ L- f } - * @return bool 当图片相似则传递 true,否则是 false 5 I+ T2 ]2 ~! u! s
- * */
# K$ F* n( a0 @$ F2 j, U+ B! T - public static function isHashSimilar($aHash, $bHash){
' e' ]: O1 p ~6 x* H0 Y - $aL = strlen($aHash); $bL = strlen($bHash);
0 ?5 ~) E) e& q A5 X" K: P - if ($aL !== $bL){ return false; } 2 ?7 R3 t" G; h: d4 |4 _' j
-
, m" r: l: b/ r+ B T - /*计算容许落差的数量*/ & B+ D, h/ n0 T7 v9 }; s
- $allowGap = $aL*(100-self::$similarity)/100;
U+ ^ P$ k/ {4 `/ c! h" u- X -
/ C" O1 d- Q" H) e& L - /*计算两个 hash 值的汉明距离*/
) y4 x! i+ B$ z1 V - $distance = 0;
! B+ i0 l% C8 o i; I - for($i=0; $i<$aL; $i++){ s! N* c, {: p; z1 X0 ~$ U
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
" C( K2 A& F/ P# n - } 5 e4 i, L# H* M! E+ M8 e6 O
-
( {# @, \, b2 v2 | - return ($distance<=$allowGap) ? true : false;
, e: R& s9 Q5 c+ ?% w9 G& ~ - } $ W* m, Z F' f e5 |7 o2 v% A6 C
- : e6 X7 P" r- N* e# P) L
- # q! P( K6 X) n/ Z0 u2 M, c4 }
- /**比较两个图片文件,是不是相似
. }. L" ]2 d- ?+ u( f - * @param string $aHash A图片的路径 ' P" C2 T* M7 F% P- m1 i" C
- * @param string $bHash B图片的路径
7 p2 X0 E. j9 h8 s3 o - * @return bool 当图片相似则传递 true,否则是 false
" i- u. ]; n. X3 W" W$ z' p' ^ - * */
# F) ]$ I7 f/ K- j6 ? - public static function isImageFileSimilar($aPath, $bPath){ 4 j8 f- E. E$ c9 B% g3 }
- $aHash = ImageHash::hashImageFile($aPath); ( I9 P A1 L) z, k
- $bHash = ImageHash::hashImageFile($bPath); % B$ c, R1 N7 X7 B7 v+ g
- return ImageHash::isHashSimilar($aHash, $bHash);
6 }% b& q5 g6 V4 z3 y9 \ - }
9 X8 p; R g% w" V5 P! P; ?+ @- k -
, T( [0 F, u1 j, k - }/ d5 z. I: A8 p% s, q
复制代码
, C" {- Z: Q8 ?3 o+ A
2 V& A' c y4 |) r |
|