管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图# O1 m6 R0 R0 L1 S. h1 T
' a5 g E5 Z: ~- y" K
. [( c# \) h/ ?9 g+ G
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
7 w- k7 O$ a* p" D9 ~/ \* B: u0 E: \- <?php
8 i: z+ k( K) _0 [ - /**
/ u+ Z/ u. [' H, O - * 图片相似度比较 : ~% Q. ?$ T( Z2 {
- *
2 B. }2 ?+ f1 m# ]) H( ?" N - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
0 P( a: L; R @6 [( q- M - * @author jax.hu 3 c. E) U5 N+ y3 J; J" s9 r
- *
: Z% U! A0 X* K4 h - * <code>
$ N W9 `! p' S5 h - * //Sample_1 6 K3 g2 I: v. m- s/ D
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
* y% ~6 a: l: k- j - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
$ o4 j% ~9 H! \( n7 Z - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
. @' w6 s6 N/ z' v - * " F4 \; S3 _6 m' b& h
- * //Sample_2
$ Q" c$ @/ B+ ^; B/ K: d - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ! {$ T! `( Y: V C7 h
- * </code> 0 @; R1 A+ X$ {* {: {2 @
- */
* A; t: ?% G8 s E k1 g" w - ; |' k9 A& I! T& A9 X* t
- class ImageHash { 4 n7 k2 `# s; _& I3 w
- ) o8 H* W, l- T" B [$ v& z
- /**取样倍率 1~10
% o" _4 c- ~8 e3 V - * @access public % G- J/ w, a4 u4 j+ k
- * @staticvar int & c; D! J/ ~0 @! Z+ R i
- * */
% i) _6 D& V) K - public static $rate = 2;
- a6 F$ h5 e& ?3 J6 Q% ~1 V0 H -
( V$ \- k4 [; @4 P% D; k1 P. a - /**相似度允许值 0~64
4 e$ ~ d. I# ]5 e+ c - * @access public $ u5 {- t7 S) N9 I6 u
- * @staticvar int
2 S2 m2 J6 M0 |# @ - * */
$ Q* R% u5 e' d; i) v) ? - public static $similarity = 80;
2 n, d! c; k; r% H% T1 Y - & _7 }9 L; @* s# H' n8 u2 H
- /**图片类型对应的开启函数
" X) E9 l4 o- q3 I; h) Q1 n9 b - * @access private - C/ H0 k' V0 S7 Y9 k0 Y
- * @staticvar string
& O- K( B! d- ^( x - * */
1 W8 q- z7 D( B( v) _9 b - private static $_createFunc = array(
. M: u+ |1 r6 z/ m0 O, ^ - IMAGETYPE_GIF =>'imageCreateFromGIF', , o6 I3 q" b$ l$ }" O2 N9 E
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
3 X/ N$ y2 s$ R- l4 N5 @. s$ R6 @ - IMAGETYPE_PNG =>'imageCreateFromPNG', ; l/ e# C0 ^3 @$ u
- IMAGETYPE_BMP =>'imageCreateFromBMP',
) o! q. S8 \# ?: U - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 0 q, Z% q# W5 k+ v
- IMAGETYPE_XBM =>'imageCreateFromXBM', 0 e6 E7 m6 T, h: [) D
- );
- x( X1 }% X" X Z: H7 K- q3 j8 p -
% }1 Z; q5 E4 c9 U" R! R1 e9 {+ D1 e4 q -
/ H2 q1 R: n/ Q% b1 ?" }; W2 }8 `* x - /**从文件建立图片
! U4 }# S0 I, k7 V+ N6 k - * @param string $filePath 文件地址路径
% ?0 M( B% G/ s8 @" P$ x - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
' v* a% L( O a n) G' E4 p, U - * */ ( s$ I* j* E3 \( @: \
- public static function createImage($filePath){
9 k7 _, r5 R6 F7 ^+ f! ^1 q2 X - if(!file_exists($filePath)){ return false; }
" T6 q! @# o4 m: W - + o2 Z$ ~, v5 q0 Z6 c: [8 ^1 ]
- /*判断文件类型是否可以开启*/ , n! C' r; ?" W1 k% \
- $type = exif_imagetype($filePath);
4 d6 P6 T/ b& ?: |# P) l6 | - if(!array_key_exists($type,self::$_createFunc)){ return false; }
- i" P# i) S6 b8 ?9 ]7 t4 e: W" ~: I7 n -
2 \( q) ^5 s4 |' a( X$ a+ ? - $func = self::$_createFunc[$type]; 4 i k* z& A8 C) ~
- if(!function_exists($func)){ return false; }
/ P% \& M% \+ L4 ] -
, s5 P+ B3 n2 z. h8 y. U - return $func($filePath);
5 [2 w' u8 _, u. t! w - } : U0 Y1 }' z- B& x! {4 l4 q
- . O5 S1 D {& o/ {
- 0 G: W. m7 V% S+ N2 ~) Y) z+ b
- /**hash 图片 ; B6 b" q( v! X# S& Z+ b7 R5 B" B
- * @param resource $src 图片 resource ID 3 z3 O v; t. J8 ~2 {, a
- * @return string 图片 hash 值,失败则是 false
4 o8 | J: _/ i' t B5 C - * */ : T" L( g3 y& `
- public static function hashImage($src){
' G* V! T! m2 Q# Z" e - if(!$src){ return false; } . M2 q0 i6 Q8 o( [ `
-
# \' [# O' y4 _9 }) ~ - /*缩小图片尺寸*/
/ u3 X& b. Z) A. r5 U; ?; ^2 K* n* ?9 |0 @ - $delta = 8 * self::$rate; : C6 O6 Q1 u8 R# y2 | ^8 W
- $img = imageCreateTrueColor($delta,$delta); + j! H& j9 [7 N
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
8 H4 |/ i8 Q# s0 w8 d3 b -
2 E) \+ d$ ]1 j$ `. |+ a( {, w1 x$ _ - /*计算图片灰阶值*/ 2 s- K' j5 v# o2 N5 Q
- $grayArray = array(); 9 S5 T y9 S. U
- for ($y=0; $y<$delta; $y++){ 7 t1 l# ^3 i: F2 s2 u5 O
- for ($x=0; $x<$delta; $x++){
6 f4 r/ _6 y- t& ~3 m! z - $rgb = imagecolorat($img,$x,$y); 1 H- o1 v6 l; k
- $col = imagecolorsforindex($img, $rgb); / a. ^' X1 O5 U/ m1 Y' X
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
7 U! [7 ^" `# K5 ] -
u% S# x% v' m - $grayArray[] = $gray; 8 X9 _/ l2 }$ g% P/ i/ Z
- } + `# k1 D# U/ [8 H
- } 7 e/ H; E9 f! ^) H
- imagedestroy($img);
* o2 j; V7 \! O$ }, u -
- r6 |- d0 J: p) L' O - /*计算所有像素的灰阶平均值*/ % w* L% b2 X8 P& G5 E1 }2 y6 D
- $average = array_sum($grayArray)/count($grayArray);
) z4 A$ |4 M9 O/ u6 C, O -
5 J8 v+ d! B0 E' Q+ X7 Z - /*计算 hash 值*/ ! j" O' V3 m( J- j \, S
- $hashStr = '';
2 ^# w% N; S! a( V" k7 n - foreach ($grayArray as $gray){
( H+ T6 m. k( { - $hashStr .= ($gray>=$average) ? '1' : '0'; 2 Q3 T: [. t" d) e7 V
- }
$ g) s3 ?/ m. x - 6 f8 G* I( p8 G5 A+ F& b( W
- return $hashStr;
# T0 u* T5 o: x - }
- {; s3 y d# F4 e" s! i -
) `# D7 H5 K5 K- ? -
2 ]% G& B) [4 J: A - /**hash 图片文件 # x; q. R+ l6 x5 W9 Z2 W
- * @param string $filePath 文件地址路径
' i1 \) A% N1 T% t - * @return string 图片 hash 值,失败则是 false $ O1 m- G) I% x0 F; v. K
- * */
H7 ~3 V( O1 p# f - public static function hashImageFile($filePath){
8 V- e: [/ `; {6 b+ S* ]8 o; t - $src = self::createImage($filePath); - e5 I4 L3 G, g& c/ a; d
- $hashStr = self::hashImage($src); 5 X% E6 l# q l9 t
- imagedestroy($src);
/ e. |1 E: N o4 V) e& J9 [ - 2 u! a5 F5 U: Z1 ]/ N
- return $hashStr; $ t0 C0 P+ k; F. N6 z4 e. G
- } $ {! U; X8 p7 n! t# O! @* U7 j
- # U6 N& K& L5 w6 P* i9 V' Y
- 9 w3 A5 B0 b7 Y- [ ?7 _
- /**比较两个 hash 值,是不是相似
+ b* A- I4 y- `4 t v5 L - * @param string $aHash A图片的 hash 值 : \- O) `% c+ F6 U j) k6 s
- * @param string $bHash B图片的 hash 值 1 Y- y [; P8 V$ H9 z; ~
- * @return bool 当图片相似则传递 true,否则是 false
0 L2 ~0 E8 n) ^ - * */ % ^* \. H' I, V% ~* r! [' U( G
- public static function isHashSimilar($aHash, $bHash){ 4 O) N" ?! ~) Y c" c
- $aL = strlen($aHash); $bL = strlen($bHash); 1 S$ u1 _- M1 [. u* G) G9 k
- if ($aL !== $bL){ return false; } 5 A; N4 M# ?+ O. @
-
7 Q6 a i* b; D- c5 h1 A1 M4 X - /*计算容许落差的数量*/
9 f2 r( g0 k/ g4 e" ?1 R - $allowGap = $aL*(100-self::$similarity)/100;
- E; r& R& W! Q2 n9 b -
( c" X7 L9 r, v, K! E% c - /*计算两个 hash 值的汉明距离*/
# t4 l, g& c2 Z - $distance = 0;
: Y, O# }" i- K$ q% G/ o5 D1 M& s: b - for($i=0; $i<$aL; $i++){
2 v8 o& Q5 F' A8 E& _! {- u - if ($aHash{$i} !== $bHash{$i}){ $distance++; } , F" H! E6 k6 g* i7 X
- } . ?) o+ a- R$ T9 X
-
- d8 t4 U& i+ ~0 g; j% y1 r5 P - return ($distance<=$allowGap) ? true : false; 8 e; C/ ], F* J, h/ `+ ]
- } 4 Q1 l6 x: v; H0 p% X. R0 }5 \: r' d' F
-
# A2 J) o& B+ V5 y: u -
' B* m( C7 p* k" X$ R) _/ S - /**比较两个图片文件,是不是相似 8 } h- L" q# w1 R
- * @param string $aHash A图片的路径
! V& ]) N: g! u2 I - * @param string $bHash B图片的路径 3 l$ {+ V" W; H; h
- * @return bool 当图片相似则传递 true,否则是 false : b9 M# h4 L9 o
- * */ 4 b! L1 ^" z% d6 B2 C# E
- public static function isImageFileSimilar($aPath, $bPath){ , j5 [$ ?, y% K- G4 W- Z7 I5 S
- $aHash = ImageHash::hashImageFile($aPath); # K* d+ v$ w# Q- s9 O
- $bHash = ImageHash::hashImageFile($bPath);
- @5 G' \# g }9 e - return ImageHash::isHashSimilar($aHash, $bHash);
% m; q# b( k8 B0 I2 T h: O - }
& V* ^. U% I* Z, T1 g0 F# B3 j - % @ P$ n8 M$ u4 X
- }2 }' b" k& C( d3 ^0 U( X2 n% s
复制代码
. a, p& Z8 B1 [
: \6 J; Y6 P2 ` |
|