管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图9 A6 ]- A0 U1 p- @# D) P
1 ~1 ~ [6 k! E2 n {3 q2 C5 ^) h& J% j# ^0 T
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
! v, v4 a' ^/ n9 a) M- <?php 4 F7 X, Y7 Z+ d
- /**
% y+ Q ~* x, [# y `% k+ ^3 k( y - * 图片相似度比较 + s; _5 P& ~8 a8 g" K6 g6 y
- * 9 D. ^0 O0 R) k8 E I
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; & ^$ g! E, F+ Z/ T/ M9 N/ \) O* p6 ^
- * @author jax.hu : u8 }( B" B% t* s3 n2 x! K
- *
. B' [ }8 N! u4 w2 ~6 Z - * <code> . N3 p) G# }+ ^5 v
- * //Sample_1 0 O5 j& B) ?; d1 V
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
3 C$ M/ g. k% U) n7 P - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ) B6 s3 ^4 V+ D: H+ Q0 ~
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
8 Q5 ^7 D# t2 ^ - *
$ u1 I5 _1 Z& V& d b0 D) C0 u0 h' U - * //Sample_2
8 C" ~$ O2 _. A: n$ w - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ( |/ U( P+ ~. t# c; _7 c1 ~
- * </code> 7 }0 i* W: Q/ W1 f& x# w! a4 d
- */ ) m8 ?/ w/ z2 J
-
1 b' v# ?. I3 r$ o - class ImageHash { ( y/ X$ R, p) w& B r, r" b
- 1 F5 l- T5 K% b" e# z! \0 _; N
- /**取样倍率 1~10
" i0 b# M8 ?2 P# P; M- @ - * @access public ! F4 d0 a8 k- }7 E! b
- * @staticvar int
$ u7 ]: a( x# h9 i3 h - * */
- M( T% \8 L3 g9 G2 ^/ o: q - public static $rate = 2;
6 o7 {5 z6 N" W - 2 @! H% q& C0 d0 z: z+ F: B
- /**相似度允许值 0~64 7 R1 g6 J) e- o% Q# u5 }$ q: d# r
- * @access public 2 K. X' f( f' z/ u |/ k w& d
- * @staticvar int ) p; h# Q8 V% J) ?
- * */
+ B& W, x Z, g2 n5 i - public static $similarity = 80;
0 N$ D1 {1 G2 {6 l - ) U/ d6 G* K `7 W/ j
- /**图片类型对应的开启函数
# O3 N& [$ w. U' \ - * @access private
5 L; b3 d% e( l8 i9 J - * @staticvar string 0 `9 q4 G- r$ h' r' _1 L
- * */
* E4 l, @4 l0 Y# i& k& _' f' K% J, u - private static $_createFunc = array(
% b( D F' v$ |: j0 Z - IMAGETYPE_GIF =>'imageCreateFromGIF', 7 N8 Q7 b- ]& x) ?' x" P+ [: M8 s$ e' B
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
$ m# V/ t6 B- |. _" D) a - IMAGETYPE_PNG =>'imageCreateFromPNG',
' g1 |- u* e+ l - IMAGETYPE_BMP =>'imageCreateFromBMP', / y/ x) V# P- x! y: E" L+ |. Q
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 5 T X, e; v. ], ^: Q8 t
- IMAGETYPE_XBM =>'imageCreateFromXBM', 2 ?3 c6 k7 \; {) }. i7 |) A
- );
" G9 U) e1 e# u o" a2 b3 t( G0 i - 9 w# P# D* X! J# I6 w B
- 2 v7 a5 i6 R9 |% y
- /**从文件建立图片
9 l+ N/ o; Z. D0 p - * @param string $filePath 文件地址路径 % V+ r3 r& J; W
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
" j7 t8 e5 X1 a# @! s0 T+ @/ ? - * */
. {2 K& X# g* ], u( p n0 z3 l8 ~" g - public static function createImage($filePath){ & o( W( P# L5 V( w8 V# h0 v# y
- if(!file_exists($filePath)){ return false; } 6 R1 U$ g& N* @' _9 ^
- / J8 F" r9 s4 @( c* d" ~
- /*判断文件类型是否可以开启*/
8 a& o& \) q4 ?, X% i - $type = exif_imagetype($filePath);
5 J/ H. n! \) x. V$ H0 A - if(!array_key_exists($type,self::$_createFunc)){ return false; }
, {; d& b; ?7 M - 6 a" G7 |1 b; p; S- e% l) K% b) [
- $func = self::$_createFunc[$type]; ; T$ `+ d5 m: w! l
- if(!function_exists($func)){ return false; } # Y0 b4 W) ]$ ^ a, P2 C" L
- ( e2 D7 a' s7 _; ^6 o, @
- return $func($filePath);
( B: {4 t" ^! y6 h _: F' J9 m9 s - }
( L7 R7 }$ z9 @% I6 A( n- i -
4 E$ g$ w, V8 ?7 |0 Y -
2 `% K" o- U7 A4 \8 ^7 g! B - /**hash 图片
, { _% v/ p9 _/ N3 P, q - * @param resource $src 图片 resource ID
, M3 {0 A# N9 Q5 B - * @return string 图片 hash 值,失败则是 false ( z7 h/ t' c' N* Y: [9 y% A7 V, P6 f
- * */ " K' p( u. h0 l5 d
- public static function hashImage($src){
2 x9 {' p4 K" f. D1 Q - if(!$src){ return false; } 8 c7 q1 B+ \, H6 ^) Q
-
; b( Y2 y# E* x6 {( b( l- _ - /*缩小图片尺寸*/
- x" q P+ F8 ~2 { - $delta = 8 * self::$rate;
" ] }- k6 R5 U% h2 ]7 B* q4 c& J - $img = imageCreateTrueColor($delta,$delta); 1 t3 d3 [9 D* S, [5 m' a
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
; o) G C5 X" ` i* `+ l - 7 k4 x( p2 [, W6 U
- /*计算图片灰阶值*/ % f4 U# O+ u6 C2 d9 O6 A( v
- $grayArray = array();
; k0 E2 m' f2 H) z - for ($y=0; $y<$delta; $y++){ % B: Z3 t. x. E+ Q' ?- A' n9 ] h6 P
- for ($x=0; $x<$delta; $x++){
1 l5 A- z0 k$ y" W! z) [$ q$ u - $rgb = imagecolorat($img,$x,$y); ; X" o* k4 e6 U
- $col = imagecolorsforindex($img, $rgb); ! a- u* r6 G! \6 L
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 2 u- ?* E/ o7 x& d0 i
-
0 v) S2 k3 e3 b8 g+ q - $grayArray[] = $gray; + @4 d1 J! y& s
- } $ z S* o \" Z0 d" e
- }
# {/ B/ n1 R5 w. b% R4 y - imagedestroy($img); 7 P2 z( y& E' H3 a
-
! U! ?# I( {0 V* D4 ^( U% |! r - /*计算所有像素的灰阶平均值*/
/ g- M9 E* y) ~! y0 } - $average = array_sum($grayArray)/count($grayArray); + `: H$ {4 ?' n1 ^
-
2 {$ d0 b' `* b* h9 _% v - /*计算 hash 值*/
; b) }8 k& `: O# I: ^, G - $hashStr = ''; 8 W d) H/ ?/ O5 k, s ~4 i
- foreach ($grayArray as $gray){
4 m+ l; {/ L1 ^, a( G9 J+ `; B& @7 e - $hashStr .= ($gray>=$average) ? '1' : '0';
2 T4 E" x) j: J2 o! U - } ! l; |$ B# E" d9 D a) G+ B% U$ M
- 4 r" u. q- a# C& m n
- return $hashStr;
- Q/ [7 ^3 k: y0 `9 ]/ O; n2 w - }
) A/ t1 C/ `4 l n! e* g -
% w8 ~$ x" H7 E/ j -
9 c: {5 D3 D. E2 |. p8 E" H - /**hash 图片文件 0 x- G) Z# a$ q5 X: [5 x- z5 c
- * @param string $filePath 文件地址路径 2 G! X0 H! X! B3 z1 w- J
- * @return string 图片 hash 值,失败则是 false
1 R" B# g7 f4 E% j. P$ t) Z Y - * */
: U: _9 ~1 _) m/ p3 |8 l - public static function hashImageFile($filePath){
; B3 ~. I8 _1 D7 _6 h2 E - $src = self::createImage($filePath); + S# ~1 h" Y/ a: m) N
- $hashStr = self::hashImage($src);
! r& E# ~! K2 S7 x+ O% M8 p! _ - imagedestroy($src);
9 @) ? c9 ]( H* V - 9 I+ U8 t: e5 }; `! {
- return $hashStr;
P+ l x) m0 n+ S - }
- `4 l, m J7 [) W: G5 x" ~6 W; O -
0 A8 k' y! [& Z, q, \7 Z -
^' Q4 m8 \* ]2 K, @ [; x) q( P8 U - /**比较两个 hash 值,是不是相似 4 ?" C4 S; d1 S. I6 b( e
- * @param string $aHash A图片的 hash 值
0 r4 a2 |/ Y8 k. j# p w1 ` - * @param string $bHash B图片的 hash 值
$ B# y! u- s, @* \ - * @return bool 当图片相似则传递 true,否则是 false 8 B P. Q1 z. R8 H- i0 ]1 S" M, W. }
- * */
5 ?2 [1 U, v9 y2 o5 S. d9 ]& R - public static function isHashSimilar($aHash, $bHash){
6 d' c' Y. P2 C - $aL = strlen($aHash); $bL = strlen($bHash); . j4 D# { ^ ?4 |/ v( H
- if ($aL !== $bL){ return false; }
( w/ G T9 u- F. C) U0 G3 v - , F% }, V3 S. p. F& f/ G
- /*计算容许落差的数量*/
9 W( W. q' J/ c2 s7 X8 @. u0 G - $allowGap = $aL*(100-self::$similarity)/100; 2 ?4 w4 e7 s7 l8 q3 o! s. X. t
-
9 U8 \' b: ?# L/ C# y# s - /*计算两个 hash 值的汉明距离*/
1 Q$ G5 `4 U' k& j' M5 N( H - $distance = 0;
% I4 K- k3 M2 t' e$ i/ G - for($i=0; $i<$aL; $i++){
9 b" i( W1 E. _& \& N6 D, q- j+ p$ N - if ($aHash{$i} !== $bHash{$i}){ $distance++; } 0 g6 P* S* s. V+ D
- } ) b+ N2 s% ]5 g U; J; m. I6 l
- ' E8 H# [+ q- L" ~/ f1 D; r
- return ($distance<=$allowGap) ? true : false; ) e& ]/ z4 ~+ @" D+ c
- } / d# a" f& |' C1 ?- Y8 S; k0 C
- ; P6 Y* p: b8 e& P
-
1 y: O+ Y6 G- |1 q% R" e - /**比较两个图片文件,是不是相似 $ j0 Q( r0 G( u+ d
- * @param string $aHash A图片的路径
* T* d# e, r* Q4 |( O# D - * @param string $bHash B图片的路径 1 X5 X4 G2 l0 u6 I: K; O
- * @return bool 当图片相似则传递 true,否则是 false
4 B0 y& L# K( z4 i0 h! z$ L - * */
5 f* Y: e3 X; k - public static function isImageFileSimilar($aPath, $bPath){
; {2 \0 g c2 j: Z - $aHash = ImageHash::hashImageFile($aPath); & z5 F) h- _0 Z; M
- $bHash = ImageHash::hashImageFile($bPath); 2 @# y4 {2 A: q( |
- return ImageHash::isHashSimilar($aHash, $bHash); ' G; x% Y% ?& ` Q1 [& H) A1 _% w
- } ; I* e$ u* G. ^
- ' |5 i% l. J& p
- }8 M) `- i. [+ K2 Q: L' q+ h& ^
复制代码
. `& e: j4 p* ^% \6 |0 ?5 R$ q8 C. m
|
|