管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图4 i, q" F2 x6 j8 U6 u+ J1 Y; U
5 H0 @" f1 O7 `' G" I' m
! d7 z$ Q* u! ? P由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
3 w9 V7 T9 e+ M& u4 X6 d8 G9 W' K5 K- <?php
, O& x: n5 |& x& F8 m - /**
% \( T% l- \ C) q j S9 P - * 图片相似度比较
; z$ |1 _9 U8 Q% T& |8 G) K5 i - *
* h. w' _0 a1 N - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
: U4 b% _9 q" o; [6 V5 Y7 ~ - * @author jax.hu 9 O( a+ }/ q S
- *
/ S$ F& N9 @5 U: {4 M - * <code> % Y9 _" m- L5 O0 u, }/ g" @
- * //Sample_1
3 a C! @' G" T8 v% ]' {8 X - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
/ A3 k- I' E# n- m% s - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
$ G1 }* h9 s& V5 M+ l - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); , Y6 k$ \, f- l8 i( K+ l
- * " W* D7 I/ c; I$ h
- * //Sample_2
& v: w4 ]4 H8 m$ i! A, `8 l8 N% _ - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
4 f! V9 R, |& r3 H4 i' N: C# U) j @ - * </code>
! E( X9 K Z* W2 x - */
6 f3 |# g) q3 r' a% r - * T( r4 Z+ C, e; T* I
- class ImageHash {
- k5 j5 ?9 L6 W+ j! j -
0 h1 L7 Q( E' R% D - /**取样倍率 1~10
% J9 W U( w& c - * @access public 8 G2 F" _) E, }7 Y6 ~
- * @staticvar int
/ D ?3 E U5 ^- U5 P* d$ Y - * */
0 i, f5 J# w; D. a$ W* Z0 d - public static $rate = 2;
( d0 K$ L8 }% h; t7 H) R - . w$ P* i9 M/ F2 H8 k2 R8 y; ]
- /**相似度允许值 0~64
5 l& u n" h- J4 Y+ l1 Y7 H - * @access public ( H: b; q' z0 q! [8 H
- * @staticvar int
4 V, i, S9 g6 \0 _5 L q! f4 a - * */
7 V3 h1 P; L6 T1 q) T; M3 \: [ - public static $similarity = 80; 8 I1 g- g: t2 b; p0 p4 f( |* [
-
+ Q5 w9 F7 R; l! t/ O# x+ R - /**图片类型对应的开启函数
4 y. B$ k# ~3 q) ` P2 ? T - * @access private
J+ S- ]- U/ O& a% ]% S3 ~( b5 r - * @staticvar string
7 K( ?+ [- @5 a n. `7 T) j2 g6 ` - * */ 8 e1 Q1 P9 i7 `5 Q
- private static $_createFunc = array( ; Y( M& O+ B) `: H0 `. b
- IMAGETYPE_GIF =>'imageCreateFromGIF', ' e" K. p* w, l% U. v+ z
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', l3 U+ M9 u; A) a& C# x" ~: M
- IMAGETYPE_PNG =>'imageCreateFromPNG',
3 R. D( m+ s, q3 C+ V+ k% Q - IMAGETYPE_BMP =>'imageCreateFromBMP', % C/ Y3 Q+ ?, s. U( [# S2 G# E
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 2 n! [, G2 ?# N3 e# Y; l
- IMAGETYPE_XBM =>'imageCreateFromXBM',
; t- [/ ^, N4 x8 N+ q8 v& n - ); 5 n$ i- {6 l9 o% j0 y/ c7 x
- * P- v2 n, e# [
-
+ a( \" W/ u) a% V - /**从文件建立图片
2 K& I) H& I$ e9 J4 p w - * @param string $filePath 文件地址路径
, Q9 G- z0 X& J6 O1 i - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
, H2 k0 v) I. ~; f8 X3 Y+ G0 b" Y - * */
0 f# j! t+ g. @" O - public static function createImage($filePath){ 8 E$ @: w, g8 h
- if(!file_exists($filePath)){ return false; }
; Y! Q& _1 t- F- { - 9 t% Y: g+ Q$ [3 C* L' c
- /*判断文件类型是否可以开启*/
. L5 N& R9 P; o$ x' M0 J% t - $type = exif_imagetype($filePath);
3 m$ E; i2 u% K1 w' b9 P - if(!array_key_exists($type,self::$_createFunc)){ return false; } & r# J2 _6 ]: F) H2 h
-
* \# Q/ ^) V, L5 b4 Y* | - $func = self::$_createFunc[$type]; 8 Q$ {. H, M0 I/ N8 K% c
- if(!function_exists($func)){ return false; } . S8 [, ^6 J( o1 v# n* \' B) w
- # o# k* M+ d/ z$ |- v
- return $func($filePath); " z8 j `" ]" f S1 L( v$ x
- } + L: Q- |) d4 O1 y( E) U. V
- : ]5 ?0 E& p+ Z0 ~/ L8 Z
- 5 a' h8 y1 D! s. R1 L' b x4 @
- /**hash 图片 . Y# d% ]" Y1 i3 ]0 O8 V- Y! c1 U- I
- * @param resource $src 图片 resource ID
9 p$ @8 i, [# L3 t7 x) ?( {: W - * @return string 图片 hash 值,失败则是 false 0 E$ j1 o0 i' ~$ N$ {
- * */ 6 ], y! c, J0 B' E
- public static function hashImage($src){ 7 a6 w* x/ b& T% m0 c. e
- if(!$src){ return false; }
N: v9 X9 n5 j! r# U! a# y1 F -
# l0 D5 R3 d" m+ `: s - /*缩小图片尺寸*/
/ B( q9 `$ J, }7 g. h - $delta = 8 * self::$rate; " _9 s; s- u4 ^% s
- $img = imageCreateTrueColor($delta,$delta); ' o5 z2 f6 d. S& \" Q
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
# g6 r! y' r' Z5 H -
0 C9 I2 k+ ~0 h/ `; l. C - /*计算图片灰阶值*/
6 U5 D5 n1 i6 X$ @- o' S - $grayArray = array(); , P8 g9 E8 h. c/ E& L6 p
- for ($y=0; $y<$delta; $y++){ ! v0 u4 P7 O* k: Y
- for ($x=0; $x<$delta; $x++){
* v- C3 X% `. _- w - $rgb = imagecolorat($img,$x,$y);
. B+ U# d' ]2 e) z- d+ `# U7 d% S - $col = imagecolorsforindex($img, $rgb);
) e) n6 K8 L8 x5 n' d* ` - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
( Z5 s0 f' n1 S - 0 U) d& p9 h& w6 g
- $grayArray[] = $gray;
% N/ y5 b, s( {" V% C3 J3 m1 v - } ; a4 j) z, @% |& Q! R b
- } * }( z! k& N2 k) b* M
- imagedestroy($img);
1 I* R* ~% u. k - 5 ^ @1 U6 t }: S0 Z. k6 H( \
- /*计算所有像素的灰阶平均值*/
& C3 w5 c2 W1 k% v - $average = array_sum($grayArray)/count($grayArray);
( |( I- x1 R) ]; N5 M - 6 }. W2 k3 k9 a7 I( j! a. @
- /*计算 hash 值*/ & q2 D* [8 M X7 e8 g; T4 Z: D, @, M
- $hashStr = '';
! u0 U# o, y. k0 B( k - foreach ($grayArray as $gray){
8 c- M ?! Y- m% F - $hashStr .= ($gray>=$average) ? '1' : '0'; 5 p8 J$ ?5 e8 x, N4 |& P
- }
7 p* ?4 l3 `3 b+ B6 v -
) Q' f5 J) S( V$ l7 R - return $hashStr;
/ P; q* x/ w+ ^# y# N# V - }
% F$ a* U- x9 g2 f0 p! e. W0 R -
- i0 r0 X2 l# |& z; b7 P: ~ - & x0 {, x/ f! J6 r2 m
- /**hash 图片文件
: Y' t3 D$ i, c0 c - * @param string $filePath 文件地址路径 ; T8 ?9 w8 e# ~0 S7 h3 o) {
- * @return string 图片 hash 值,失败则是 false * F6 {7 o$ _, L9 j' ~5 b
- * */
* h0 y% k1 h6 P+ P' R: u/ r - public static function hashImageFile($filePath){
9 H* G( H! {; D - $src = self::createImage($filePath);
0 Q! m0 o0 k; P0 j) K* X8 P - $hashStr = self::hashImage($src); / V/ R4 p0 T* E
- imagedestroy($src);
* T0 d: o1 c+ \) A -
9 b, j1 r+ T/ y" k# @4 O7 t - return $hashStr;
: n9 _8 A, V4 t6 ? - }
8 N W3 i! y! r# F6 v# R2 w/ U -
* v( G9 G$ R1 V1 }4 Z - " s2 h% T- h( Y0 c, c
- /**比较两个 hash 值,是不是相似 ' h: |' Q2 H% R- T
- * @param string $aHash A图片的 hash 值 # H: \5 a7 l- ]# x8 C( V
- * @param string $bHash B图片的 hash 值 ( N7 K3 ]$ H6 ?; H1 W6 V: t
- * @return bool 当图片相似则传递 true,否则是 false
6 S. l$ j+ d' E6 R" ? - * */
2 B! X+ T6 y: x1 s8 N; t - public static function isHashSimilar($aHash, $bHash){ 0 V0 l3 V% e$ r+ \1 i
- $aL = strlen($aHash); $bL = strlen($bHash);
! C) N! X' Q2 L+ Z - if ($aL !== $bL){ return false; } ( u+ m3 R2 |3 ~$ u5 J
-
/ F0 E9 d. \6 o- a - /*计算容许落差的数量*/ ( a5 \6 A7 u/ U- g3 k: A
- $allowGap = $aL*(100-self::$similarity)/100;
6 `- {1 U; V4 j9 M$ i% w - " ~, I. E5 S+ O) M+ c8 B
- /*计算两个 hash 值的汉明距离*/
8 \) \+ A' t, d5 @- z0 [) q0 H6 c - $distance = 0; " p9 q* M& H2 B" d
- for($i=0; $i<$aL; $i++){ ) Y, _0 K4 s& N: g/ L& k
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } - m( k' N& w' W# t) a
- } : ^) \% A( ]2 K j
-
+ m# \2 _9 B2 [& m0 ~& \ - return ($distance<=$allowGap) ? true : false;
6 j$ @6 w: b! i1 D0 S - } f% w! S! h- q- f0 B
- & \5 d) [: w5 }) ^6 l* \+ b9 X
-
M1 I1 x$ L) F' D5 l, f - /**比较两个图片文件,是不是相似 6 `, [) V- g7 r: y) S3 H& g0 E$ p
- * @param string $aHash A图片的路径 9 q/ R$ I! K+ T, [9 O! `
- * @param string $bHash B图片的路径
. v( m) F0 u7 X* j5 h! N( U7 k3 x - * @return bool 当图片相似则传递 true,否则是 false * C# d7 O; r7 P
- * */ $ T% l t, c0 e) n; n: z
- public static function isImageFileSimilar($aPath, $bPath){ : q% x3 C( Z( G& n$ d. `, L
- $aHash = ImageHash::hashImageFile($aPath);
& i2 o0 o! y' y6 \/ c: b) E - $bHash = ImageHash::hashImageFile($bPath);
* ?# M( ]0 x) X* M) U - return ImageHash::isHashSimilar($aHash, $bHash); 1 K- N+ V& D( Y
- }
( Y, r# ?9 x) [& o - + ]3 \( O# @. b8 F2 D
- }
; K6 B# c0 b7 D
复制代码 + l6 d' g; K0 l4 ?" G
7 V8 s! s7 `. P& u4 I A
|
|