管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图% o, |. K0 v% M+ f! ^) c6 J
( y1 r& w' O* m1 {. P
' v) z% v; P# @% [. Z
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
. t9 E% k1 _7 q2 x5 J- <?php # P" j) g: f! [2 S" |
- /**
: b0 c2 M& w6 u4 P2 l- H - * 图片相似度比较
`1 ]! v3 J* S4 s) M! [ - *
9 \" \) m* ?1 k" r% h+ e" m - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ' L# W- C9 Q/ ` s \# W. n
- * @author jax.hu
6 s: g" {& @$ M* R& m( ] - *
/ [. c. }* t7 ~ N - * <code>
3 n1 x" b2 V* M6 C. d - * //Sample_1
- b# a/ P- S9 K* Z7 D' l: f - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
$ l. w' Z6 C3 M1 G7 k6 R4 G8 o, F - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); + Q; }1 x4 x+ ]- c5 T. B8 m( A
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
; U- Q, x1 G$ k - * 0 \2 b/ Y J1 t; i. F
- * //Sample_2 - g% G q2 \) \$ ?5 h4 `0 P
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); $ O! r+ i9 Y1 e* O% I6 g. E# X9 z i
- * </code> 2 H) b. j x# x: [: y/ h; H
- */
9 z' q) L# T1 B; A$ R2 _ - 2 t1 v0 @9 k0 E% {: h
- class ImageHash {
7 \: x( i& N4 K& ~' ?) t -
, E. p0 i% C) k- b1 k0 W) Q, g - /**取样倍率 1~10
5 r1 E7 B) N& K+ Y1 h: }, ~2 C - * @access public : U+ ]% D; L/ W& f: U
- * @staticvar int
; P$ d$ S2 n! o2 H - * */ : l: |6 z' v1 P8 v3 K, o2 U6 b
- public static $rate = 2; + y8 J, {. P) e- H
-
t; p1 q. D, t5 v - /**相似度允许值 0~64
# F, N% j1 ?- z - * @access public % P2 d/ |" k- g+ N6 ?+ J @, k
- * @staticvar int
8 `( L; r$ {8 S( i+ g3 y& {0 D) a - * */ - _! O6 H Z- N+ i
- public static $similarity = 80;
+ o4 W. Y# D6 f" W - $ D' Z. G' H1 V/ {5 O
- /**图片类型对应的开启函数
) M9 n5 r/ y9 S3 u1 X5 @ - * @access private & Y* p8 v& W+ x0 g0 P
- * @staticvar string
8 M( x Y1 O/ | - * */ $ ^/ j4 F2 r# N. r& \
- private static $_createFunc = array( 7 @9 g+ J6 O1 I2 i6 o+ g7 k& f/ u
- IMAGETYPE_GIF =>'imageCreateFromGIF', 7 W8 ]0 l# V/ i( {! T
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
8 T0 M5 a F3 t& M A - IMAGETYPE_PNG =>'imageCreateFromPNG',
* t1 y! a( P* E - IMAGETYPE_BMP =>'imageCreateFromBMP', . l% f5 \. h1 F ]* g1 \; t
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
0 g8 v/ I/ G& k! e - IMAGETYPE_XBM =>'imageCreateFromXBM', 5 F: p5 I6 i" {; M3 ?/ a
- ); ; V$ s5 \4 B3 ^) g$ R, y$ n
- : T N; N$ z; h. u% w' F1 `
- 4 g( h" d/ }! b/ b( f; s; j
- /**从文件建立图片 ! S) |3 V: R8 f" N8 H {. o) r
- * @param string $filePath 文件地址路径 ) `/ R2 P5 Y& }- I
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false . L I, h" D- O3 y/ m* b+ q' d
- * */
1 O3 ]" |4 y, f+ c9 ^' ]0 n! C - public static function createImage($filePath){ 5 j6 G: f) t8 ?$ e2 g* V: H. [
- if(!file_exists($filePath)){ return false; } 6 I+ z# O( U6 F' e
- ! f1 ?4 A: n! L% A6 K
- /*判断文件类型是否可以开启*/ " H) N6 c" {# I: D
- $type = exif_imagetype($filePath); 8 v$ |+ S) v2 w1 u3 Z a
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
3 X6 v* Z( }# R+ ~3 Y3 u+ ], D -
$ e' v) m+ I! I5 B - $func = self::$_createFunc[$type]; ( `/ M% m9 W# L4 |3 N
- if(!function_exists($func)){ return false; } . v# C5 K, i+ h$ r& g1 b L7 I
-
+ ^ Z v, @/ V+ Y# |0 Y - return $func($filePath);
3 y+ W1 L, T: a3 T8 Z. C) b) ] - }
6 V, }, M4 [0 v) V - * |1 T5 z' t" q7 }4 b6 W5 E
-
; ]; O0 p& `. ^3 c - /**hash 图片
5 `; a! _1 U3 e9 g6 N m - * @param resource $src 图片 resource ID
4 O& E! C) Q" o1 h. {* Y - * @return string 图片 hash 值,失败则是 false ! r( v4 z2 K& T4 y
- * */ 4 \& k4 @, _* f- p& {
- public static function hashImage($src){
- T3 A( b5 \2 P3 d - if(!$src){ return false; }
- x2 |7 }4 l& E: n - % A9 B+ Z+ V, P
- /*缩小图片尺寸*/
$ y! ^+ d& j# j) J+ b: { - $delta = 8 * self::$rate; + A: j( u( J0 T+ O3 x1 m! }9 ~) H
- $img = imageCreateTrueColor($delta,$delta); + h. O1 G9 L4 z; p
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 1 |7 y3 k! S6 n! E2 z
-
^3 F; J7 K' s - /*计算图片灰阶值*/ $ L: {* G5 L/ B
- $grayArray = array();
1 c8 t1 ?; W8 P: e) M2 }3 m0 O- @7 P - for ($y=0; $y<$delta; $y++){
& i! M! B# Y9 F - for ($x=0; $x<$delta; $x++){ * d; U; S( {5 a9 R' z( D" V% O
- $rgb = imagecolorat($img,$x,$y);
% f$ `$ T% C! U - $col = imagecolorsforindex($img, $rgb); ; m- T7 p7 t' L/ j8 S
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
' C6 p _& o. Y- n2 g - ; C; u, p- D/ ?* E; A# B& W; ^" R
- $grayArray[] = $gray;
- y2 |9 g: T3 S6 O4 u; C - } # A+ i: O/ S9 X$ U( A. N5 b
- }
- b( U, J! F, A F* P6 I - imagedestroy($img); / M, ]6 U9 R1 N; C2 |
-
8 s! X9 W# X8 V; I - /*计算所有像素的灰阶平均值*/ |% T0 e; S4 [% P x E
- $average = array_sum($grayArray)/count($grayArray);
0 {, j# l% P# p# c -
( n7 d3 P& @% v) W, i+ G - /*计算 hash 值*/
) I6 R' ^+ I" F9 Y% F - $hashStr = ''; 6 W0 j! ~! M3 w, o8 Y2 X
- foreach ($grayArray as $gray){
+ I) C- l% @# ^" w0 H3 { - $hashStr .= ($gray>=$average) ? '1' : '0'; 3 w- L, m+ x' n# [
- }
% b% Z6 B+ q6 I& q - ; y. Q; u+ Y% n1 P' K
- return $hashStr; + l7 v$ k7 ~ {4 r2 ^6 n
- }
% q5 T. h/ {9 P' M. n6 r: l - 1 G6 L Y9 J# K; `) \3 c% V
- 3 R$ r% k( m) K2 y
- /**hash 图片文件 6 }) V7 d* d% d8 i" Z
- * @param string $filePath 文件地址路径 b+ W4 j9 O/ T2 H: A' N0 h; Y! _
- * @return string 图片 hash 值,失败则是 false 7 \- K/ l: v) Q1 m
- * */
6 T( ^* p1 P5 N7 o; n - public static function hashImageFile($filePath){ 3 D6 ~ E& w; G4 ~* A* u
- $src = self::createImage($filePath);
# c) O% J/ C5 u2 F, ~6 W - $hashStr = self::hashImage($src); : X* g% u! {( _$ P( E
- imagedestroy($src); 3 H/ W( \7 a" R8 _
- " S& I& C+ }+ p) X6 {% q
- return $hashStr;
9 n5 S6 \" E' c A - } ' ^! F5 B; b( y; U- r d
-
2 y9 U' W, P6 N6 L( ^" P -
7 D7 @7 k6 z" d3 e# u - /**比较两个 hash 值,是不是相似
! U$ I. @ S% }6 a/ |1 N7 S# b( N - * @param string $aHash A图片的 hash 值 $ Y/ _( M) {3 v: h; T' d) X
- * @param string $bHash B图片的 hash 值 4 ?5 \ H+ ?, i: A& j
- * @return bool 当图片相似则传递 true,否则是 false " ~/ ~# G- _, t
- * */ / J- Z' A2 u/ K- v8 p; M
- public static function isHashSimilar($aHash, $bHash){
0 ^; m' F1 `! y3 O: G+ | - $aL = strlen($aHash); $bL = strlen($bHash); 4 f4 J4 h4 w. @ g) S6 V
- if ($aL !== $bL){ return false; }
% F. [+ l7 { w- y9 I/ D& d - 8 h8 l2 i. S v/ y
- /*计算容许落差的数量*/ 9 j/ c( E% ?% U- F: a
- $allowGap = $aL*(100-self::$similarity)/100; 6 \' }8 X4 F3 |7 \, H% S- P
- / Q8 q( |2 c M( Q7 T) z: W# ~7 P
- /*计算两个 hash 值的汉明距离*/ , b7 {4 R" t' ^/ e+ J- k
- $distance = 0; + |+ H8 t" ?! e( Q8 m" {. }& b( i0 j
- for($i=0; $i<$aL; $i++){
: O( ?) r O* z: T7 s( l8 G8 {! ? - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
; j0 S( w% d$ o3 ] k - }
0 \$ r* N! q3 E5 ? -
/ x: v: f# l# p: ^# d9 \! }" ? - return ($distance<=$allowGap) ? true : false; + C, k9 w" J+ }9 h0 D8 D
- } & L' M0 |. q8 m4 x& S! s- o2 ^/ y
- % h- q7 E9 g$ `2 y5 V& F- h7 ^
- . N2 [: s/ L( N
- /**比较两个图片文件,是不是相似
{* f( C0 d* _8 K) O( @8 R: N* } - * @param string $aHash A图片的路径 # _8 H1 r t9 H- n7 j: W4 X% O
- * @param string $bHash B图片的路径
6 k9 W6 s5 E) ~5 r - * @return bool 当图片相似则传递 true,否则是 false
1 z" v0 ?1 E* a6 ^ - * */
G2 D1 K5 A) F: h. B! {9 H - public static function isImageFileSimilar($aPath, $bPath){ * x7 f! H* }, p
- $aHash = ImageHash::hashImageFile($aPath); . {0 Q7 z! z& }; _2 O, h( l
- $bHash = ImageHash::hashImageFile($bPath);
3 f$ W/ H. [) q; i, ^3 | - return ImageHash::isHashSimilar($aHash, $bHash);
7 I& E+ h2 \; j5 k - } 1 @" l1 A7 L1 ~- Z8 c
-
0 t% _: q7 @* h# b L - }
9 h$ k6 w+ r# @3 A: G
复制代码
6 W- g, x" v- d a- @0 J8 L8 |4 O' v) R; Z6 |8 Q6 }6 l
|
|