管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图5 O+ V( u5 k! h# D; G0 |1 x
. E- w$ w- [- P: B
" X) S& `9 F8 ~. m) I. r由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。9 Q) b5 b( `( O) A1 B; V8 @% Y7 h0 R
- <?php
4 ~1 r2 x7 n6 m/ n4 Z - /**
6 T4 F$ a v) P4 `. y - * 图片相似度比较 0 t+ d A# f; ?8 R; S- `
- * 2 D: U; [8 I- w4 B1 ?7 V; |( V
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
) B8 c- _9 c7 U& q; Y( S3 h* o/ \ - * @author jax.hu
% H: D( U6 Q. x/ p- X7 v - *
) A, W. E6 M! X5 I' H5 b/ l& F - * <code>
q& ^/ e* H- ^9 o - * //Sample_1 & E k, \. w; M8 |2 W" V
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); , W( o5 W+ M, `
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); : A! G. m: {7 ~9 N! ~
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
) _% m$ u4 g# {( Q% y/ f. z - *
8 b. D3 @$ o/ h) c3 P1 A - * //Sample_2 5 p/ ?1 ^7 B* G" m: M
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); # n( n4 @4 E. a2 b" J8 n' K
- * </code> 8 P6 D: ^ @; j" u2 C
- */ + e$ I7 v) e3 }/ b+ |1 ]
- 1 W8 Q6 w. B( j4 t
- class ImageHash { % j1 n& h7 ]) R; u- f
- 1 q/ ^0 \# _7 Y& l' J; g
- /**取样倍率 1~10
( C% A# T) P0 u+ [8 b" l0 b - * @access public ) d: k/ K! S6 G3 S% G
- * @staticvar int ; ]8 i5 E; V9 T
- * */
6 d- Q! K4 F; w2 Z( N8 n1 g2 L) ? - public static $rate = 2; 6 {, T1 w K2 j0 C7 b
-
. f; w+ a9 l* V- s8 @" S% b - /**相似度允许值 0~64
% Y: L' B c9 X9 S$ Z - * @access public . x" m+ a) h0 U1 g* n) }
- * @staticvar int
8 G- W! T( `7 N B( }* U- s' Y t( w - * */ - q. t$ w% x8 F+ ~$ K. d- v4 T
- public static $similarity = 80;
2 T* y/ ~3 c5 y8 O1 H - 9 M6 ~& P8 J6 N7 S3 e; @
- /**图片类型对应的开启函数
! P6 L1 d% K$ @2 g# c - * @access private
% i9 L" Z( V6 T" m8 J! Q - * @staticvar string
' S- h+ ], L6 Y0 d, ~3 d - * */
6 f3 e- [* K, t; p9 t1 U; O - private static $_createFunc = array( : l% U! Y: v+ @6 |9 Z* y, y
- IMAGETYPE_GIF =>'imageCreateFromGIF', % n. L6 n& K: l
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
2 D* D1 }, M8 r( g% z, e" F0 X, z - IMAGETYPE_PNG =>'imageCreateFromPNG',
2 l8 {3 g+ U- w: Z- {: X1 ~; k - IMAGETYPE_BMP =>'imageCreateFromBMP',
4 Q& j8 l9 H. @& t% O3 \ - IMAGETYPE_WBMP =>'imageCreateFromWBMP', $ U; F% ?. U( f1 N$ S9 }
- IMAGETYPE_XBM =>'imageCreateFromXBM',
* f! X: @: i. c) r# r - ); $ a$ X7 z5 b6 J8 A( |7 H
-
" P Y, d; D+ x) z3 J1 l - * X% U+ M0 _( T8 I0 @
- /**从文件建立图片
( X2 ?0 Q! C; k - * @param string $filePath 文件地址路径 $ @ E( i/ v. f \
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
8 d6 y( m1 }: s5 z - * */ ! Z8 x6 x6 Z: I! p X3 n0 {5 O
- public static function createImage($filePath){
8 M0 s2 H, k# k7 u" t - if(!file_exists($filePath)){ return false; } * U" Z* \8 F/ T# J- V# Q) f3 e/ L" T
- 3 L# y* S% L6 L4 d% I
- /*判断文件类型是否可以开启*/ 9 V$ a7 \8 z2 F
- $type = exif_imagetype($filePath); 6 E) C0 z- A4 @3 |
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
8 S: R3 c; y% z4 o) w6 ?$ ] -
7 \* @: @2 Y$ O0 e7 o7 R9 H - $func = self::$_createFunc[$type]; 8 R) ^5 l4 X4 }! r
- if(!function_exists($func)){ return false; }
+ w$ a( X7 j6 ^7 X; V6 X5 v) d: S - 0 ~4 s" t+ d2 L! |7 k* ]7 A' L
- return $func($filePath); 5 m& q+ f1 s) ?$ d5 t. y$ u
- }
3 q7 G1 _0 h' J8 u3 P - h5 d& M. l5 S$ J/ g
- ' F# r' y" a7 i+ T8 H
- /**hash 图片
+ Y/ G: Z) H9 u0 @: Q8 k. c* u& H6 |4 E - * @param resource $src 图片 resource ID
{1 E! f8 m; Z& h9 t9 \ - * @return string 图片 hash 值,失败则是 false ( W u+ {, Q$ s- n7 `
- * */ 9 t6 K2 E/ j2 p1 ?
- public static function hashImage($src){
1 u' L6 Y4 P7 T# R' {* B - if(!$src){ return false; } }! p2 q; K8 _. h8 F- ]3 z. _
- 1 c! W1 Y3 H, E9 Z$ ^" [) u8 ?; \
- /*缩小图片尺寸*/ 5 U# l+ ?1 _8 b6 y1 j, }5 ~
- $delta = 8 * self::$rate; ( f( R+ R7 y3 n, B3 ]% {2 \ i
- $img = imageCreateTrueColor($delta,$delta); - [& k, O1 @% H
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); M/ v; L7 E% Q* A& t% ]$ I
- 4 R, X) B& K7 _& n4 v2 }: a
- /*计算图片灰阶值*/
$ d" I1 b, x5 a, L/ g. E* ? - $grayArray = array();
& @7 o$ \! t R4 e: x, A5 x - for ($y=0; $y<$delta; $y++){ : r; i* P( c. K8 p) V+ n' S
- for ($x=0; $x<$delta; $x++){ , Y$ T2 d. a! G! P- y; L
- $rgb = imagecolorat($img,$x,$y); / b' s, i @2 _0 R/ t
- $col = imagecolorsforindex($img, $rgb); ' s: V% N% L9 D9 ^4 \- Y8 a
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; , ?3 G( U3 t6 O; C1 }
- 4 T- D' j4 G; j$ R
- $grayArray[] = $gray;
4 A e8 C6 y3 `5 D, W! M - } 4 _9 W: }* t' {* z5 l- b
- }
+ p6 W5 j* m6 i5 N - imagedestroy($img); 2 o q+ r& i/ v$ @
- 3 o7 ^- J- q* T. G/ z/ m N2 T
- /*计算所有像素的灰阶平均值*/ 2 Y$ V9 }/ i5 r8 i
- $average = array_sum($grayArray)/count($grayArray);
* W i1 X5 [ [% c% ?+ k -
- e: G6 x1 y1 R- L1 j. q - /*计算 hash 值*/
5 J, o' X2 W$ B2 G0 A - $hashStr = '';
( ^! e- L0 f0 D1 e! {9 _% i/ P0 q - foreach ($grayArray as $gray){
! S6 Y, E7 J# w$ W! s$ b5 M4 O/ Y6 x - $hashStr .= ($gray>=$average) ? '1' : '0';
+ B3 i6 y( v/ q2 o& Z+ \ - } 3 {0 |. W. d. p. _+ ]) j
-
/ r- i* A$ S- g$ O2 j - return $hashStr; 3 W0 K# A0 l8 t6 Z( H! R
- } ; \ k: f" n0 i# P, g
- , F9 L9 }+ V6 Z( z0 V Q
- v* C- L% \( Z& ?% ], \
- /**hash 图片文件 9 J/ A4 K) I* Y& d: F
- * @param string $filePath 文件地址路径 ( l% |6 c+ r, w- D" w0 i
- * @return string 图片 hash 值,失败则是 false 3 y! j( @9 g3 v/ r
- * */
2 y! T6 ^5 z. N/ q1 f5 U' X - public static function hashImageFile($filePath){
# B$ g, m Y% o' N* ^8 X, v$ w - $src = self::createImage($filePath); ' d& {7 @! g0 `/ v% P
- $hashStr = self::hashImage($src);
* U+ i! z! U( J; B; J - imagedestroy($src);
$ U: |. e6 l+ E( r+ O# `$ K - 5 n- q6 t3 }# U/ M- B8 f! _
- return $hashStr;
; Z0 I1 V: b6 q1 E3 ^ - } + w! n" C4 j6 M% m
-
0 X" a l$ z2 z1 w; T' V V -
' v; C8 A2 C6 F - /**比较两个 hash 值,是不是相似 ( \+ X6 H/ @- R9 N2 _; Q
- * @param string $aHash A图片的 hash 值
# j- L6 O( |/ y" \ - * @param string $bHash B图片的 hash 值 , W$ ^& O" Z8 `6 B% h+ r
- * @return bool 当图片相似则传递 true,否则是 false ! A* M3 |' r( q6 b
- * */
# S/ q6 U* }4 o6 f7 C3 ?( [ - public static function isHashSimilar($aHash, $bHash){
9 {( b. v; [) p3 Y- |) \ - $aL = strlen($aHash); $bL = strlen($bHash);
% M4 I D/ ^ P2 D1 O - if ($aL !== $bL){ return false; }
0 E8 f9 t G* j& n. N, s7 ?/ \$ e% |/ \! r - 2 a! u5 j r% k( n$ ^* W+ j
- /*计算容许落差的数量*/ 7 m0 N3 ^, ]$ H( d) H5 z
- $allowGap = $aL*(100-self::$similarity)/100;
: G% x: G/ C* y% ]7 W' s; `, q -
% I s+ [5 C- n' q* _, r% O - /*计算两个 hash 值的汉明距离*/ 0 o) [& e$ [- v' k3 c7 u
- $distance = 0;
1 I7 m' W/ v- z4 q, s$ q v6 C - for($i=0; $i<$aL; $i++){
2 Q6 W$ W$ m( c5 W4 K" B6 M; p - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
) @+ G5 F% Q- r' {4 k7 I - } + d: \( B: C; z# c1 g4 H0 |% I5 m, G
-
; M9 L* q9 w- l x, o% v" ` - return ($distance<=$allowGap) ? true : false;
! p; f' r% o7 U - }
- `- o, t6 i- c -
. v" X+ F, z J! M$ {+ ^7 { -
7 x8 u2 o: G* W/ }0 B; b; R* @ - /**比较两个图片文件,是不是相似 4 n! h5 g: @0 o! v. H: ]+ u$ W
- * @param string $aHash A图片的路径
+ N' z! W4 A8 y - * @param string $bHash B图片的路径 . X, G7 O3 Y8 k: Z6 l! d- b2 t; f
- * @return bool 当图片相似则传递 true,否则是 false
# Q9 q6 [% H4 \2 O - * */
2 l! d7 S+ @/ t- x# R5 E0 {- N - public static function isImageFileSimilar($aPath, $bPath){ ; X& i0 S& d! ]: t
- $aHash = ImageHash::hashImageFile($aPath);
4 ^- l: I8 n) A8 `0 K2 w - $bHash = ImageHash::hashImageFile($bPath); ( z' m+ X, Z9 a ]; ]+ W* s- ]
- return ImageHash::isHashSimilar($aHash, $bHash); / i7 v9 g# k- Q5 h! q7 ^! m5 [
- } 4 v1 X; `4 e( [$ Q' a! ]3 v H
- 6 w- b) `& G0 D& j
- }
. [: j" ]8 X) p7 I- e6 S6 J
复制代码 1 G, Q9 t* c S v
1 _! c. V2 c' @& ` |
|