管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图* f- ^4 Q G- b) m. P
7 Y9 ~! D) q! }% w
" _5 @% Y, g0 E5 L) u1 H! c3 m由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。8 C9 h* j9 Y5 ^( R: r
- <?php % n- I& A5 F* r$ m+ ^ F/ |4 g: q0 G
- /**
. r- t4 S% k$ B% S( ?1 V9 ] - * 图片相似度比较
% @3 }6 g+ ?3 p& ? - *
9 ~4 _" K. |; A% w - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
a; [6 F# }0 ^) q- [' g - * @author jax.hu $ J9 ]) ^3 L) ` {
- * 7 n& j! }* y7 p% `& X
- * <code> + f, B7 c+ `& K: J7 \# y0 p9 B- V
- * //Sample_1
3 _- \4 N# B2 q+ ` @; \ - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
3 W- b. @9 P+ R. J4 M) L - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
Z% Z1 ] s! l% r. ~ - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 8 Y0 c8 Y. ~- @4 h
- * * n# I* t& n v ~; D% B5 d
- * //Sample_2 ) V- U+ T* Y% Y8 C$ c6 R
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); : K3 m$ D7 R: w# X$ Y
- * </code> 3 e& K2 Z6 ]5 }# M9 D2 B' U1 U) ]/ S
- */
9 C. ~5 s0 M* o f% O. l7 r, v -
. b! p' N, C _" o; a - class ImageHash {
0 o/ }+ X- o- M' T$ @( h - 1 ]. u8 M; I7 k
- /**取样倍率 1~10
" d+ v$ k/ x) V - * @access public 1 @. z9 i& g# Q9 Q6 p0 Q. w. @* \
- * @staticvar int
- ]4 O7 X0 K$ s9 [) e! w - * */
1 P' O7 c3 k: d7 S% S - public static $rate = 2; 1 E! ?! d! u7 y6 q3 i. p+ p
- 3 C, S$ g9 H2 Z" S5 J$ t) W* u
- /**相似度允许值 0~64
' G5 F2 c" g% Q; T: S - * @access public
. k- _1 s4 z7 W2 D/ Z - * @staticvar int 8 n; u( J/ C3 |) B4 N1 D% ^
- * */ 7 r# t. g+ {- ^( s5 K1 H6 e" L
- public static $similarity = 80; $ F2 x$ w9 i }6 N, a+ z7 U
- 0 r8 u) }: B' i
- /**图片类型对应的开启函数
# v3 s; C3 B7 C3 D' g - * @access private
2 A6 j! e* f; G* e - * @staticvar string
7 M$ Q& k7 D0 X# a6 |' F- [ - * */ 1 V" l/ u% V& U! z- F
- private static $_createFunc = array(
! |/ K* o# m. e# c0 h' m" x - IMAGETYPE_GIF =>'imageCreateFromGIF',
* i* e/ H' I* B, d - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
: T" l, E9 _* l) W& A - IMAGETYPE_PNG =>'imageCreateFromPNG', 9 E$ x9 x. K! u+ {. S2 n
- IMAGETYPE_BMP =>'imageCreateFromBMP', * G5 G( V6 o& y
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', + s# r4 `8 p8 P
- IMAGETYPE_XBM =>'imageCreateFromXBM',
# G2 q2 r9 S$ W# _$ n - );
5 y, A4 [( y4 E+ q -
8 K8 l8 }* ~1 X8 N - 6 P& d% E# x$ s4 U3 f
- /**从文件建立图片
" N' O& C) A, T; f \: m* T7 Y - * @param string $filePath 文件地址路径 * b" O$ ]3 b! b. ]% n
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
) o8 F' |& h9 K# Y. L - * */ ; M- z% d) q/ a
- public static function createImage($filePath){ 1 k4 p+ S6 R. @0 X
- if(!file_exists($filePath)){ return false; }
/ q, G) g! s% J/ R c -
+ y( m8 P# n; x7 V" d; d - /*判断文件类型是否可以开启*/
9 ? j; z, O9 t! N" i - $type = exif_imagetype($filePath);
% |# z+ j' s4 }1 V7 n - if(!array_key_exists($type,self::$_createFunc)){ return false; }
4 ]/ q5 o/ V: Z7 n+ L$ Q/ w4 M8 B1 j$ d& H -
3 W* T1 _" j" A+ a - $func = self::$_createFunc[$type]; + s( q4 b8 J' C& r! ?& e
- if(!function_exists($func)){ return false; }
1 t* ?8 I. f, y8 U -
$ r! V4 `/ ^8 ~/ C! g( p - return $func($filePath);
" i. y3 h6 J q - }
( `7 J1 m7 O$ E/ v* W% M" H/ n- J -
0 v, A0 M' F* T' V2 Q - 0 z4 q1 s/ i; n( w* l2 l. ]$ i4 g
- /**hash 图片 + h3 X6 z' X7 |+ n- Q# I, h
- * @param resource $src 图片 resource ID + X4 V' s# a1 N6 {$ F
- * @return string 图片 hash 值,失败则是 false
4 g+ a' D9 j( g- V$ h& J - * */ - y9 O, V2 R% s. x2 C
- public static function hashImage($src){
, `7 c2 r4 ?4 B( b. z - if(!$src){ return false; } 2 P( `3 u7 ~* i$ d @/ F# P' A0 k
-
1 W, ~3 L+ H. L5 Z4 h - /*缩小图片尺寸*/ 5 L8 z& }( l0 d/ W( C/ r
- $delta = 8 * self::$rate; 0 @5 s d( W! L5 H# |
- $img = imageCreateTrueColor($delta,$delta); 4 Y8 Y8 K( m! T
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
6 h* K {- q$ M* F& w" z - 1 r# j" ]& _3 d0 S
- /*计算图片灰阶值*/
@% h! G' f/ d5 l- X9 A - $grayArray = array();
) N2 N6 w" c6 j& R - for ($y=0; $y<$delta; $y++){
/ i- A8 s3 q/ D$ L8 F+ G - for ($x=0; $x<$delta; $x++){
2 `2 {/ W6 J3 g/ { G1 @, U6 I - $rgb = imagecolorat($img,$x,$y);
8 d1 e0 f3 d* x& Z$ _' z - $col = imagecolorsforindex($img, $rgb);
' _+ [: ?$ f. T6 R# f+ I' g8 A J! o$ \+ W - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ( B8 [0 r+ Q5 |' A
- / x8 f8 A- ?* q8 _
- $grayArray[] = $gray; 5 J8 S: B9 A( M% b
- } + H- {0 q# Q# \# n; p
- }
0 T" r% ], P2 F7 W2 f7 G) \ [3 C - imagedestroy($img);
. h/ U! a% g, \/ U% t -
2 v: G) t& z+ v( k/ J- l - /*计算所有像素的灰阶平均值*/ 5 c. ]0 N; M/ f7 ]1 i
- $average = array_sum($grayArray)/count($grayArray);
* o% h# Z; i5 ?: t - 5 G7 {; k2 }3 I7 G/ x" O7 A! k
- /*计算 hash 值*/ \7 q3 ]9 s; h. i! }, z' e
- $hashStr = ''; 5 K) F$ l7 U, J
- foreach ($grayArray as $gray){
! u" m9 J8 K# O/ ? - $hashStr .= ($gray>=$average) ? '1' : '0'; ) S' S: ?) k3 F6 `- z$ K" Z
- } 7 B6 s9 y7 ~! B p8 H
- * G" b! ^1 r6 X5 ]& n
- return $hashStr;
3 r2 B; u% a) l; f - } " z5 ~6 @: u3 n# J
- 5 a7 [& S) J* t7 y* x$ z
- / u h/ c. f7 ~- I
- /**hash 图片文件
' N5 o6 b1 ]4 ?: y - * @param string $filePath 文件地址路径
/ [$ Q/ O9 G" M0 M) T$ H - * @return string 图片 hash 值,失败则是 false & V. o. T; x/ T: T
- * */ # Z. r9 S8 {- r, j
- public static function hashImageFile($filePath){
/ U( b6 a8 c- Q9 Y8 o - $src = self::createImage($filePath); 6 Q& k/ x J6 K
- $hashStr = self::hashImage($src); 2 b* X9 r) ^# i0 y/ Y4 A' C+ z
- imagedestroy($src);
) K- `) @, l/ U: u7 u0 e - ( {% X! l- u, }4 y! P
- return $hashStr; ! s! F& Z0 f& s/ j, T# A+ W
- }
, e4 y3 p: f8 m7 }% l -
2 B1 _$ h8 G4 n1 L$ M7 K; L -
) x" v' [5 u* x1 G5 a - /**比较两个 hash 值,是不是相似 # m ?( u* F) G
- * @param string $aHash A图片的 hash 值 - E6 K/ d) o5 X4 `
- * @param string $bHash B图片的 hash 值
* `( r5 c& i( M T1 k9 W: l - * @return bool 当图片相似则传递 true,否则是 false
3 e" h% ^+ ]' f$ H+ m7 Z - * */ 1 A4 B0 z: ]" F" [8 r' {5 y
- public static function isHashSimilar($aHash, $bHash){ J5 R" X3 u3 m
- $aL = strlen($aHash); $bL = strlen($bHash);
; U' ?, r8 J2 i5 g* |$ R* s+ L - if ($aL !== $bL){ return false; } 3 p; }, |; E; [0 e
- + u- V5 Z* Y3 D3 w
- /*计算容许落差的数量*/ ( O7 u5 [% k$ c5 x- r6 ?: N) [
- $allowGap = $aL*(100-self::$similarity)/100; / K. m5 I; `# w. V3 n# ^
- 5 V/ B# o; {! ?
- /*计算两个 hash 值的汉明距离*/ " E+ n1 S: L9 g6 M% Y0 C1 I: e2 u. J
- $distance = 0; 5 a: @ A1 x8 b9 }+ k
- for($i=0; $i<$aL; $i++){ ; S! u+ K4 U$ t- M+ d: o
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } , P+ ~' T7 @4 P: h! r$ [8 Z
- } ' l5 C* ~5 X8 C. d' G9 l, Q
- ' g6 q" E+ Y. Q! l
- return ($distance<=$allowGap) ? true : false;
! b& R: i# e0 g2 D - }
5 }6 ^ x h+ X' K -
# h' `+ d' X* f7 b - ; n' ^0 F5 G. t6 @ j/ v
- /**比较两个图片文件,是不是相似 ; f( V- S9 y( i) Q+ Z! t
- * @param string $aHash A图片的路径 / H" d7 `3 S( B
- * @param string $bHash B图片的路径
; W& z X: D$ A# e( w3 i- m - * @return bool 当图片相似则传递 true,否则是 false ; Q, i9 t( I. v" `1 `
- * */
: R9 w7 q( s9 P' h5 k" L$ y - public static function isImageFileSimilar($aPath, $bPath){ ; n9 R8 ?! q4 E, _4 |( S& ]# u$ u; h
- $aHash = ImageHash::hashImageFile($aPath); " N! N7 g* p, t5 j/ U8 F* _) A) q0 c
- $bHash = ImageHash::hashImageFile($bPath); ( n5 `6 Z3 [1 v* c, z1 `3 |1 ^+ `. X% A
- return ImageHash::isHashSimilar($aHash, $bHash);
( Q% m n! M3 \% f3 Y - }
9 K' O3 ^: l+ D' h8 `* i0 B, J/ V -
, O: z, I; L8 b - }
# ?2 i" D4 X/ m; `6 L
复制代码
. Y" ]; r% Q: X+ W. k( {+ ^/ v5 e T
3 N) W/ N3 H; i |
|