管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
) D, m* u' {8 C7 o5 Q: a" \0 P( z5 H% i8 x: J
, f7 Q+ e5 E1 f; Q2 @
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
/ u" t8 x$ X% J* ~9 w- <?php 6 ^9 b, w8 l4 X2 ^( g) L) V& b" C
- /**
% z9 E4 O) \0 b( u - * 图片相似度比较 $ L% o, T+ ~6 X% z. M1 m
- * / P1 _ ^8 {9 g5 j( P& ? s
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
( X1 u2 F6 \1 k5 P; F% [ t* { - * @author jax.hu + N+ M& Z i+ @3 u* S3 J) g& ^
- *
) Y7 p! c* f1 \- k - * <code>
) n p3 ]! W( ^9 o% ^ - * //Sample_1 0 }& E1 b8 Y" p1 _- \ b
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
& C0 y5 B! H" W* \+ D' d - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ; `5 }/ B1 @7 l
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 7 f4 U% x0 I8 V" ^7 c0 k% w) i
- *
, F. ]- P* _3 z+ h4 o' x - * //Sample_2
t4 v# l2 \1 ? e+ s2 O* w - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); - o* S: D6 K5 L
- * </code>
0 o+ n3 j$ r8 {5 \ - */
- M ]: {1 q( s" ~ -
% B& B) Y; [/ y' z8 G - class ImageHash { / i5 v! W3 ~1 R5 K! ?- ?9 ~- o
-
3 S* ?$ W0 p/ K+ ~5 m6 z4 \4 W/ @ - /**取样倍率 1~10 % b. V# y8 l8 @
- * @access public : ~2 B& b; \8 ]: q6 w! Q+ Y6 i
- * @staticvar int
6 K* h, O/ R# c$ m2 o/ @ - * */
% a/ B" A: r+ ?' A, K# J7 M0 d - public static $rate = 2; . I$ ]- t1 ]" `* h0 P! K
- . ]6 m3 Q+ ^9 e) t! X' ]
- /**相似度允许值 0~64 % D* j/ G/ v, c N" z( M" @
- * @access public # V1 @! b" j& N6 R: w
- * @staticvar int
5 o/ k. @9 z# U - * */ # R0 O$ l4 a% @& l* A) D0 L
- public static $similarity = 80;
3 B; H9 C/ {8 |9 w -
" N0 G1 t0 n, n" S2 P. f# q) c - /**图片类型对应的开启函数 0 y8 t; M, U$ P: |( Q3 q4 M
- * @access private
* r I( D5 e- A/ q( { - * @staticvar string
- a: z) W$ y9 g" h# _/ ~ - * */
2 G) F& j: S/ e( q; Q6 k - private static $_createFunc = array( ; Z' a. G4 D) `$ `5 m5 a4 _% w+ {
- IMAGETYPE_GIF =>'imageCreateFromGIF',
: M3 I" e& I# b% V - IMAGETYPE_JPEG =>'imageCreateFromJPEG', ) ^% S. [6 R+ {8 g
- IMAGETYPE_PNG =>'imageCreateFromPNG', 1 E- J- b* w: C. u. l
- IMAGETYPE_BMP =>'imageCreateFromBMP',
. \, ^7 k' o7 A0 P. ^5 z - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
+ U& g! @/ q. u! z0 L8 X: } - IMAGETYPE_XBM =>'imageCreateFromXBM', 3 ~% w/ F6 i4 l5 i! E3 }
- );
6 i) @6 E+ F( K, s& m e1 t - 8 H+ H* v6 `( X* ^+ r
- / a. g+ d1 N6 y: f; \7 p& m" M2 z
- /**从文件建立图片
7 @2 b( q6 H9 g) `, ^2 {5 B - * @param string $filePath 文件地址路径
" _, L b, |& e; A3 a8 U& O - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ^3 v! Q m, d( Q
- * */
( J5 I, R/ c1 f- Z3 K' e - public static function createImage($filePath){ * U* F- \& s' R
- if(!file_exists($filePath)){ return false; }
* \* s, h& ~. z" t2 T# l - 9 p* W1 z1 U" c" K% k l
- /*判断文件类型是否可以开启*/ * C5 r7 o+ }+ t! N
- $type = exif_imagetype($filePath);
3 b7 ], z7 k& E9 L. L5 ^: \ - if(!array_key_exists($type,self::$_createFunc)){ return false; }
T, N7 b O( P* F -
- I& B; H p9 Q) g - $func = self::$_createFunc[$type]; 9 Z5 j$ c4 B: X9 O' I+ L
- if(!function_exists($func)){ return false; } 0 x0 B \; G' w0 w a
-
! h9 O8 v) `8 p8 U7 a& Y; X/ v3 |1 n - return $func($filePath);
e% K3 [$ f7 x" T" e% v1 z( x# z - } ( R2 m L8 G) }# G
-
8 z0 p2 g ]4 W. C4 \6 B - " S% [4 v2 @% @; i) ?9 f) F4 M
- /**hash 图片 3 e% ~! P8 ^ w- b
- * @param resource $src 图片 resource ID 0 ^1 G% W% J0 Y8 C, t$ f& p
- * @return string 图片 hash 值,失败则是 false
' \/ z! R! Q, b; `# ^ - * */
8 T1 e: N7 b2 o; \/ |/ ~ - public static function hashImage($src){
) W2 I% y# R a0 p" B4 o5 b - if(!$src){ return false; } + }* z( P) m' l g: x
- " _$ _' H( g& X, O7 n" H3 ^
- /*缩小图片尺寸*/ # t$ F8 Q! P' U
- $delta = 8 * self::$rate;
: D2 r, ]4 z" W1 Z+ w - $img = imageCreateTrueColor($delta,$delta); Y' o3 \: \7 F' F6 {" }6 a3 [
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
* R2 z/ ]4 @- l+ \) N. v- g( b - * o8 r7 i$ e3 E0 T( s- w
- /*计算图片灰阶值*/ ! p9 h) v3 @. w# r( ~% s
- $grayArray = array();
2 u& U' X4 z o* z. }/ _9 l5 U - for ($y=0; $y<$delta; $y++){
5 V7 J2 Q- [$ F% C2 q3 P" w; d - for ($x=0; $x<$delta; $x++){
4 [+ i; n, G: R+ n7 } - $rgb = imagecolorat($img,$x,$y); ' q& ] I; v1 N* D
- $col = imagecolorsforindex($img, $rgb);
( X2 S7 T4 r+ ^+ J3 H5 ^ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
! v g8 F2 e4 [; t, R -
# q: x) j- z: G0 O - $grayArray[] = $gray; 9 m: r* w! t( f; h B5 T
- } 8 j4 v8 |, h6 I+ @& ?, [! \& `& k
- }
2 e! H h5 k1 s- t - imagedestroy($img); # [. D5 t9 h, U/ w. m c: M
-
$ z" P1 u* F! h- | - /*计算所有像素的灰阶平均值*/ " B* n6 K/ w( ?' X* C
- $average = array_sum($grayArray)/count($grayArray);
6 D5 m1 m' ]6 d R' W2 [ -
. U2 D4 F3 ~+ E - /*计算 hash 值*/
( t8 k* f, M4 Z4 f! J7 l - $hashStr = ''; . M3 K; S; w7 I" i! T' c! x
- foreach ($grayArray as $gray){ L5 d4 b0 Q* {5 A$ ?5 |! M' w
- $hashStr .= ($gray>=$average) ? '1' : '0'; : {8 G# \; m# F" ?& Y k( v
- }
, G4 W9 y4 l' U; ]* O! M" C4 G - # \0 K" f% U% Y1 L& S
- return $hashStr; 7 [& o, E2 h3 V$ \+ O! j0 A
- } k0 h6 ~& F O6 A
-
8 |$ \ t W: N: ^: @+ Z/ n - 2 V8 h# z1 Q& m4 n/ B1 V4 V. ^8 c
- /**hash 图片文件 % Q7 Y6 f+ |+ _. t
- * @param string $filePath 文件地址路径 + S3 ]& M, p* n
- * @return string 图片 hash 值,失败则是 false
! o1 K, I& j) I - * */ 0 t! F7 w9 h. t
- public static function hashImageFile($filePath){ ( b# }" E. j# s- T' t* Y9 B7 ]4 F
- $src = self::createImage($filePath); - a+ s3 a' ~) N4 e/ S' g
- $hashStr = self::hashImage($src);
5 Z8 U, b e" K9 V. g - imagedestroy($src);
4 t/ o. ^9 N/ c+ B$ j' M -
, ~+ Y% g/ j& J1 w: G' X5 G; n - return $hashStr; & N% g& _( z2 P0 G0 o
- }
6 V2 c+ v" T9 [) E" }- Q5 L - ' f% b2 z# g% Q, c
-
) T5 d4 L+ b8 o' Z - /**比较两个 hash 值,是不是相似 9 a) A. c* N$ l5 c- f" g
- * @param string $aHash A图片的 hash 值
. y- U. }, X1 Y6 w" L - * @param string $bHash B图片的 hash 值
, x% J# Y3 b! D8 e. f( \% P- S0 R - * @return bool 当图片相似则传递 true,否则是 false . N7 j1 u& o, p9 f1 l9 K+ c
- * */
7 o! E# f+ P5 B, l - public static function isHashSimilar($aHash, $bHash){
# r8 w: a3 ?6 M, G4 R9 e - $aL = strlen($aHash); $bL = strlen($bHash);
) D; ~" J. M% ^ - if ($aL !== $bL){ return false; }
% M7 ]& c' O8 ~5 |) Q - 1 c# L4 Q. }* q( a% N
- /*计算容许落差的数量*/ 1 V$ s7 _9 Q" D: i0 k/ ?
- $allowGap = $aL*(100-self::$similarity)/100;
. a' g2 e1 G# M) O f, Z -
3 R$ ^2 l4 S) l& i/ D - /*计算两个 hash 值的汉明距离*/
- `1 j% A: v% ^0 B! d8 n# K+ v - $distance = 0;
8 N1 U( A' c1 X" ]5 J, @7 r - for($i=0; $i<$aL; $i++){ , W! c9 W, T. h( Q, ]4 q9 j! {
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } 3 m8 Y7 K$ J' l( J; u# N
- } 4 i1 ]+ u# ?1 n6 c ]: }
- + ]/ e/ r' r$ S5 b* z! s" E
- return ($distance<=$allowGap) ? true : false; 8 s$ h4 g- U; ]
- }
. g. S" Z" W* G# q - 2 t6 G* `: T7 k: J. l+ p
-
4 W6 w+ Y2 A! B7 @5 o - /**比较两个图片文件,是不是相似
) g* Y/ W0 |% r0 ~ - * @param string $aHash A图片的路径 : ^9 M0 Y J3 L0 G% \
- * @param string $bHash B图片的路径 8 s0 z A$ W5 a1 V
- * @return bool 当图片相似则传递 true,否则是 false
: B7 B- h, m# r9 f8 t - * */ 7 j- u7 Z4 w) @4 d. z5 O( q: D
- public static function isImageFileSimilar($aPath, $bPath){
8 |! U# ?. E' S6 L% Z9 D - $aHash = ImageHash::hashImageFile($aPath); . }$ E/ Z( @; e2 P- T' J0 ?' E
- $bHash = ImageHash::hashImageFile($bPath); q6 p3 m- B: ~
- return ImageHash::isHashSimilar($aHash, $bHash);
# G Z& B5 Y4 P4 ^+ {! s - }
" L8 P/ x; ^% f( w7 g9 a -
: a. l3 l$ _0 Y2 R: Y' U+ R - }
j! @; Q" E" j
复制代码 : l* r4 Y* y0 \& S I# I+ g3 ^: Z( I
$ Q/ _1 Y, h8 p/ Q5 u1 C- w& k6 ?
|
|