管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图/ o! v0 u* ` O1 O/ s
8 Q7 Q3 j& h/ N D' j; ]
7 h' V7 b8 t" ] G( g由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
: k+ x8 \. i8 ^$ h- <?php
: X" b G" G2 S) [" j( r Y - /** 4 S, w2 V9 b. Y; g) B1 g8 X
- * 图片相似度比较 ( _/ L+ w3 `5 f2 C% R9 M5 X
- *
3 r( o3 p( c' M0 f5 C - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
' u- l4 r1 Y/ z - * @author jax.hu
7 ^! l7 t* G2 Z5 _" t - * ) |- Y' k* |+ ^% R
- * <code> h: i" p% k' X9 R
- * //Sample_1
' J& _& O, l, ]. K; T8 B - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
0 B* G# F; J8 J0 p b- {( k - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
, O" H; Z, t/ V# e0 b" e7 V - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 3 K! C u k* L* u/ l
- * + d# m5 H# ]- o3 u( M9 q
- * //Sample_2
: ^- s- G) s1 \ - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
2 o9 M& q* P7 |6 M - * </code> 2 V+ w3 a* f$ t& ~
- */
7 K: J8 W& v5 n6 g# o5 d6 G1 m -
; b- `" G J7 Y3 ]- C* I* h, M- X - class ImageHash { % ^9 _% m+ v* ~
-
4 w: j, C1 J4 I1 y8 U: p - /**取样倍率 1~10 ; J9 y$ A3 ^* K8 Y
- * @access public $ l9 \5 l% ?) H4 r% i
- * @staticvar int
& L& c2 S9 R+ g5 j$ \, o- v - * */ / B! O9 g) E( k6 X( N1 X3 a
- public static $rate = 2;
- P5 _4 G6 Z/ V* w9 H( R - . |) m$ [# A0 z2 |
- /**相似度允许值 0~64 4 U4 w; D+ y2 I7 O3 R7 T- G; O
- * @access public
/ A7 P" X9 O1 O6 n ~3 z - * @staticvar int 5 ?. ?4 y+ w1 Y. k. H
- * */
: H% a, G* Y& n - public static $similarity = 80; / T4 X$ `) N3 I( `# K! M
- , [+ ~" W+ H* ^3 N- H
- /**图片类型对应的开启函数 7 j4 @0 b4 V4 }# W6 j9 x
- * @access private % K" U( `& G$ J
- * @staticvar string & f1 i# E& h) a# @. p
- * */
* W, @1 l: ?9 Q% X - private static $_createFunc = array( + p$ Z5 _/ J$ O- q: V5 } X
- IMAGETYPE_GIF =>'imageCreateFromGIF',
5 U! }; P* w6 a - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
. h$ U, ~- e" ~8 f - IMAGETYPE_PNG =>'imageCreateFromPNG',
0 b% P" Y+ y6 W - IMAGETYPE_BMP =>'imageCreateFromBMP', / k& k( a& I9 v$ J1 V. L
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', ! q2 j/ ]6 P& i* e* t: s9 V/ ^
- IMAGETYPE_XBM =>'imageCreateFromXBM', . J- w8 F& a, U; s* }$ U+ n
- );
) e( O& J. z0 C# D' Y3 Y) ] -
% c2 D4 E# y% s8 G R - 3 c' n+ y) |' G* f+ e& o, M
- /**从文件建立图片 : {1 z7 {; n) m F
- * @param string $filePath 文件地址路径 ' ?9 d1 y+ s- U' u( z" E! s) q( X
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
( `/ |5 D# }5 L t G; Z5 _( V - * */ * Z5 [5 Q- s9 M
- public static function createImage($filePath){
" @3 H4 d3 c- r2 A - if(!file_exists($filePath)){ return false; } ) c, C8 s+ Q$ q6 N" | `
- 1 \0 L% F0 N$ y. j; \* o# w
- /*判断文件类型是否可以开启*/
' |, }( |; j* U4 P5 A5 f - $type = exif_imagetype($filePath); + A* ^: Q6 x R% u. ^6 I$ U4 D+ z
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ( R$ }/ e" e$ o
- 0 r4 \1 x" `/ {: O
- $func = self::$_createFunc[$type];
& @% M- E$ Z5 S0 N( [* Z0 i - if(!function_exists($func)){ return false; }
5 j! i8 I5 R+ u! B$ `+ y -
* m0 u6 D( B7 Q8 f7 r/ O( c9 M - return $func($filePath); . h, D$ i; D5 H9 s9 s) a7 z9 _0 [
- } & M: ]7 Z: \: `' f+ i" y
-
0 S/ q8 i3 p) } - M- q1 J. r, }$ M, r
- /**hash 图片
1 T, T, ?, K" Z$ T9 T) U6 s7 @ - * @param resource $src 图片 resource ID
& Z/ O. s1 G& f& @: Q S - * @return string 图片 hash 值,失败则是 false ; a, z( r5 d* C; I! ]! S4 @
- * */
1 I) J: M% e1 z5 z) S1 W - public static function hashImage($src){ 3 ^& {2 Q& J2 W+ Y) ]
- if(!$src){ return false; }
# e8 {% c1 ~" v! J -
0 m: [$ x+ i3 r, u. X! ]* ] - /*缩小图片尺寸*/
( V. y0 ~* y* Y+ L$ j+ Q8 ? H - $delta = 8 * self::$rate; 6 j+ @- ]6 M# N, l
- $img = imageCreateTrueColor($delta,$delta);
9 n0 p6 n, x3 g* O, |$ w/ W - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
8 Q9 U) r. i4 e# o. `7 f - - \2 s- n2 Q* v
- /*计算图片灰阶值*/ 8 g9 j+ k* ~9 f( F5 K
- $grayArray = array(); - \& M2 g3 v* h/ N4 D, a$ U
- for ($y=0; $y<$delta; $y++){
/ D9 z( }# R6 M8 c& i* H5 C - for ($x=0; $x<$delta; $x++){
, z/ @# _: P* q - $rgb = imagecolorat($img,$x,$y); 4 \5 p) Q: I5 y, ?; y8 _# E
- $col = imagecolorsforindex($img, $rgb);
/ N W: ?, E# y, | }: `, \" g - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 5 e. D2 v0 g5 b5 o3 S+ v" x
-
' T. R% O( B! O7 k$ e: b) w" c - $grayArray[] = $gray;
8 o+ H7 h+ _* X1 c8 C7 ^ - }
% }/ C! X: L8 ]" X4 r; X - } # y% J7 M9 o% d4 d B1 G! B
- imagedestroy($img); 4 v: I) }7 ^& \6 m
- 6 v: T- H. g4 g& l
- /*计算所有像素的灰阶平均值*/ 5 T6 d( K. J0 R
- $average = array_sum($grayArray)/count($grayArray); & t( y5 @: d; t9 o8 ~4 J. q2 A
- 4 Q6 _5 o! q8 ^! i' P
- /*计算 hash 值*/ 4 u t8 y6 @' v! W: ?# [
- $hashStr = '';
2 J% Q" F) k! J# A' N$ D% U - foreach ($grayArray as $gray){ & Y" J: ?/ z& u% k2 g, C& Z- Q& u7 b f
- $hashStr .= ($gray>=$average) ? '1' : '0';
+ g+ r+ n( v( k1 t: S: U8 B - } : z% P6 ]( ~1 T$ k( [
- & W/ B, w4 L) C ^; k5 R
- return $hashStr;
( J3 j; r7 s0 c' Z - } j. h* J3 u! e# Z
-
' V( a- s3 m( w. I9 H! Z) g( i -
|! b% A# W" W( D - /**hash 图片文件
4 h X F( |) X s0 p# r: Q - * @param string $filePath 文件地址路径
3 V5 q4 F' e; Q1 O - * @return string 图片 hash 值,失败则是 false
- G4 L1 b u( v; g% m - * */
) ?+ h/ W: B& u5 b: D+ X7 ? - public static function hashImageFile($filePath){
+ A" J5 I: b( p/ y. C3 p - $src = self::createImage($filePath); # w" V1 |" \( p0 O' c( p3 m7 @5 O
- $hashStr = self::hashImage($src);
" ?1 o2 C0 k# f2 j! R - imagedestroy($src);
" r% E; ~( k* ^. @ -
) @1 \0 ?' W* j - return $hashStr;
& m8 b/ F1 v: ^ - }
2 P; d9 v& k" X8 j. W - & ` K# M( N# [5 P
-
; X! e* J, r& h5 {' { - /**比较两个 hash 值,是不是相似
4 ~2 v7 }0 v7 X' x - * @param string $aHash A图片的 hash 值 5 }1 \7 d) ~2 M( r% Q
- * @param string $bHash B图片的 hash 值 ) N; P7 w1 d& Q" C: [. X$ n4 s4 c) H2 e
- * @return bool 当图片相似则传递 true,否则是 false
( S' S' m. Z' B x9 g( R4 {6 ` - * */
# t) O$ ]; F; x, C - public static function isHashSimilar($aHash, $bHash){
; `! L D3 Z! v5 G% h - $aL = strlen($aHash); $bL = strlen($bHash); 2 D) o/ k5 {4 y t! a) l8 b1 Y
- if ($aL !== $bL){ return false; }
! B/ M$ f/ }/ w - ' b( p# _+ a4 x* R4 T# p! e
- /*计算容许落差的数量*/
8 q0 ?1 ?& \; Q1 @3 p - $allowGap = $aL*(100-self::$similarity)/100;
2 E; B5 N: R- d. g5 k -
- Z& N1 f7 |9 X2 f( K# ?3 v& Q; _1 p - /*计算两个 hash 值的汉明距离*/ + @8 e0 }3 {- R+ Z* ~! ?
- $distance = 0;
3 A5 U* N* y. L6 @' `8 T+ J - for($i=0; $i<$aL; $i++){ 1 a* a% ?/ ~3 S5 k
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } 3 f1 ]) ~* Z0 H0 a
- } 8 h- K. ]# h. J& I [% l$ W
-
7 P+ Z6 p. q X1 D2 l - return ($distance<=$allowGap) ? true : false;
+ F% {: b: Z9 G# [' z% j - } 0 o9 {; w3 }- n) q. g# ~
- * E! U% Z' f j5 T+ Y* G1 H+ W
-
+ t% H% K- y, W; {' z( T" r - /**比较两个图片文件,是不是相似 1 f, u4 e* e+ Z: J2 V0 R
- * @param string $aHash A图片的路径
6 e6 f& E3 v- c3 T' Q - * @param string $bHash B图片的路径
+ Z; i" b, T2 b - * @return bool 当图片相似则传递 true,否则是 false
' _% d+ [6 }! l3 f } - * */ $ B7 K& D& F" G% R4 ^3 A
- public static function isImageFileSimilar($aPath, $bPath){
0 E Z& j/ }! e* _; M - $aHash = ImageHash::hashImageFile($aPath);
/ R n! D# G1 _; ~6 B) j( p& Z8 R - $bHash = ImageHash::hashImageFile($bPath); ' ?# {: ^7 u0 n
- return ImageHash::isHashSimilar($aHash, $bHash); + Y0 Y' Y4 u3 ]6 O# J
- } / k, s7 l: R/ q$ C8 ?4 \9 K
-
# l1 w0 c7 `& x8 Y6 {: Z$ a - }
. ^! X& w: s$ c8 [1 p) F
复制代码 % Q: Z: o2 R6 T5 Q
! s# }8 r# f K$ D5 x# _' A+ Y
|
|