管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
0 ^& j' o3 y: j5 T" A
/ F( K6 i7 I' u* r/ M, @: ^
8 D! g) ^7 i; ]9 d j! P6 c由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。6 h5 @) n' |; P8 m! a P
- <?php
8 i, x3 ]9 h8 J" E0 d - /**
4 @ Y& F5 G6 C3 b& V9 h9 \0 z9 C# @ - * 图片相似度比较 " U9 h. Z8 E$ [( G% N9 k& `7 ^' ~
- *
( L4 P8 U8 M- U7 N% k! k8 p9 ^2 Q - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
# h @$ O6 F) J' f' M1 x - * @author jax.hu
& ~9 m$ o# ^ E6 X! t; V$ t5 ~; { - *
" V( o) N- _, k; | _! C - * <code> . t0 d! g4 g( R; ?% C7 ^% W
- * //Sample_1
# T1 \' `, D+ B3 n - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
0 e' {3 E7 ~0 ]9 f - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); - n( f3 j+ V3 k) r
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
% }0 A: W4 u3 R7 y - * 7 P8 U/ m6 h( I: P) V' k. D" Q
- * //Sample_2 ! E' u q3 B( o6 \- r
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
$ B- B. {+ j Q( O* z+ \ - * </code> 8 B- u" ]& H j
- */
6 N( M8 R( _; R1 B% b- V( ] - , L( A) m/ H. y8 e* t: W
- class ImageHash {
8 e, \ S3 D. T- ]- l" N1 [ -
. X$ U8 \& f9 S( j; @1 J - /**取样倍率 1~10
- \7 r2 v7 n, C: a: v" d# j8 w8 M* o - * @access public
' I% O- S4 _' f' Z3 R4 A; b: b5 W* U - * @staticvar int 3 S$ h- m! j: i# D
- * */ ! J( y2 x- S" O. C) ^9 O2 c
- public static $rate = 2; . M( T0 x0 q. Z
-
6 t9 G( A: n7 H* h4 J- A - /**相似度允许值 0~64
& W1 J2 ]" c" _7 w8 |3 d' P - * @access public
# m$ H7 {6 e6 [$ w+ G; v) V# h1 I - * @staticvar int
: _, @, Y, J, W3 B& n# M - * */
7 N! c' w: Q( D, l! K& K4 Z7 o - public static $similarity = 80;
; v4 ]6 g8 J; s9 S- A -
* l( d( Y8 b8 q, ?! Q7 ?) w - /**图片类型对应的开启函数
2 M" p% _6 `5 _. _ - * @access private
% i! ~9 ?( M. d6 h. ^ - * @staticvar string
9 `3 o/ w1 S/ u$ y) S( x) P - * */
2 V& r- R4 l0 p% X. D - private static $_createFunc = array( " W* I( D" f& A, o$ M4 H2 w" w
- IMAGETYPE_GIF =>'imageCreateFromGIF',
0 L3 m2 z% |2 F( w9 k - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
3 b* w! I0 I4 I/ h* U - IMAGETYPE_PNG =>'imageCreateFromPNG',
( F0 ^, B1 u7 ?; L( g - IMAGETYPE_BMP =>'imageCreateFromBMP', + X/ t; J+ ^% h: N
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
7 T5 P- x7 r. o& W+ w$ P# g4 y - IMAGETYPE_XBM =>'imageCreateFromXBM',
( k/ i! S1 U* [+ I- G* y/ r+ R - ); . _. m+ |# B+ S
- y) k/ z$ O% [
- 0 Q& w! y: S- t. r8 x
- /**从文件建立图片
3 F3 J) I. L0 W( `; r" z - * @param string $filePath 文件地址路径
- `9 g4 O5 }0 {, h! q - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false " g+ k& g/ r: S h" T; ?
- * */ / [: v9 Q& e; a0 f# B: X
- public static function createImage($filePath){ % N* g7 ?5 Y3 N" z. _! o3 ]
- if(!file_exists($filePath)){ return false; }
5 ~9 n2 S* Q9 z: P n7 L -
, m- a' y& S7 D: j# j p6 V - /*判断文件类型是否可以开启*/
4 ?' Q: P; Y( A9 f - $type = exif_imagetype($filePath); 3 L2 ?( X+ n6 A$ p/ \1 e) T- ~( y# X
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
2 S, _0 M" U3 A t3 X3 Q' P -
0 p: E2 i1 h- v/ L - $func = self::$_createFunc[$type];
5 ~ H1 a( u1 T& ]8 }( T R - if(!function_exists($func)){ return false; } # @7 P1 y9 F# M# [
- 7 M+ ~1 h; {& g+ ~0 ^8 a. r9 R
- return $func($filePath);
~* n" |" m5 [" t2 G$ A0 t3 ` - }
8 T( f# w& e' {1 m7 @ -
p8 t& _3 T8 I. C0 \3 v - 8 i5 L9 f- y# z/ w
- /**hash 图片 6 j& X: h! o, h+ i
- * @param resource $src 图片 resource ID
0 _ ~7 z& R- z# ]+ n - * @return string 图片 hash 值,失败则是 false
- \ N, ^% d. O7 {) _0 z8 N7 K/ f - * */
9 Q3 S# B; E6 j+ P( X1 o - public static function hashImage($src){
/ L( I3 s) T* d9 j. M0 S - if(!$src){ return false; }
; S7 [3 \0 ? }. j3 x5 G% X9 E - + A( k& x1 r k" ^
- /*缩小图片尺寸*/
1 e! R5 n! j- s) O/ v - $delta = 8 * self::$rate; 4 g& i' M- `, h
- $img = imageCreateTrueColor($delta,$delta); 7 B8 h) l K( q2 D7 J: I$ X
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
1 e* W0 m4 l: ~' w, j6 C; [' q+ Q - # A; t1 D9 B5 \! Z
- /*计算图片灰阶值*/
* \1 C/ z9 A( Q1 Z6 } - $grayArray = array(); + u0 {: B1 |3 Y" [7 `/ D1 H: Z E
- for ($y=0; $y<$delta; $y++){
2 U5 R3 ^' A4 u0 t- G - for ($x=0; $x<$delta; $x++){ ( [1 U6 B( D' y9 t: y% A
- $rgb = imagecolorat($img,$x,$y);
7 N+ B* E3 T6 B* u& X - $col = imagecolorsforindex($img, $rgb);
6 @3 n2 J; b9 r& E4 C - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ~& ]; D: \" f6 E0 z
- , k4 t, a' z7 b" }- @% o% F# `; L
- $grayArray[] = $gray;
: B( _$ R- `; }7 |, Y; j% S - }
/ z* p% M+ ^. u, F# u5 L$ {* Z - }
( K8 L9 @8 I. B1 s; l1 M - imagedestroy($img);
) b0 h! _$ o6 t+ B8 d1 p8 O -
: V( i, [3 p* ^2 u2 _) H& H" K - /*计算所有像素的灰阶平均值*/ - w# H- A& _% Z
- $average = array_sum($grayArray)/count($grayArray); & n* D6 C7 [: ^
-
# V3 ^8 [0 Z& Y3 l - /*计算 hash 值*/ ) ]7 ^2 M# F: ]8 ^
- $hashStr = ''; ; |, d2 |% X- D4 q
- foreach ($grayArray as $gray){
0 W$ y1 ~9 r7 D - $hashStr .= ($gray>=$average) ? '1' : '0'; * ^! S7 P; @. J, X: w T
- }
* l( O5 p% n5 R0 u* O! e -
) b6 s. t1 p. b5 M+ N - return $hashStr;
- q5 T% M; M. X* c, [# V - } # h7 L) I5 u6 [+ ^; f/ ~
- + L- ~7 F9 U$ B& ]3 `" E
-
8 r- v9 Z) {0 a - /**hash 图片文件 9 G2 d+ p6 f7 \% h' A" d
- * @param string $filePath 文件地址路径
: e [3 X2 V. V - * @return string 图片 hash 值,失败则是 false
6 j8 {5 O$ m$ y/ ~ l( f- O! w - * */ # a1 y. q8 s$ P/ F0 P# [# C4 _
- public static function hashImageFile($filePath){
8 i' j$ ]7 L& R* ~6 a( R - $src = self::createImage($filePath); 0 ]* M f0 V0 _( ] D
- $hashStr = self::hashImage($src); 6 E" b( d9 b }% ? P5 |# F; \& w
- imagedestroy($src);
1 j r4 C/ y$ } t5 C' J -
* v* R9 K* d5 X1 a: B7 b - return $hashStr;
7 ?& \9 {$ l/ R0 O' k: |! j - }
% d8 y$ Q& h' q/ p' H o -
# ~+ E8 j8 ?0 ?# d% w - . K( t# K8 i3 P7 I6 ]. g
- /**比较两个 hash 值,是不是相似 6 J7 [0 S& \; z8 L5 a# L
- * @param string $aHash A图片的 hash 值
, i2 |) c" R7 i& V - * @param string $bHash B图片的 hash 值
, v2 i9 J) \/ I, N4 h0 k - * @return bool 当图片相似则传递 true,否则是 false ) p1 P6 M+ Z, J' _3 d1 v
- * */
4 h6 T# A* Y0 R9 \) { - public static function isHashSimilar($aHash, $bHash){
/ P3 [( [ X- M1 w& ^8 E. E - $aL = strlen($aHash); $bL = strlen($bHash); 1 C1 Y" M* J. ~( i
- if ($aL !== $bL){ return false; } . `! d) O8 W* s
- 2 f7 b. S4 k+ P* m7 q/ W2 }; `
- /*计算容许落差的数量*/
8 F6 ^5 j) ^3 x P B% N) d y0 i - $allowGap = $aL*(100-self::$similarity)/100;
7 `/ i# H. ~1 }0 J) u7 b# v - 2 ~. s/ [8 [0 R6 K
- /*计算两个 hash 值的汉明距离*/
+ ] j3 b3 H& y$ X - $distance = 0; 7 ?& u& @) {% w( Z
- for($i=0; $i<$aL; $i++){
}/ R& B$ m7 y4 c w J" X - if ($aHash{$i} !== $bHash{$i}){ $distance++; } : @0 n. C3 g$ g3 W, s3 `
- } 3 ?8 H: X6 q$ l9 X0 M7 f7 U
- 1 y, Z8 D# ^+ X
- return ($distance<=$allowGap) ? true : false;
0 }- s& s, A' M& m - }
" Q( A! O3 q: R# P) C4 i. N* ] - 1 V4 t& c& I3 v: l: w
- _' Y8 n2 l3 w
- /**比较两个图片文件,是不是相似
3 R) P3 }6 r% Y: A - * @param string $aHash A图片的路径
8 F; T: t1 ]8 H - * @param string $bHash B图片的路径
# o2 h4 b) G) d! f' V - * @return bool 当图片相似则传递 true,否则是 false
5 G+ N" k* `. ^1 i - * */ 4 F3 P% q# E+ a' g
- public static function isImageFileSimilar($aPath, $bPath){ 2 j3 B# m) G& i0 A6 Z
- $aHash = ImageHash::hashImageFile($aPath); 9 Z9 l+ N* K9 T; }* |
- $bHash = ImageHash::hashImageFile($bPath); ; s% }$ q' d$ j
- return ImageHash::isHashSimilar($aHash, $bHash); - G9 P, G: L& A8 ]
- } . |# c; k( T) u& r
- " W7 Z! z# f- D4 D
- }3 T, }2 m: T* h& Q5 S+ @
复制代码
/ _( N4 C) `6 Z: n4 N1 F1 y/ Q& ~- }8 q: m* W+ q7 {$ o# ~
|
|