管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
- g! E+ _( @" x0 ?, X3 F
0 e) R. g; l4 k2 m7 R7 ^4 k, D B; v/ R
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。1 t5 F1 M1 H/ w8 e9 {
- <?php ( I& P/ g) W$ F3 m" J
- /**
4 e7 K/ l. a" a& \ - * 图片相似度比较
: K3 J5 b3 q4 B6 S. O8 Z - *
( m/ Y4 P. k" I- T3 [# w$ M& R - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 8 n7 y1 b" A. V, f
- * @author jax.hu " y. \8 k0 O3 T" {, G
- * 8 E. D/ Z0 J, L+ r# M8 E) A
- * <code>
" F5 |1 I% A# A& c - * //Sample_1 * \) [! I+ o! g. V: a
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 2 \; s/ ]& Y" O
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
7 l& _! L$ `! W& E6 m - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
; z" y$ W, t9 n' Y: A( b a - * ) d( ?; a* J, y! f) s, k2 q
- * //Sample_2 ' k% R+ h- x1 S3 }+ `0 O
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
- s' Z. b9 X7 `: s6 w! @- A4 C - * </code>
# ?; Z* G' V. N - */ 6 u4 W3 Z; U9 Q7 w+ ^+ l
- / C+ L3 g) n. ]; ]& x& N" h6 v# R& b
- class ImageHash { 2 n( ?4 H6 R8 H4 Z; Y+ [
- z" C- f" P- |: I8 z4 F, R1 _) w
- /**取样倍率 1~10
9 \; B1 e- j d6 r - * @access public 7 P. T2 v) e% O) Z' l
- * @staticvar int / Q y9 g" r3 ~$ v, r
- * */
# K# D/ a1 m* {, q0 O2 L - public static $rate = 2; & ]1 H1 N7 [5 e \& |
-
2 g& [7 a0 W% B' i; L6 G# ? } - /**相似度允许值 0~64
# R+ I$ w- ^$ \/ s - * @access public ) @% t1 | \+ ^) j6 U) ^
- * @staticvar int ( m4 `/ E* B! l) y
- * */ # i6 g2 f: k4 m; M" U
- public static $similarity = 80;
' H d1 c, I* N' V3 x, l -
4 Y: q6 u2 _6 G+ J - /**图片类型对应的开启函数
- k. _5 e0 ?+ ?4 Y4 V' e) M6 | - * @access private
/ }, p* H5 h6 A% A) C. n - * @staticvar string
/ z" c( Q! O! o4 X v u - * */ A9 Q" N/ s) I* Y) H; S
- private static $_createFunc = array(
& y2 d+ z. i! I9 E' l- w; T) X( ]: G* T - IMAGETYPE_GIF =>'imageCreateFromGIF', 5 J5 _5 @1 X6 Y" N
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ( H& Y* W# ~' W( ] h# g' U" V+ P* g
- IMAGETYPE_PNG =>'imageCreateFromPNG',
6 V* I" B+ b/ t+ o/ `1 K k6 e - IMAGETYPE_BMP =>'imageCreateFromBMP', / { B# _; r5 v I) ~0 A9 i
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', - e/ t4 c( x/ V6 ^ H
- IMAGETYPE_XBM =>'imageCreateFromXBM', 9 v5 g; y5 Z- b: U2 b( C; ~
- ); 5 M9 X" g1 V8 ^! w
- ) _% u! q0 P' x- a# Y( ?
-
- N5 n2 X* ^- m6 @7 n6 u. n - /**从文件建立图片
- Q6 g3 [2 f7 h8 w P% w8 ` - * @param string $filePath 文件地址路径 : {2 C5 |, S8 Z
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
$ v& w% U% H, `$ W* W3 { - * */
/ |& C4 e0 p* N* f3 F" P - public static function createImage($filePath){
- {" a/ e( q" i7 y1 S: e P - if(!file_exists($filePath)){ return false; }
4 o, f" V% Q( |+ n) w* {; c) K - ) L! X5 d! v) {' t4 s
- /*判断文件类型是否可以开启*/
! U7 O8 `: l# s/ `0 T- u O - $type = exif_imagetype($filePath); 5 n* P. u4 H! S/ {! J# R8 r; p
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ) p0 t: C( [% U" w
- 8 o' |$ a3 V- F" s" Z. ?
- $func = self::$_createFunc[$type];
: G$ F# l2 P& L4 c" R: Y3 { - if(!function_exists($func)){ return false; } 9 Z$ q0 G0 s+ h/ W* D4 c$ z) A6 S
- $ o2 L, X' R5 t5 Z# s
- return $func($filePath); 2 F, k2 l6 p/ J& A0 f
- }
: K# _) u6 B, H4 m( B6 [" f -
( M0 j! Y1 w: B. ^ -
7 B6 M$ Y$ E: C# E K& M) ^! P* U% Q - /**hash 图片
. j. P$ D4 ]7 o+ Q6 W - * @param resource $src 图片 resource ID 0 s$ i; {! }$ e7 |- t1 b9 q
- * @return string 图片 hash 值,失败则是 false
' Y* D# P: d0 \ }/ j - * */ S* Q+ M, z. z, b
- public static function hashImage($src){ % y+ n4 E" s* J
- if(!$src){ return false; } 4 k8 t4 S# `% P F4 `
-
# D: G+ A* P/ h - /*缩小图片尺寸*/ ) v' ?" |2 x3 F- [/ u# v
- $delta = 8 * self::$rate;
6 N% E& ~ t: X' p - $img = imageCreateTrueColor($delta,$delta); i' k: i4 N, [8 Z9 Q
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
' y7 i: g5 K6 y$ {2 K. s - 7 O2 E7 M7 \% X; V% L
- /*计算图片灰阶值*/
% u) G# G2 ^2 y2 H2 D, A) g - $grayArray = array(); ) y7 e. ]2 X( ^$ Z# x$ U6 {0 ~+ }
- for ($y=0; $y<$delta; $y++){
5 N" R: }5 Q' p+ N/ w! T% I - for ($x=0; $x<$delta; $x++){ 5 W6 a! T$ ]" I: q
- $rgb = imagecolorat($img,$x,$y); 4 }, _1 t' K1 r5 J; @/ e6 p
- $col = imagecolorsforindex($img, $rgb); ! J4 p3 m3 J9 V+ D+ b
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
# w& d5 [1 G2 W: p: |, ] -
2 A0 h0 @7 N# c - $grayArray[] = $gray; 7 s! |$ i3 i5 u! E4 b- w
- }
4 Q5 k( b6 \# g0 _8 z+ W1 n - } 9 T: H+ J6 R3 r/ q/ u3 n
- imagedestroy($img);
* b+ Y7 d" q% r' A9 q - # S5 @( Z5 C, J" Z- `3 z+ J* m
- /*计算所有像素的灰阶平均值*/
- I* U' d2 D6 ^, K6 o2 ]2 J - $average = array_sum($grayArray)/count($grayArray);
# M j$ h) C( G. p - + x* M# |( p" h1 I
- /*计算 hash 值*/
" v+ Q% O. u8 k( N& j0 V( y' R9 O6 U' g+ e - $hashStr = ''; " D' K* T- F5 W
- foreach ($grayArray as $gray){
% a9 Y/ D% [3 _( o: _ - $hashStr .= ($gray>=$average) ? '1' : '0';
0 M( q- C. {- E# _5 n( N7 i - }
( c/ r' \/ M, { - # L, G& d8 o3 r, q' g1 e1 o% S% ?
- return $hashStr;
* K. k0 [1 D$ K% U$ u7 p) @' \ - } 0 K: J/ \" k- O1 I; W
- - [/ Q) Y7 _ f) i
- # Q9 R, Q, y/ l6 J; l
- /**hash 图片文件
& Q0 X0 o; Q3 c) P* x4 f7 ?5 w - * @param string $filePath 文件地址路径
7 ]/ W8 j `) w; y" S, F# W1 c* M - * @return string 图片 hash 值,失败则是 false 5 S0 {6 T5 H c1 M3 [0 |% d
- * */ 6 _* T% ^$ b- G! ?, K0 j
- public static function hashImageFile($filePath){
0 h0 `5 ?. b, O: U - $src = self::createImage($filePath); , A# W) l9 f4 t; w: V! q) O, [" f
- $hashStr = self::hashImage($src); ]% V* Q) B3 K! a/ O8 _
- imagedestroy($src); 4 l' _/ ~4 z& Q3 Z
- 9 J+ b# O8 F6 j( m8 ~
- return $hashStr; ) Y J. w' B( p; s
- } ' R8 ^, T+ S) ~. m
- 2 b4 X& x B) \* U! z- K
-
+ }. R4 ^) g) b" L, U6 p - /**比较两个 hash 值,是不是相似 4 a" v! X. Z4 o
- * @param string $aHash A图片的 hash 值 % }* s7 ?1 [. t6 ]7 o' O
- * @param string $bHash B图片的 hash 值
+ M, E4 h* Y2 F; q Q) G - * @return bool 当图片相似则传递 true,否则是 false
" y: r5 e- Z4 O' J2 h# k - * */
3 O0 d! G/ T! j! x/ ^4 d/ [ - public static function isHashSimilar($aHash, $bHash){ 0 @! W# q n. P! [8 ^ p" q
- $aL = strlen($aHash); $bL = strlen($bHash);
) _' M' Q% {% M( M& ^+ t7 d8 X% l - if ($aL !== $bL){ return false; } ; a5 O: S# w0 W7 k/ R1 l
-
) a3 r. X$ W- i! Z7 B6 @; P3 B - /*计算容许落差的数量*/
4 r5 e7 Q; o/ H% c6 k - $allowGap = $aL*(100-self::$similarity)/100;
8 Z3 n, W, k* v - ( [5 q0 t, \0 S' @7 `
- /*计算两个 hash 值的汉明距离*/ ( a, y4 \2 h3 T: w( B! r8 }0 r3 D- `6 ]
- $distance = 0;
8 ?8 g2 m! H2 x D# F# m - for($i=0; $i<$aL; $i++){ 0 f% R' d2 m* z9 S) u; d
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } , d/ q+ k8 J5 p/ D
- }
: N* J+ f( g- b. D9 [9 w - & P% a _5 g4 H. h
- return ($distance<=$allowGap) ? true : false;
' K. @1 m, O% Y, M. D; g - } " M% [/ W) G0 x s s: K/ c
- ' y# U% }+ ^% [: n6 A- ^9 `
- % f7 H$ a. @7 s& x
- /**比较两个图片文件,是不是相似 - V( T: Q# r- { F2 Q: @
- * @param string $aHash A图片的路径
8 v; x' Z3 u0 J% {4 s3 r) z7 g( h& J - * @param string $bHash B图片的路径 " @6 ]% k: L# V0 S
- * @return bool 当图片相似则传递 true,否则是 false
/ H- _* h" ^4 M3 [+ u - * */
( Z3 a, q7 |: M% l- w+ Y - public static function isImageFileSimilar($aPath, $bPath){ * P7 c& W* r2 z/ s, D* ]
- $aHash = ImageHash::hashImageFile($aPath);
% ~0 R. q! \2 Z5 K' U - $bHash = ImageHash::hashImageFile($bPath);
4 E5 F& F0 a. K, W$ M, \ - return ImageHash::isHashSimilar($aHash, $bHash);
# r# N$ P' R# G+ L4 A' \ n - }
& Z' {* \6 ~* Y6 g5 i - + e6 P( Z# D2 E8 H* A+ Z
- }
) @, v9 ]0 w8 I2 r/ p! c
复制代码
; s" D, H0 ^6 ?/ s
" \$ j' F1 f5 m |
|