管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图, b' a% C& w) D5 W! x( w0 H+ _
% r& ~2 w W1 t- G6 y- Q7 ^; G
, h0 B( C5 T4 S' J, m由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。! r- \, l4 ~5 I$ K
- <?php 7 q3 _" l# U5 X ]9 H* ]- P
- /** & B3 t o4 w( ]3 Y4 h
- * 图片相似度比较 ' m( J4 A6 f) R; [, |
- *
! c: a4 s% q/ v, q c - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; & a9 c# D( p' }
- * @author jax.hu
5 x" W/ X, E; |6 G7 q$ Z - *
0 m4 r, \& L( L+ O - * <code> & X$ f6 Y$ W2 R1 y
- * //Sample_1 7 z, B+ G2 Q! \9 p
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 4 K9 K q6 J7 |3 H5 M9 Z
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ; j* g5 L: d$ y, L3 ^6 u4 e0 }( M
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
+ b; J! g) P; T3 q6 E8 e2 ] - *
3 W6 w3 L$ E. l - * //Sample_2
# c" M, A4 }0 x% g - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
# C; ~( @ U' \& G4 U - * </code> ; V# U, t0 w! K& K6 B
- */
6 J0 @. B- o" l0 Z" Q) ~. b" f -
' i0 S4 U V# u% [1 U8 U - class ImageHash { ( v: V! r# U E( y0 U
- / ^6 P$ b: @/ o9 A$ A( U. E0 F
- /**取样倍率 1~10
( J6 j8 g' b6 Q - * @access public 3 o4 c7 a% w+ K7 I2 `
- * @staticvar int
) X% ^& S! Z" m - * */
+ `0 K! M \$ Y | - public static $rate = 2;
6 u1 `! g% } d! o2 M3 D. ^( i -
) ?1 L; @' i3 c( Q - /**相似度允许值 0~64
# E" D8 ~( c4 z* x" O; W9 |1 V# Y - * @access public
7 B4 b- Y1 J+ ?/ z4 S0 C8 z0 d% G - * @staticvar int + m1 a( ?$ [" j) W( j$ s8 N
- * */
" t A& ]* f, q% J. Q# n - public static $similarity = 80; 0 c( \* l) m \) j. K, ~9 f3 m
- * d% y3 S& l7 ]
- /**图片类型对应的开启函数
" I) \" |7 L2 O' S; C' a0 F! { - * @access private
" a L/ S& O. u - * @staticvar string 1 R; P8 n5 i; _ @, _) O
- * */ 5 E3 y6 n$ }! ^; _! C4 _+ Z
- private static $_createFunc = array( % N3 z+ g6 _4 |; R t
- IMAGETYPE_GIF =>'imageCreateFromGIF',
- T$ s' S! j) f: a - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
& u4 P+ x& T% a; Y( H; C, _, [ - IMAGETYPE_PNG =>'imageCreateFromPNG', / E2 z* ^& q% `% {8 r; o$ i* r9 U
- IMAGETYPE_BMP =>'imageCreateFromBMP', 0 F. ]+ s5 h. f% N2 C1 D- U. K# e0 r
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
, k# g1 f2 v# _; R( ~5 z. r - IMAGETYPE_XBM =>'imageCreateFromXBM', 9 y* i6 u; z6 ~2 ~. n% a
- ); # i' c! l. c. h, c+ |# z
-
$ l7 P1 q* B* O9 Z+ D2 u - . ~/ F; H* h7 _+ Y! }5 c3 u1 |5 R
- /**从文件建立图片 ; e+ b" A* [* p, F$ Q
- * @param string $filePath 文件地址路径 1 X( f5 j- R2 J; g
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
) P6 z# s+ ^! y0 X. c! ~% a- P' x6 l - * */
* f$ L& _7 H$ n8 Q0 N, k3 f# O - public static function createImage($filePath){
% r- y* g' n8 O0 T- S; y4 E - if(!file_exists($filePath)){ return false; } / @+ e" t3 W6 c ~* _& N
- ! l8 Y# Q5 }; d+ t q
- /*判断文件类型是否可以开启*/ 0 d7 m& K- e1 u5 g, w G
- $type = exif_imagetype($filePath);
7 k0 {! A. o6 w7 o+ n! p - if(!array_key_exists($type,self::$_createFunc)){ return false; } , {; A7 Z" e3 q8 A, h- x8 K
- ( ]$ x# Z2 F& |& s4 P
- $func = self::$_createFunc[$type];
- v% D& V5 D! n. d; o7 y - if(!function_exists($func)){ return false; }
" j d/ A3 ^4 X$ w! y5 N -
1 j" b% y0 L- i, f$ G @ - return $func($filePath); 9 f; c( p6 y7 k
- }
6 [0 ]* G/ {3 R - * S. ^! p% @+ ?6 c# [$ p
- & f* F5 C6 M* Y
- /**hash 图片
. q# c, I; o% g - * @param resource $src 图片 resource ID
# W/ M) i9 ?" b2 l8 u1 i5 x - * @return string 图片 hash 值,失败则是 false ( V$ y ^: `7 x' M
- * */
0 v; T" i2 ~, |$ B1 X _ - public static function hashImage($src){
- h! `& E& z7 A& G: K1 u - if(!$src){ return false; } 5 ^1 \& D6 @# p) h
- : }* h2 v' n" l7 v C
- /*缩小图片尺寸*/ # Y' q% Z; Q3 ?3 `0 x2 Z u
- $delta = 8 * self::$rate;
' D3 n# i) e3 T, f - $img = imageCreateTrueColor($delta,$delta); 8 J7 y* c' s j2 D! _$ v! B
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 7 g& F+ q( E) q
-
/ f4 K4 i" V& G: R2 e6 Y - /*计算图片灰阶值*/ # m5 k, O3 C6 e, Q+ j* F! W
- $grayArray = array();
+ v4 p6 d4 H9 r7 ?5 ~ f5 H0 {& _) ]/ v - for ($y=0; $y<$delta; $y++){
( j9 @; L7 [/ N- v, `! x - for ($x=0; $x<$delta; $x++){
6 U S. E+ v( [ - $rgb = imagecolorat($img,$x,$y); ' x: q& J, A k( j: `2 t; g% H
- $col = imagecolorsforindex($img, $rgb);
& I6 o. W' v6 a" |2 U1 R: M - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 5 t+ a- I5 _# I5 T3 o/ e
-
' d! k% t1 A) V - $grayArray[] = $gray; , t$ W" A! G. S9 L3 i
- } , q& k# P4 [% v
- } % X* ~3 k' v% Y) T5 a& P
- imagedestroy($img);
: J0 ?1 R( N7 Z( u0 _( Y" v/ L. T: W' H -
V+ n a# t6 A( O4 p! J0 t - /*计算所有像素的灰阶平均值*/ + C T% w: _. r, {0 d$ `4 K1 C
- $average = array_sum($grayArray)/count($grayArray);
0 b' A, K0 {5 O0 I: e$ O4 Q. ~& U - " e* w7 u- N1 G3 U4 \
- /*计算 hash 值*/
( _& h& a' z" [. O9 a, T1 q - $hashStr = ''; 1 S4 @' R. A- E. Y4 U3 d* G
- foreach ($grayArray as $gray){
# D& X4 `4 B% @' [* N4 z4 E - $hashStr .= ($gray>=$average) ? '1' : '0'; ! Z; s* _- z+ g. W- @
- }
7 T2 t5 x3 h/ ]* [5 `: m - 4 W2 h: W- n6 f
- return $hashStr;
/ A6 C' q/ z' q; X - }
6 I8 S4 B' R/ Z- t8 c ^ -
0 ^' M- f. f, d- ^2 ] - 9 ^2 }. X4 ?2 F; {' O& t
- /**hash 图片文件
2 m. L$ W2 q5 b9 v6 `" P' K x( r - * @param string $filePath 文件地址路径 ' b$ t% |1 `8 p" Q- I! L* V! E
- * @return string 图片 hash 值,失败则是 false
3 }4 A+ c5 L) D7 g g& b% s- K& L - * */ & G2 [& a3 ]0 \: L8 l) d& `- B1 P
- public static function hashImageFile($filePath){ ' r9 C$ H8 y* F3 E! W
- $src = self::createImage($filePath); , R* D9 j* n9 U0 J- \4 u0 G" @
- $hashStr = self::hashImage($src);
5 i9 I& y. T5 U1 h - imagedestroy($src); ; V$ G8 \6 K* i$ m/ }. h
- * [( m' D+ ` R8 m
- return $hashStr;
9 o/ _2 t8 J c6 J3 N# F+ a* H - } : r; s& b7 ~3 I; p1 [# Z- A9 {0 g
-
% J. F- @- w9 }- a1 ?( S+ W: J -
# R; N$ C2 w6 B% I" o" y/ h - /**比较两个 hash 值,是不是相似 " r% `- J1 N" P# I
- * @param string $aHash A图片的 hash 值 2 H0 F: n) x: l* |) r9 n
- * @param string $bHash B图片的 hash 值
8 Q/ r/ n# q1 j0 _6 l d4 O2 e - * @return bool 当图片相似则传递 true,否则是 false
; f5 U' C# S6 M' d9 G - * */
( t+ c" U5 v( o8 i$ G - public static function isHashSimilar($aHash, $bHash){
' d% y$ q; I0 l# G, E - $aL = strlen($aHash); $bL = strlen($bHash); / K2 L3 C4 `3 X6 a9 b9 u
- if ($aL !== $bL){ return false; } + o$ A/ g6 n+ p% X
- 8 W! f, Z( P+ o" Z1 a v
- /*计算容许落差的数量*/ ; V3 i+ X+ m5 U8 J' O5 j
- $allowGap = $aL*(100-self::$similarity)/100; ( X7 Y0 M# p2 {+ w) D- {
- 9 B: J7 i7 _: N$ n" ?$ G$ t
- /*计算两个 hash 值的汉明距离*/ * {9 W+ W$ N8 B7 \0 G y7 x! B
- $distance = 0; 8 h: P: M: S( F' q6 \2 Y
- for($i=0; $i<$aL; $i++){ ' u% q: |! \( a$ f
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
* Q: \& c. i! h! E7 f( Q# p - } 3 F! e3 A6 k' w9 M# x
- % C! O# m. g% Q8 o9 s
- return ($distance<=$allowGap) ? true : false;
d2 u2 v6 H+ s- y5 @2 f, N2 J& L - }
( @/ G C/ W2 c& k; n! V. V -
3 r7 h5 I2 _ o( r- d4 I - ; G3 u, t' I9 P; X" I' H
- /**比较两个图片文件,是不是相似 2 j1 F. ]. ]6 @! l( [
- * @param string $aHash A图片的路径
. S2 ?# t; |0 ]3 S+ {" { - * @param string $bHash B图片的路径 & P+ c9 p" B! L, d& r* w
- * @return bool 当图片相似则传递 true,否则是 false
3 M- o8 O7 k5 h; [+ Z1 ^$ i - * */ , V2 v- C7 R- \+ |! Q2 _& W
- public static function isImageFileSimilar($aPath, $bPath){ * ?$ ~# m- V4 N$ {) t9 [$ B
- $aHash = ImageHash::hashImageFile($aPath);
/ B+ l3 u- n* k$ m ?( g9 P - $bHash = ImageHash::hashImageFile($bPath);
5 `: y2 H, h) C" O1 N7 F1 k2 a. t - return ImageHash::isHashSimilar($aHash, $bHash); + l$ W( t" I! I; @" V! j$ s
- }
& }' D$ P" N% G+ _ - 1 x( n. f, L0 i1 s- t! W$ b+ ^1 A
- }
* U" C1 D+ U3 B7 H; r: Q: d$ S
复制代码
9 \3 j6 R# A6 R
: l( _! T$ _3 @; `$ x% M9 W |
|