管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
' R, L" ]4 v7 k. c+ N9 ?; K3 }: n1 S; y- h2 b
/ S n6 {+ i* K; J A由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。0 a# j7 B" b4 E
- <?php # e: g N5 ?, ^* F6 @
- /**
* Q" k" Z2 R7 [: Z1 v - * 图片相似度比较 ' A# r$ D5 n% u, S# P
- *
% a9 {: |) d6 J8 a0 E- m% Y1 Q3 h - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
2 Y- j% q/ K% Q L7 O; z - * @author jax.hu
& {2 F' x8 c4 `2 X5 Y' p( V% ~ - *
0 d7 D# i$ U% X n2 S# u; L - * <code> 3 D0 u5 ?* F2 x9 i) J3 S
- * //Sample_1 9 y" ?2 w. {# }% ^
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
( m+ m" ?) d u+ `' T8 J - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
6 {8 }- Z; M1 U/ P - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); & o7 f4 s& ]% t8 Y1 K& P$ d( y
- * ) }) E% i0 h4 ]2 Y
- * //Sample_2
) T* m. w9 v9 ] c5 T0 J - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
7 G/ i+ }: Q. Q5 E) `& S - * </code> 9 Y' I+ U9 F4 k) k9 u# s0 [. r: m" @
- */ 7 p$ [7 l3 n- f
-
3 I! Q' [6 u J6 O; h! H& G1 ?8 N - class ImageHash {
2 J* Z- _0 @- T -
' L9 y( E" {1 j4 u; w - /**取样倍率 1~10 # ^3 v$ }5 \% T. }8 i' Y% E; H. K! G( z
- * @access public
: k2 o5 H0 v: p7 K - * @staticvar int
# i, w a9 i4 ~7 T. A: W) \ - * */ ' k2 ~8 [* w2 R( W. A
- public static $rate = 2; ) e. p) c3 Q1 j2 h9 G6 [
- 6 n8 G4 v/ V8 x. }, h1 v
- /**相似度允许值 0~64
4 J4 z! C4 I. c! s( C: J9 j - * @access public ; G, W% \/ H7 C! [4 l, D+ f
- * @staticvar int ( M* _$ u+ \* H: A; p( a
- * */
1 J" s! c% b W9 o0 n0 f - public static $similarity = 80; , w1 @0 r& R7 q: [2 D
-
6 O1 }# f2 R2 `" N" W; s! e - /**图片类型对应的开启函数 $ ~: L4 G Q$ O9 t, s+ Y5 J
- * @access private s/ y# O: H2 r) `5 \: H# c1 f5 S
- * @staticvar string
% ^% j4 A6 D# h# H - * */
; v/ G* L0 I2 c2 L2 g - private static $_createFunc = array(
% o2 j$ N4 k& \ - IMAGETYPE_GIF =>'imageCreateFromGIF', 8 A. _- h$ ~ U. \& e/ E0 h
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ^, t$ \, `- H* s0 f5 m. B
- IMAGETYPE_PNG =>'imageCreateFromPNG', 1 V' S8 w" ]1 y% W
- IMAGETYPE_BMP =>'imageCreateFromBMP',
1 ^4 ~- m( p$ \, i5 g2 v - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
9 P8 B# Q: P. }! N5 q - IMAGETYPE_XBM =>'imageCreateFromXBM', 2 M8 ~0 l# |2 I% j9 Y0 B
- );
& f" y. n( V- B1 O7 R* C -
# @9 i! _6 p) ~% ]9 F5 u - 1 o$ I0 l4 i+ [4 ]' i
- /**从文件建立图片
/ g' T+ Z, h' k+ I" Q/ f8 j - * @param string $filePath 文件地址路径
4 j$ @4 c7 Z& Z; R - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 8 |% _* n" H: Z! P" O0 c
- * */ 5 a$ X* z1 d; i* l- `$ {% j* E
- public static function createImage($filePath){
2 x1 l" d' m( Q4 O) v - if(!file_exists($filePath)){ return false; } : B, m/ h# Q! H- C. U; I5 P
-
W& a0 t% V. d. ^ - /*判断文件类型是否可以开启*/ 7 Y1 k! e3 g8 U' W$ y$ `
- $type = exif_imagetype($filePath);
+ E! g# K1 {) z$ X W: _- [+ z - if(!array_key_exists($type,self::$_createFunc)){ return false; } % b2 ~ o6 ~5 {% w
-
) A: G; G P' A" [. [( C5 | - $func = self::$_createFunc[$type]; ) I2 K" m: O: y( i6 `' g, U
- if(!function_exists($func)){ return false; } ( f! {9 n3 U$ C/ w8 ~6 ~
-
5 J( I O/ x1 \* o: h2 ? - return $func($filePath);
1 t/ j: r: I% D) M6 ^ - } / V! f- c; U) N1 \! R
- 1 K0 p+ z1 X$ N8 E" z
-
$ u0 a+ i3 \9 T- _/ ~ - /**hash 图片
) N' @( t, m1 O% N" E' r. C6 v. f - * @param resource $src 图片 resource ID % h; a. ^. V( T& p& ]& Y
- * @return string 图片 hash 值,失败则是 false 7 C; w4 t$ U: i8 x
- * */ G( a0 d$ \7 s- ?. Y1 x2 W* `. ~ g0 i
- public static function hashImage($src){ & ]7 D$ Q. d6 W4 W
- if(!$src){ return false; } 4 y9 }" P# O8 u: A$ _+ a
-
. o* r7 R0 Z. @$ w! x1 J( l, } - /*缩小图片尺寸*/ 6 v: A2 u$ r3 _8 R2 k2 t- m: j
- $delta = 8 * self::$rate; 6 X8 i; [) J- v
- $img = imageCreateTrueColor($delta,$delta);
2 s! }) O- f) a3 R - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
, B1 h E6 d' F9 Y - 1 h3 m; U% e4 u. G x+ p
- /*计算图片灰阶值*/
. h4 J- K: }: n/ B/ U9 [/ F) w - $grayArray = array(); 3 q4 n1 N2 J: X
- for ($y=0; $y<$delta; $y++){
6 N9 M# g9 u+ b4 V1 |) O+ ?5 X! i - for ($x=0; $x<$delta; $x++){
' ]/ t* r, G }1 u' Z$ U - $rgb = imagecolorat($img,$x,$y); . p3 ^4 P% Q3 K2 `! g `7 [
- $col = imagecolorsforindex($img, $rgb);
0 r3 w4 b% B$ j8 Z# a; Y - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
! ]4 ~' Z" U, O9 \; d -
! `, }: l; a) D3 \# _ - $grayArray[] = $gray;
- z4 D( v. l0 v7 ~8 | - } - p/ k; b5 l7 I' G" E, d
- }
: s6 K# a$ k- N: p% M% p# H - imagedestroy($img); + x& ]" V* Y) |( L& h. ~$ T
-
+ a- \3 M: x" w1 H4 n: b - /*计算所有像素的灰阶平均值*/
4 O8 Y5 t* d$ n v# ]9 _% I - $average = array_sum($grayArray)/count($grayArray); 8 o" H5 z, w, X- B3 Z
-
* O$ X, B) s) \2 s3 T$ M - /*计算 hash 值*/ 3 [& H; n% e# O) Q+ e- P6 N
- $hashStr = '';
w6 P' L% U! m! d6 p$ K - foreach ($grayArray as $gray){
" Z' a9 ]" i2 z, \ - $hashStr .= ($gray>=$average) ? '1' : '0';
( Y! t) c8 K; g; M' Y6 l - }
+ {/ N- f" z2 d- |& ]/ ` - 7 X& T0 Z b( q: a2 C& [; B! J# \/ \2 g& c
- return $hashStr; 6 @% k% x; s7 m( l
- } . O2 i1 I1 _, M; V# z4 T9 C
- " E$ R% o9 [) a$ R1 Y9 K$ \
-
% \4 G8 u; h$ G# \$ ]+ r - /**hash 图片文件
* H% N6 u) A& W$ O: a% F { - * @param string $filePath 文件地址路径 ) r8 F9 G+ ~. h. x& j
- * @return string 图片 hash 值,失败则是 false 9 N" w7 j7 i' v) @% S2 J* p9 a
- * */ ) M! J; x, X; O+ j& w+ X8 \3 K
- public static function hashImageFile($filePath){
8 g6 Y) X! v6 e/ U- D$ O' N - $src = self::createImage($filePath);
8 ^7 Y3 j% w7 r9 \6 h) _ - $hashStr = self::hashImage($src); " i8 A3 B8 u% ^
- imagedestroy($src);
4 M- \# S8 j9 T( B2 G | -
* _, O a8 B2 O: x - return $hashStr; 8 c: P i" P( Q
- } 4 Q+ u* x- J8 x' F
-
7 E/ H. r2 T5 C- S* X! Y% O -
- j! y ]' v5 k8 C: h - /**比较两个 hash 值,是不是相似
! O3 C. C S: B j- K; A0 ^0 N - * @param string $aHash A图片的 hash 值
+ Y2 j. H" H+ a+ \) K4 V - * @param string $bHash B图片的 hash 值
( R9 [3 p" q6 |/ o& @: p8 U - * @return bool 当图片相似则传递 true,否则是 false / r1 V9 {8 p" f+ ^% ]+ V
- * */ % B- e; y( E7 E' |" N
- public static function isHashSimilar($aHash, $bHash){ . H4 k- b1 w7 d) p8 E* I$ |2 ?2 p
- $aL = strlen($aHash); $bL = strlen($bHash); 6 K3 ^, j& r3 p7 H q
- if ($aL !== $bL){ return false; }
1 g- R) G. N. ?7 J0 | - h: X8 n4 k9 ?
- /*计算容许落差的数量*/ 7 y! _, z$ D! e9 a# [, P( c/ L' Q. H
- $allowGap = $aL*(100-self::$similarity)/100; 2 x# a* j/ m* M# [- o( B5 O- p
- " u$ y1 P3 L% W1 C. d2 z
- /*计算两个 hash 值的汉明距离*/ 5 _. u3 X+ b. b' C2 G' ?
- $distance = 0; 1 o6 b8 ?2 S. x5 F/ j
- for($i=0; $i<$aL; $i++){ H- Q+ v+ U* Z
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
. d u; W( G1 C' F; ?( P - } - S% w8 B E5 B& b: Y% ?
-
9 I0 Z G5 L0 T - return ($distance<=$allowGap) ? true : false;
/ H( ^5 s$ p3 K: p( _$ O. } - } % M0 y2 D! p1 ^# X4 |5 Y2 a
-
* F$ A+ H p. i' L) z$ b - * j; s2 c5 ]( c. r0 q
- /**比较两个图片文件,是不是相似
- K) a5 l* n5 z# Q - * @param string $aHash A图片的路径
9 J2 D. \; C, v0 B& c4 l; V - * @param string $bHash B图片的路径 & e- g& l( h* Q$ X. a9 z
- * @return bool 当图片相似则传递 true,否则是 false % M Z6 w3 H1 m$ E
- * */
& c* ?! ?6 ?: i1 u - public static function isImageFileSimilar($aPath, $bPath){ t- q6 C) w1 d- S
- $aHash = ImageHash::hashImageFile($aPath);
; N+ c" {3 ]* b. Z+ d - $bHash = ImageHash::hashImageFile($bPath); ( H l! ?5 W9 n! u) b
- return ImageHash::isHashSimilar($aHash, $bHash);
+ x: { W/ N- i7 A - } . k: i z6 ~& }+ d" h+ m' C# Z
-
[( m& [* L6 \' Z% h7 ]! `# c - }8 i7 s9 D# c6 |
复制代码
|0 n+ l7 n% \/ a) z- G1 o7 r4 ]9 w. i9 e' s9 C, {( `# I
|
|