管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
# d4 s# n* [2 C9 l( ]. w; B5 p& p M; F& A, R. i- @+ z
" ]9 j U" ^5 \1 T9 l& O; C! s
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。" D% l- d+ \; N% w
- <?php # v7 u/ f! U5 O/ I/ q7 A5 [1 n
- /** 2 g: l9 U( v: T+ B7 S
- * 图片相似度比较
" @% l% [6 l+ v( D3 ^. i5 { - *
0 H8 z0 I8 n3 w7 n' X' F+ ^) P - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ) ~( M* Z9 E. R' N4 C
- * @author jax.hu
3 Z& a) ^4 Z& ]* t' E6 ]3 b - *
0 ~* G1 i' r7 @( x8 H+ t - * <code> 9 y, B6 X6 y/ l
- * //Sample_1 % I3 M$ a4 T2 n8 j9 }
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
' t* E* w5 g, Z6 Y" j - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
. e% S% x( r0 c' y7 Q# ? - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
N& o% W8 N# c0 e - *
7 I% ^! b' \. U+ q - * //Sample_2 + q" t6 s5 C2 N; i
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
/ x; j$ a& k2 D - * </code> 8 E& z8 J9 H& e* c& h. T8 E$ I1 G
- */ , f# u0 }9 V! Z+ x7 c3 e: n$ v) s
-
3 {+ k- x; P R+ F+ k - class ImageHash { 7 Y; Y6 T0 h- E8 M6 ^ r' Q- x
-
8 e% n! ?% H4 g, I) ^! k" V - /**取样倍率 1~10
1 t5 r3 H& A* |& o! x' w# F r% \# f - * @access public $ l# G: J4 c6 e4 U S$ _$ \
- * @staticvar int
9 O6 ~" w! r4 l6 h5 H - * */
4 j* q) O; A* ]( p4 Z# m: c - public static $rate = 2; ; _* v/ K! E% }9 u# k: Z. g5 w
- ! z) N8 m; s( `8 s# Q4 v
- /**相似度允许值 0~64
5 L; u4 K. U; J9 x+ a6 w6 S - * @access public
1 M( U0 ^5 x; z! o0 ? - * @staticvar int ( i" D( R0 o9 O- m" V, a/ T
- * */ 9 b1 p$ @! b9 Z: I' g
- public static $similarity = 80;
1 B& L7 ?9 T& \5 x/ s$ N -
3 u4 [& K( c6 L* x8 P+ P, ]2 A - /**图片类型对应的开启函数 / A* |9 d- Z+ W+ m* P$ \ f4 M
- * @access private $ s% f) E+ A5 l( I$ G
- * @staticvar string 5 b- J) P# G2 O
- * */ 8 M: o% d+ K; g: @ \: \( L& s- ~
- private static $_createFunc = array( ~3 j: ~* d+ J: b
- IMAGETYPE_GIF =>'imageCreateFromGIF', 6 Q# o+ p7 M S9 O" {
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
# Q* b7 m- r. v1 Q$ e" k - IMAGETYPE_PNG =>'imageCreateFromPNG', ( x8 q1 K0 ]4 V. t7 {
- IMAGETYPE_BMP =>'imageCreateFromBMP', # m, D7 ?# B- l) B2 b1 [% H
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
- k& E7 o; v, m7 P$ X$ \- M' T - IMAGETYPE_XBM =>'imageCreateFromXBM', 4 W) Q, e2 Q: b, S3 y# {
- ); $ V/ x6 B1 m' {/ V2 [2 g% |
-
$ Z: Z* n! E% u& s. x! T -
0 u9 i d8 w7 F4 Y+ l- B - /**从文件建立图片
3 Q& G2 C7 Y$ V, q5 S, w1 R1 q/ t - * @param string $filePath 文件地址路径 % k& [) ~' U8 p& n& V1 d* ?) `
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false " H3 w8 z! J* D9 I* z7 F4 X
- * */
3 Q" ~; Z' g) f' M3 l - public static function createImage($filePath){ 0 y6 M c, y' x' U& K' r0 b
- if(!file_exists($filePath)){ return false; } # S% i4 R/ F4 ]+ ~; ^( x
- , ?$ |4 _+ I: A4 e
- /*判断文件类型是否可以开启*/
" A* ?8 O+ @& G2 F# E - $type = exif_imagetype($filePath);
/ v! [1 W: F. a3 ~ - if(!array_key_exists($type,self::$_createFunc)){ return false; } & Y4 ]. p' I8 N7 x h) H
- ' {( } }1 c* e
- $func = self::$_createFunc[$type];
* B4 P$ I, Z- h8 g2 L0 q - if(!function_exists($func)){ return false; } 6 f/ U2 J8 f4 t8 x5 ^# O
-
# K! {, t% O, N0 W - return $func($filePath);
W! l" Q+ }* M" T0 W+ W$ n - } . P, i- n! V$ {( F( o/ x/ P
- - R" G2 s8 e6 Z5 h2 C5 B( [" B/ Q
-
) m' U) A4 n& ]5 W; l - /**hash 图片 # A/ r( Y3 ?% n
- * @param resource $src 图片 resource ID
0 y D7 ~/ L) F: I - * @return string 图片 hash 值,失败则是 false - [& U6 n# N8 _- ]# Y, n' ~
- * */ 8 i8 o" \" F' B& {7 H; l% `4 g
- public static function hashImage($src){ $ M) d: H5 D- b* h) \
- if(!$src){ return false; }
+ p" e- Q. l. j; q -
; P) r9 m9 ]8 q+ N$ S' `+ O. W! p - /*缩小图片尺寸*/ 2 }9 k0 p5 d. Y {4 y* p
- $delta = 8 * self::$rate;
- z2 M* P& ?4 N+ b# D# P( h - $img = imageCreateTrueColor($delta,$delta);
; W# } B; i& e; b1 Y - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); : ^8 h, S B4 W/ a1 Q* s
-
4 M1 _) v4 Q: H' Y& B- P2 p3 @ - /*计算图片灰阶值*/ $ |5 G! n. w9 q' B6 x3 x
- $grayArray = array(); 2 K) m) m# m: h) `
- for ($y=0; $y<$delta; $y++){ 2 ~' a, _4 F7 H/ h
- for ($x=0; $x<$delta; $x++){ - Z% w, E0 l" x( L0 V
- $rgb = imagecolorat($img,$x,$y);
- A% `+ T( m" U3 G/ N1 O - $col = imagecolorsforindex($img, $rgb); & n7 Y9 e7 {8 L& r* [1 p
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
1 |: S5 W, M. j* K+ R5 N) Q( l9 m6 e -
Q8 |) ^8 w4 T8 q - $grayArray[] = $gray; & B1 K* r- e- D% l/ `
- }
- u3 o# `$ f, u( ?( c - }
+ r( n% s; m! R" |: b - imagedestroy($img);
. l* n! l# ]5 q* M -
- R1 j3 i! c v9 W0 {! U+ c2 w! w - /*计算所有像素的灰阶平均值*/ q0 i! k; `: X% ~% n3 D& W( s
- $average = array_sum($grayArray)/count($grayArray);
7 o1 P* l7 x9 ?* C k2 Y - . l. x, n5 O7 r' B/ G
- /*计算 hash 值*/ $ C/ Z& P& N, }( Z1 }
- $hashStr = '';
7 G3 e h; V/ s0 n; |, f - foreach ($grayArray as $gray){
# w k O8 F3 N: q4 k% p - $hashStr .= ($gray>=$average) ? '1' : '0';
- I" H$ s. V7 t, m - } 5 F7 \+ y) K W1 p
-
5 b" A# F X3 [- d3 f - return $hashStr;
6 \! O* V3 i; U S$ Y# ^ L - }
/ n5 m" }+ k" E - 8 G- x# c' l8 G
-
8 P A9 R" l8 t! L( a- I+ n - /**hash 图片文件
5 c) [# h* }, R3 V' G8 d5 q - * @param string $filePath 文件地址路径 2 k K9 m. {0 }
- * @return string 图片 hash 值,失败则是 false
4 o* g6 @ K& {4 ^# E - * */
- Q: Z8 l. ~6 J" y% @( q8 j - public static function hashImageFile($filePath){ - L) z; k' U! r
- $src = self::createImage($filePath); + b0 F9 I: g1 W: z% `7 ^
- $hashStr = self::hashImage($src);
& u; k" R7 y5 D" n- ~/ Z - imagedestroy($src); 6 a4 v/ a+ l, T* ^# j
-
2 {8 W' H9 Q9 ?9 O3 G/ U( k ?7 _ - return $hashStr; C8 {: W9 [/ z& x* o5 W0 c
- }
1 p" t1 U0 I% Z; Y( I9 A1 i - ! T8 Z* k! q1 v
-
1 `* `3 j) ]8 X* A- I - /**比较两个 hash 值,是不是相似 . t$ b; n0 c; { r' ?; G
- * @param string $aHash A图片的 hash 值 ( D# L- m2 \: \! S+ e! H, w
- * @param string $bHash B图片的 hash 值
0 B6 y" t9 s: C8 e. ^; G9 E$ ^4 ^+ O - * @return bool 当图片相似则传递 true,否则是 false
4 P0 D& w) V6 H1 J* k" S - * */ " s( n! K4 u- i: {! B
- public static function isHashSimilar($aHash, $bHash){
4 D2 \. B$ k3 n - $aL = strlen($aHash); $bL = strlen($bHash); 9 L1 U t) W2 T8 t) ? a
- if ($aL !== $bL){ return false; }
9 l# v) A$ X& P; `. X -
( R+ Z7 ^0 P5 s$ k - /*计算容许落差的数量*/ 6 W1 n& M, I. J& m5 X1 a9 s o
- $allowGap = $aL*(100-self::$similarity)/100; & c# i# R/ u' \* K
- * r: W# |8 V, u' ?- a- G2 t
- /*计算两个 hash 值的汉明距离*/ : V# ?, h5 t7 ~
- $distance = 0; ; [* S* W& R) s4 U {
- for($i=0; $i<$aL; $i++){ / f6 K0 @0 V8 Q3 M! z
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } 6 t9 n. z5 `. D
- }
# S6 T# a0 ^" p) j/ Y - * ?+ F A" w/ g1 h! J: r
- return ($distance<=$allowGap) ? true : false; / x( h X3 s( q K9 k
- } & ?8 K- b8 o. q! j2 t
-
0 l2 n% r# Z! K -
3 G0 j( A( i3 x; T( G" X( S6 R - /**比较两个图片文件,是不是相似
3 I0 o" S+ r5 Y. Z' \* w - * @param string $aHash A图片的路径
/ c0 }) ^# c7 f3 C0 r3 X- p& M - * @param string $bHash B图片的路径
9 [% h& T- h* g. [ - * @return bool 当图片相似则传递 true,否则是 false 3 A8 h0 V8 k% A+ ^0 o7 K
- * */ 7 b* k& O& i& o( B4 d
- public static function isImageFileSimilar($aPath, $bPath){ 1 I0 ?. `; [) l2 D) X2 y6 ^3 @
- $aHash = ImageHash::hashImageFile($aPath); * }2 X5 ]9 ?; C! {/ a6 s
- $bHash = ImageHash::hashImageFile($bPath);
; u$ t2 _" {" A+ S/ [ - return ImageHash::isHashSimilar($aHash, $bHash); - a1 I+ a1 P# ?3 f
- } 1 o1 @/ S; v9 P( d" b- g5 J z
- * `8 Y# ~" P) y+ |0 F# k+ w
- }9 O! i4 ^" _9 I" b1 g" X
复制代码
0 h) u* O F; `0 |" z; b% r D, d8 b4 O
|
|