管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图* `/ U }5 l' m$ {' `
# C4 U: X6 y/ |9 Q) j$ t
$ s$ [2 T7 M% L8 v7 j9 {. `0 c" |
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。; K0 C. j: S( a+ c
- <?php
: O$ V& {, z) a- i - /**
" _4 t7 b2 L# F; x3 Q2 S( O3 ^ - * 图片相似度比较
: m% Q) J% e& w4 t+ p - *
- l+ m: P" w) a! T( a9 V7 o0 n - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
* {; \. A3 I: y# }& [/ S( C5 h - * @author jax.hu
1 T( S, f+ ~- e8 [( L - * 4 Z$ c0 G) X% W, s( O# o8 Y
- * <code> X( @- k* R% W' w
- * //Sample_1
, K* M2 k- [; {& w- g7 r8 h - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); ) m- {( @. r6 F _/ C
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
6 m& H5 z2 Z- ^% h1 v - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
6 g+ a1 ? \- u$ t - *
6 V% f- q P. z0 l6 Z+ ^/ [3 f - * //Sample_2 ! |+ p! e& O9 z' q& X2 ~4 K
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
" ?: O5 y! [* G" f' R - * </code>
, G- \7 K3 S, r' ` - */ " t7 Q0 f9 w5 a2 q2 o5 W
- , ?: A; C7 e% J& d8 q& T/ m2 z0 `
- class ImageHash {
( Q- |: u) e* b& ~6 r( | -
" s& w" V5 V0 Q/ s- |' _+ J - /**取样倍率 1~10 ) ?' r+ a5 ?8 q2 A& D
- * @access public 1 h$ Y- u# l# A$ {
- * @staticvar int 2 o" {6 Q# o& B! g! e
- * */
- g! }4 F4 k% e( Q7 T - public static $rate = 2; & v2 v; \; V5 ]2 u8 {8 C
- ( U8 F$ b9 E9 {1 d# K2 t1 W) `
- /**相似度允许值 0~64 * K6 I( F+ N6 C3 j7 r
- * @access public ! Y- {7 h( g: {5 t* D& u
- * @staticvar int
K) x2 |& ~( e$ V - * */ 6 I% x. E, X1 A/ Y( u' Q
- public static $similarity = 80;
* K- [$ |' i, c5 \8 s -
B/ `3 c( U. H3 i0 {% V - /**图片类型对应的开启函数 : }: q5 x3 Q5 e0 W
- * @access private 5 i7 C8 \; W6 c) u& H. ?! N
- * @staticvar string + }; S5 k$ w8 j- @% Q* e/ J& _9 C2 l0 N
- * */
6 F! T" O+ N2 z1 n3 v - private static $_createFunc = array( - W6 {7 e3 u. l: A! w- G+ J% K
- IMAGETYPE_GIF =>'imageCreateFromGIF', % g0 E8 q) J* k7 V9 H* p5 G
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', 8 n+ e7 k, v( a9 p9 |( ]
- IMAGETYPE_PNG =>'imageCreateFromPNG', * H$ G m q* N* @# `7 G6 d( k$ t ?- Y
- IMAGETYPE_BMP =>'imageCreateFromBMP',
& g* m& \2 X+ s, M - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 7 L7 _- X8 k4 x; y0 `
- IMAGETYPE_XBM =>'imageCreateFromXBM', k, I. @- O% c
- );
) j5 r( Q0 w# f# C) _: C& W - 3 L# F6 p' }. h
- 5 c1 V3 k! z" t- p# H8 T
- /**从文件建立图片 ; s; {8 \- Q* i# S' j7 d3 ^
- * @param string $filePath 文件地址路径
$ ?. ?5 U- A# z# j7 h - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 6 K$ N' n1 q$ w. ~
- * */ ( C# c: W4 q% W; B6 }& ?2 h
- public static function createImage($filePath){ ; j+ K' q4 x/ F" x
- if(!file_exists($filePath)){ return false; } 9 I5 o+ K5 K n& s6 |
-
* L; @* l2 b# x9 P" M - /*判断文件类型是否可以开启*/
% {! o9 W& w$ H* V+ Q6 G b" y - $type = exif_imagetype($filePath); , I% j6 _4 w$ @: b4 V; V
- if(!array_key_exists($type,self::$_createFunc)){ return false; } 1 ?8 B$ {; W0 F
- : V. S1 g4 i! n$ B3 f# M0 k3 A
- $func = self::$_createFunc[$type]; & D- v7 W# r6 A( x& G
- if(!function_exists($func)){ return false; } 5 Y \0 `% d& y6 g9 ~
-
7 w( W, `9 Q5 o0 e - return $func($filePath);
' N" Q) N I( g. L - }
1 z9 e* |3 r. _( f8 W) i4 l -
0 `5 g* p' u* a2 H# P E s - 6 m7 m4 H# S& \# d0 Z& u' w( a
- /**hash 图片
6 D" Y/ N2 P3 p - * @param resource $src 图片 resource ID
6 W$ x" m+ U$ u( \: `; F - * @return string 图片 hash 值,失败则是 false
1 _0 O: ~) u1 w& V - * */
' K7 K8 I j9 }. n0 W8 x - public static function hashImage($src){ % q8 s4 l6 ~) K4 i0 X% L6 @" r/ v O! `
- if(!$src){ return false; } $ Y5 S4 R# b$ U- g+ T: K9 x2 U
- 8 C# f" }# H- f. }3 ]' i
- /*缩小图片尺寸*/ 1 A2 ^) `' A7 d2 a
- $delta = 8 * self::$rate;
* G# Z3 h( Z8 v( d7 L2 ]0 [! p - $img = imageCreateTrueColor($delta,$delta); 2 f, m$ _" C+ b
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
- x+ @6 e/ T1 Y$ W -
9 g( U; h5 q$ E; v1 y - /*计算图片灰阶值*/
' _+ ~# F A, z3 k3 ?+ i. Y - $grayArray = array(); - |; k5 V: `6 d( k. W$ Y
- for ($y=0; $y<$delta; $y++){
# }5 G: K( ~; P( W - for ($x=0; $x<$delta; $x++){
5 ]3 R( K/ j- S, o/ D5 v& [ - $rgb = imagecolorat($img,$x,$y); # S" D6 R( b8 m
- $col = imagecolorsforindex($img, $rgb); # |2 G/ ~3 N3 e
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; & q. m8 N1 h6 ]8 O' y8 v! e. x
-
$ T0 X1 x5 e) }! j- ^& W4 M0 j( x - $grayArray[] = $gray; 9 @5 T# Z8 s7 f6 L. E! N! E
- }
0 o* [8 X0 ^+ u, i$ f - } 6 n' y% Y* g: f" y
- imagedestroy($img); 4 S2 J6 O3 A- t9 q! o
- 0 l1 v! l5 H& \8 I* N8 F
- /*计算所有像素的灰阶平均值*/
% p' X O' W i4 @- v. l, ^ - $average = array_sum($grayArray)/count($grayArray);
) G6 F' e7 H/ O9 h# } - ( B9 R" ^7 V1 p) S& @5 L7 _
- /*计算 hash 值*/
1 b8 z( c) `; g x. J' R" @0 ? - $hashStr = ''; & {6 c, A0 w6 E$ t0 a$ m
- foreach ($grayArray as $gray){ $ \0 B. G5 F% V% M T q, I4 a6 y
- $hashStr .= ($gray>=$average) ? '1' : '0';
, z8 v& L8 d5 h/ g" a - }
6 p }6 I+ @, D9 n ~. j, x1 r - Q; f X& |8 N6 `5 {0 d
- return $hashStr; ) r0 p9 W S! t1 l2 p
- }
' N7 _0 S; c; g j" G* d -
9 W6 h+ r( `. |* P) y S9 p/ c - K4 q$ Y$ B e6 i/ e
- /**hash 图片文件
) j* n& O: }: P9 k4 z4 { - * @param string $filePath 文件地址路径
8 n2 b+ ?+ G- k$ s+ f - * @return string 图片 hash 值,失败则是 false
2 V# z1 I. x& a6 G9 W6 w - * */ ' ~8 Z. r/ r, N* `6 _
- public static function hashImageFile($filePath){ & W2 X3 V7 Q# ~* q2 s, R
- $src = self::createImage($filePath); + N- v. u3 s: i- _/ E
- $hashStr = self::hashImage($src);
4 ]: z( A6 f# a - imagedestroy($src);
B2 E: H. B, C0 ^$ Q8 [ -
+ [3 e+ s! e# P* L! i - return $hashStr; 9 O1 G1 |6 S4 @
- }
. V. a9 o: W" r, N -
- w4 ~3 Z) G- y6 o' h/ i" x -
8 E2 b4 ]. f; ?. W& O/ L - /**比较两个 hash 值,是不是相似 ; T( E$ P5 ?7 }, Y; ]3 v K
- * @param string $aHash A图片的 hash 值
- d/ U# q+ W% `1 F6 Q3 `% v% h - * @param string $bHash B图片的 hash 值 6 c3 L. [4 d1 }# V
- * @return bool 当图片相似则传递 true,否则是 false
& u2 L2 {% }: I8 l' M - * */
3 `$ l! ?$ o [3 i9 L4 x2 O% I" |2 Z - public static function isHashSimilar($aHash, $bHash){ : E0 r7 T# V" ~* l
- $aL = strlen($aHash); $bL = strlen($bHash);
) F3 J8 B) z0 f/ z - if ($aL !== $bL){ return false; } 4 B, c" x9 u$ M2 @
- - }( f. a; F8 r/ N0 V
- /*计算容许落差的数量*/
4 [6 p! A; ]3 |+ ^ - $allowGap = $aL*(100-self::$similarity)/100;
+ d- _0 j# P+ `1 f -
7 T9 B$ ]6 {- u - /*计算两个 hash 值的汉明距离*/
/ @2 f: c0 X' a: h( ?7 \ - $distance = 0; - t9 }' E3 S; X4 t! g. s) a
- for($i=0; $i<$aL; $i++){
, ~/ m* ~0 @) } - if ($aHash{$i} !== $bHash{$i}){ $distance++; } 6 K- ^8 o0 m. o2 {
- } 1 k4 ~2 p8 [9 D
-
. C: D, t! C: ]0 w - return ($distance<=$allowGap) ? true : false;
- y$ P+ p( G5 n1 [% e* h/ R$ v1 G - }
9 v5 I) _0 k! u/ c; B) X' \. z - # t3 b+ R) o5 ^5 ]
-
5 r. n( [' g4 h8 j - /**比较两个图片文件,是不是相似 7 b& t3 j5 ?4 u0 m& l6 z! M* W
- * @param string $aHash A图片的路径 - c. u3 f G! F& Q+ v0 D7 |
- * @param string $bHash B图片的路径
! h( T' n# K0 E* _0 H+ @ - * @return bool 当图片相似则传递 true,否则是 false ) i2 ~9 i+ Y2 l/ R* l' Z
- * */ 4 N% B) o/ h, v, h' k( A
- public static function isImageFileSimilar($aPath, $bPath){
+ p! p) f; m2 V. m - $aHash = ImageHash::hashImageFile($aPath);
+ p; z$ O8 h; { - $bHash = ImageHash::hashImageFile($bPath); - X6 [" g: n: A1 P) G1 C/ A
- return ImageHash::isHashSimilar($aHash, $bHash); 2 H! c# b, n4 F2 |
- }
! P: ?+ M- z6 U4 T -
# V9 `, i, C& C, R' w3 O - }
" I' i/ N% r' S% T4 m W# \
复制代码 5 K1 X. U8 n( f3 g* ^8 y8 n- T* M
2 u! V: K8 f' h$ @6 { T+ d* Z
|
|