管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
* i5 i/ [- H/ W- @) V) N$ l0 {1 z& ]; b
" y" h/ j! ?9 e. }6 L) ?; x由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。* f7 w. n8 E( G a) L
- <?php 9 f) N4 v% T1 I3 M6 x- \8 ?
- /**
; C& X5 A2 @1 Q% E5 I- E - * 图片相似度比较
! q+ j' o) p/ p0 @3 W - *
8 p( _, o( D( F. e- H1 a& ~ - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
. L. t0 R0 c: \% q - * @author jax.hu
0 b z* L4 I& k: }+ |+ @0 [/ H - *
! u" v) F( R- s7 } k3 |* }4 y - * <code>
( L& s2 k( w8 w- x6 f* N+ S - * //Sample_1
7 g0 ^4 i' ~! f7 ~5 h7 e" L$ R9 F - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 1 s5 I! S3 P, w; n e: F
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 2 s" }: T6 l3 T4 O8 P
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 8 V. v; C- A# `9 m
- * $ C5 v2 ?; U1 {: O$ D3 ?
- * //Sample_2 , m% K" o: W& y3 j, E) f
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ! ?* j5 R' X2 g, w# d( t8 o
- * </code>
. `5 H* y! Z" A; q, P# d) O- j2 c! o3 j - */ 9 t* G9 U. Z9 f
-
; I; G" g" E! z- ?7 a7 x - class ImageHash {
" ]1 L) W' U/ T& v) D - + @ y& o) ~+ M' s9 Q
- /**取样倍率 1~10 2 S C9 S2 r* S( Z
- * @access public
# f' q; ^$ l8 `6 [% d& c( h) J G2 I - * @staticvar int
3 N5 Y! E* W7 x0 p( f - * */ ( J& G1 @) J( y; n; _
- public static $rate = 2; $ c% o! \8 M# s. Y3 @
- $ e; m( f, g6 P) k, U. o
- /**相似度允许值 0~64 # m& |: k) O7 j9 }' i6 b
- * @access public
: ~8 J \5 ~% |. V$ k+ @* j- \* R - * @staticvar int ' Z, s: J$ o+ s
- * */ $ z; C. _4 j2 U; W( R5 E" `' r
- public static $similarity = 80;
+ i" C' y! I% | f$ @# Q7 c. ^ -
/ g, D. e7 {! ~" G4 Y$ `- f - /**图片类型对应的开启函数
; J& L% J1 `1 v# z3 Y - * @access private
1 S+ Y" x0 B# n" U* h7 |8 u - * @staticvar string * c, s' {6 r/ c: {" @3 T
- * */
% p# z b: b' S& | - private static $_createFunc = array(
" K8 K& n, w H* ?! y; ], Y6 n - IMAGETYPE_GIF =>'imageCreateFromGIF',
7 {4 n/ D+ \% e3 ?# c4 F5 i - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
6 M; Z3 L9 B2 i9 a - IMAGETYPE_PNG =>'imageCreateFromPNG', 0 S n0 }# H5 `
- IMAGETYPE_BMP =>'imageCreateFromBMP',
9 R6 X& W' X* U! p t5 r- p. Y7 b - IMAGETYPE_WBMP =>'imageCreateFromWBMP', " Z4 V" V$ k8 H1 o0 a4 |) ^
- IMAGETYPE_XBM =>'imageCreateFromXBM', . K% Z, P8 V7 J+ f2 P+ j- f$ _7 h" a
- ); + t ?' e0 n0 D, |7 W, G8 J, `
- 3 q% E) Z1 A) |& P
-
" U; l2 ~9 H/ b6 E - /**从文件建立图片
, h& E2 A, L" A7 C0 ] - * @param string $filePath 文件地址路径
8 m3 |. s& L4 z, M& S4 r% R2 ? - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
, f! h2 V- x d% a* f! a - * */
1 l" f, \; q4 [4 x - public static function createImage($filePath){ 9 k$ X! J2 l" s' w
- if(!file_exists($filePath)){ return false; }
8 k5 W" @1 ^" L& ] ^+ C -
0 O; S0 t5 c8 H# S& a4 q- x, \ - /*判断文件类型是否可以开启*/ 1 r9 z K3 \; m' ?- N
- $type = exif_imagetype($filePath); & O+ i4 n# a3 ~+ i9 E- u
- if(!array_key_exists($type,self::$_createFunc)){ return false; } % W- d0 x* ^; Y" ?8 ~* A5 r4 x
- * [7 O5 `; C; V3 L8 ~. u
- $func = self::$_createFunc[$type];
" y9 O' s# g8 d% d2 ^8 j - if(!function_exists($func)){ return false; } N! o1 s$ ^, r. G2 F6 [
- ! f: l) }2 D; F2 E2 R8 F
- return $func($filePath); - D0 n1 H7 `. U% i( i, g
- }
: Y; q; f* C3 K D, K8 R1 Z - - w9 p; r* k, h3 h9 W% T$ G
-
: [1 \) b: i; u9 f - /**hash 图片 $ o% A Q8 r. @0 X$ q. O5 ^* t
- * @param resource $src 图片 resource ID * S$ z7 F q: }/ P4 p6 ]8 g
- * @return string 图片 hash 值,失败则是 false 6 U4 I5 v* O9 ~3 X$ W% ?" T8 U
- * */
5 Y& H) f2 E" t* y/ B - public static function hashImage($src){ ! v% |1 V& i6 ~$ V
- if(!$src){ return false; } 8 A% N4 u: p; y% ?( d+ H/ S7 x6 s
-
* X+ }! j0 A* I - /*缩小图片尺寸*/
8 @8 p! y( X1 D2 L, Y - $delta = 8 * self::$rate;
+ @0 A( ~$ \+ \ - $img = imageCreateTrueColor($delta,$delta); : _" B0 e! u$ G& V$ @8 }
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
' b* B0 D# A1 T, v1 W0 j - $ o7 f: b% _, X: R0 ^2 o& Y8 R8 m
- /*计算图片灰阶值*/
& B+ ~- \5 H, t! F! j# m- H - $grayArray = array(); ' E+ E2 z& h' c' }! J+ M( v3 `
- for ($y=0; $y<$delta; $y++){ ) ^; E5 H+ h. w5 ^
- for ($x=0; $x<$delta; $x++){
, C. ~, i9 f$ {3 T& A' E; i - $rgb = imagecolorat($img,$x,$y);
8 N& H& p6 X5 r - $col = imagecolorsforindex($img, $rgb); 0 ~. z4 @' i' E1 [ V' v* c
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 6 h D0 A( h, v9 w$ L
-
1 h; V. M! J0 ?. z' b( o. F - $grayArray[] = $gray; 0 U, B- S7 \9 A' U& V* ~
- } # } g# L2 f% M2 C+ [3 f% M
- }
6 X2 g; v- r Y- D' U1 U5 P$ X - imagedestroy($img); - w/ i9 a6 ~8 Y& c* S8 u+ P
-
; A3 P/ I! [9 [4 `4 v) }" P - /*计算所有像素的灰阶平均值*/ I+ Y9 [# Q9 J$ C8 L' v& B
- $average = array_sum($grayArray)/count($grayArray); / M0 C$ m1 U, m) A/ q
- : l4 ^* P+ t" I
- /*计算 hash 值*/ 1 V, W9 e0 E+ t. P. H
- $hashStr = '';
7 ^% |# O/ w" d" J9 W7 |5 X - foreach ($grayArray as $gray){
# E' T! V" u9 [: B4 g: X - $hashStr .= ($gray>=$average) ? '1' : '0';
! x3 ^' ]4 |/ y - }
$ _ D% E8 n- R7 C! q5 u7 ]# a - * o9 X+ R0 ^) Z+ f* X9 q7 w2 J: Y
- return $hashStr; + y1 M( p; _5 s
- } ! I- r- Z' p3 i. r5 m# `# Y
- 4 r* b5 V, ?3 T6 D
- 4 B+ E4 ?, |2 ~$ H8 u9 v# T3 C. P
- /**hash 图片文件
# {- V7 G, v* U& F! J5 n - * @param string $filePath 文件地址路径
% S- T0 d, M" Z - * @return string 图片 hash 值,失败则是 false
8 m% x, l, ~% M* b X. S - * */ j2 e8 y+ s4 u
- public static function hashImageFile($filePath){ , o& e, H9 @& S$ E& Z
- $src = self::createImage($filePath); , `& s2 p# N9 b, V
- $hashStr = self::hashImage($src);
! _+ {1 l; H3 L* Z! n# l: s - imagedestroy($src); 3 w% a, S. ?% J. |: I! O, W; ^
-
2 L6 A2 p, N8 P2 v; i - return $hashStr;
$ q: A& u) B% _# @3 ? - } 0 L; J4 S! y o% ^1 m. [7 l
- " H2 A: [1 _, |9 J1 k+ d5 S
- . F; p: U* o1 ^- l6 o, V
- /**比较两个 hash 值,是不是相似
; m$ T( O3 r% E7 [3 Z; Z1 F - * @param string $aHash A图片的 hash 值
5 Q2 q6 s- W& N7 W$ k - * @param string $bHash B图片的 hash 值
; @( E8 V* }3 S3 h: o, g6 I0 c - * @return bool 当图片相似则传递 true,否则是 false % Y- I0 b+ y' \$ C/ o% V2 ^ {
- * */ 2 S. v) }6 U Q, E, n5 r
- public static function isHashSimilar($aHash, $bHash){ 7 G" b1 r6 e3 O8 ^7 V8 Q9 k
- $aL = strlen($aHash); $bL = strlen($bHash); 0 i6 [$ n/ z r( [( p- _& c9 M. F8 p
- if ($aL !== $bL){ return false; } # m' E( U9 Y* K$ L0 j
-
+ n8 ] t9 J7 E7 P8 h) g. g - /*计算容许落差的数量*/ 8 M' L2 ^( h5 ~
- $allowGap = $aL*(100-self::$similarity)/100;
; ]& k1 q' g0 C! W- N0 } -
4 a* C7 _- K4 H1 J* \' F - /*计算两个 hash 值的汉明距离*/ 0 [: L O, k2 A1 G
- $distance = 0;
- e: [9 N- C2 C$ h. }" w8 J - for($i=0; $i<$aL; $i++){
) b. a8 |% n- U6 k% U! ]7 J - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
0 E. S0 b1 j, \+ f* _+ e, A - } 9 a. B+ t7 i7 }2 [( K
- # w4 h2 E( [# S$ t( c
- return ($distance<=$allowGap) ? true : false;
9 \. q$ a) e5 {$ t1 a - } 6 G7 z2 k4 q+ R' l( a; `* |' J
- 2 y% \+ n! j5 a0 H l9 R
-
1 D/ X2 W; {$ `7 w - /**比较两个图片文件,是不是相似
7 `5 X/ I8 b) l1 E9 R - * @param string $aHash A图片的路径 2 Z8 u7 E5 Y8 h. Z: i; C; g' ~
- * @param string $bHash B图片的路径 % t7 J* F6 U5 m2 f. G( G2 H, D, Z% m
- * @return bool 当图片相似则传递 true,否则是 false . p$ M* Z+ @6 p" v2 n; ^
- * */
6 F( H8 Z& ^6 Y% } {# | - public static function isImageFileSimilar($aPath, $bPath){ " ^% M% g/ y; V# @% e$ ~ W2 i
- $aHash = ImageHash::hashImageFile($aPath); . v! a% s2 e, t3 N8 e; E* c1 G
- $bHash = ImageHash::hashImageFile($bPath);
. S# k1 R% K9 S. d, I5 _ - return ImageHash::isHashSimilar($aHash, $bHash);
) ~! a; a, f' N) y2 F3 j - }
3 W4 Q% V& `3 |& _4 Z - 8 T0 O' I, U5 R" y. c
- }
; M" Y2 f, i# J. {& w; Z
复制代码 # d8 {9 A4 d! a0 W) P' L5 l4 k* }
" Q( a% ] d( u: \2 G
|
|