管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
~7 m* j" B; [/ m: k! l0 u& G* E! c1 N, d- `
4 f, D+ u; z: T由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。2 R; f0 U/ f% y9 Q" s( G
- <?php
4 s+ f. ~4 w$ K7 m2 t$ \3 Y6 {, d7 P - /** & m1 x/ A; i& |" L+ H# P% ~ ?- W
- * 图片相似度比较
- _; _6 Z6 B$ F1 I/ g6 g4 I/ d - *
( b% D. \ k2 H6 z; c& T2 ]; m7 N) u - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
# J3 \$ V" n# |9 h" N - * @author jax.hu % _( D1 t& U* |# M* v
- * ( f7 Q* S0 A+ C* s
- * <code> . T9 B7 P9 J1 Y9 X& L
- * //Sample_1 7 f# r. M& V& X% G6 J" r
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
6 U4 E9 n" A: q" B [/ N - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
; |& C5 N7 o, O- u! k9 d+ M7 A/ B, N+ X - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
: D x: Q! s0 k' X - *
9 D! r' C/ Q( d - * //Sample_2
: z: \' E( K# I - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ' a* h. F1 U J- K" A; t4 G# h% \" ^
- * </code>
& k4 g* \5 D2 \/ \5 v1 O A( h - */
7 f3 s. o4 `* R: Q B/ _ -
! x& j% {& `. J* Y- A - class ImageHash {
0 u g |: w( Q3 j$ g -
, a5 t; d. P8 W2 B% @ - /**取样倍率 1~10
e6 ?. ]& y# W$ C" N8 V: j* D - * @access public
! N) `! O+ |7 n9 Q- ~3 s* ~; P, d - * @staticvar int
! Y: Q; b& J( D6 q - * */
* G' w5 C9 R9 k4 s0 z - public static $rate = 2; 4 |5 _& _* r9 C
- 5 U: z4 @* ~, j( n, E
- /**相似度允许值 0~64 / u; }& Z7 e# s% N. P# _
- * @access public
+ \) D; h# m: I( h$ r' l; F: n - * @staticvar int
( i! W" L" T _9 p) Y( {/ o - * */ * j. e0 V7 f* I/ y
- public static $similarity = 80;
' j4 o8 x/ t* O# d! s* L - b* S% W& F- Y
- /**图片类型对应的开启函数 " B3 Q% }+ F) X7 X( }
- * @access private 2 ^' k: K- [* s4 r
- * @staticvar string
; Q( @$ G/ o; s. G8 G/ R - * */ 3 [+ m$ T* D3 @ N5 }8 T0 T
- private static $_createFunc = array( 3 P, V+ L- Z% i* l& K1 N
- IMAGETYPE_GIF =>'imageCreateFromGIF', # h* w) v/ S* ?8 Y" U- H5 N' g, i& Y
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ( h. t' E5 F1 i* P ]/ d! p
- IMAGETYPE_PNG =>'imageCreateFromPNG',
- W: Q) B, R+ I" Z+ \ - IMAGETYPE_BMP =>'imageCreateFromBMP', ) S6 [- e% K! F7 f: a' J
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
; G# V0 P% K8 e! Q3 i& P - IMAGETYPE_XBM =>'imageCreateFromXBM',
: I u' e+ b. \+ ~$ e" X. T - ); 2 t% t2 Z: W$ K0 K' n
- : E8 |0 F: G0 d2 Q) s1 z
-
, ]- O) T1 [( P3 K( b - /**从文件建立图片
$ F4 `: s8 S! H7 ]8 a+ ]% z - * @param string $filePath 文件地址路径
; L6 d% @. N$ j0 J - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
8 V1 ^* x8 ^! P, j - * */
0 ?! T% k! M" l0 v - public static function createImage($filePath){ , k* R$ H- p/ h# X0 k& ^
- if(!file_exists($filePath)){ return false; } 4 W3 _6 `! j, t& w
- , A8 K9 R ~5 t( S7 E
- /*判断文件类型是否可以开启*/
; U1 B c4 M4 _! B& ^1 U - $type = exif_imagetype($filePath);
- _' `% ~4 x, N1 L9 b, n+ M6 O - if(!array_key_exists($type,self::$_createFunc)){ return false; } ; @8 T% l6 J( F+ \. w5 @
-
. c0 z8 `+ M, K4 x. N) t - $func = self::$_createFunc[$type];
: M: _% h7 U) N7 I8 G/ y - if(!function_exists($func)){ return false; } 2 K O+ m2 ~/ `
-
$ ~* A* Q E6 M! B& y9 q) b6 }" Q - return $func($filePath);
: k5 o1 ^& E E6 D - } * G: z/ ?. E# {) v2 f! s l' D$ r
-
1 ^6 E7 C% C3 ^+ m7 l I5 I -
- i, C; Q& l0 ]( Q - /**hash 图片 $ x6 E; e% j6 X9 P' `* D" o
- * @param resource $src 图片 resource ID 4 d i7 o9 |0 o( k# O
- * @return string 图片 hash 值,失败则是 false ) x- F6 I( {$ E- K. J3 R6 t
- * */ % p1 r$ F6 S3 W
- public static function hashImage($src){
0 \2 P( a- V7 A! i* P) I6 R - if(!$src){ return false; } 3 A$ J' ^& g8 y( t8 f3 Q J
-
- e) r* L$ B( Z6 L9 C8 u# b# z, ] - /*缩小图片尺寸*/ ! `5 f% J- M/ u/ F
- $delta = 8 * self::$rate;
, F! l: {9 w* `1 b8 T0 v% i - $img = imageCreateTrueColor($delta,$delta); 8 A/ P' {0 h$ C6 B3 |8 m* H
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 0 q; B7 c5 A- c! e
- ) {: y$ O4 Q# S2 X( M
- /*计算图片灰阶值*/
8 n) j$ H( ?8 B - $grayArray = array(); 0 v* ` `' a$ ^' H* }
- for ($y=0; $y<$delta; $y++){
. f4 i+ y& c% j; k) _! ^% u - for ($x=0; $x<$delta; $x++){ + N7 D Q% J* I, O6 q0 w
- $rgb = imagecolorat($img,$x,$y);
. ]# T! P/ l& l; c- t! |# W7 U" ~ - $col = imagecolorsforindex($img, $rgb); 2 V8 ^: t8 A/ A/ C1 H6 t
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
! e' R, T% ~' r' \: G; j8 N3 w - , J7 R! V. N; Y
- $grayArray[] = $gray; s1 P5 ]$ v0 t9 u3 S* \2 B8 B9 ~
- } 0 o5 z9 t7 T' P$ m2 z* N' C
- }
! K2 ~0 g: S0 ^ - imagedestroy($img);
& q% A/ U( X- J1 f: _1 } -
m q1 ^- H9 I- n8 W - /*计算所有像素的灰阶平均值*/ ; y* a* `1 P; ^) p, ]! o
- $average = array_sum($grayArray)/count($grayArray); & ?8 V9 H* W& A' A+ i
-
! s5 `5 \: T) u( j& S1 q* k - /*计算 hash 值*/
7 W* ]- t5 x. ]# U8 G - $hashStr = ''; . `. H4 l3 q: y- U
- foreach ($grayArray as $gray){
$ S- k5 K% E2 o+ ? - $hashStr .= ($gray>=$average) ? '1' : '0'; / K: }6 |% B, c% Y; i
- }
5 C7 r5 j: f' m1 y& g# P; k -
$ Y" X2 i; m8 N$ @. l0 j - return $hashStr;
& i6 Q4 g) z- L9 w* x: ? - }
7 Z! n' v0 q- ~" j* R: v7 B" d: d, J - ) |* D# c: ?6 j/ Q/ |
- 1 P4 q% q4 p! X: q
- /**hash 图片文件
|7 d9 G3 @) q4 ? - * @param string $filePath 文件地址路径 6 P6 g$ y0 k; c: a& \
- * @return string 图片 hash 值,失败则是 false 5 M2 g! k7 }; n0 [" U9 I T+ o' B: p
- * */
- V) H' G0 V( r1 ~+ A3 i3 ^ - public static function hashImageFile($filePath){
/ v% q5 r- D* v5 w: J: E - $src = self::createImage($filePath); $ v1 E. ?4 |, z
- $hashStr = self::hashImage($src);
) B6 f. K% a. h8 c - imagedestroy($src); 9 r; B! b3 T% T
- 2 m% t9 P) k+ Z6 Z/ r! }
- return $hashStr; ! _& J) k& `& ]5 F& S
- } 2 I2 _$ m! U d2 N- {
- / V* z- L( x% F2 l% l3 g" U
- ) \ M; X3 ~; U( O& y# t/ w$ Q1 }
- /**比较两个 hash 值,是不是相似 ' P# C6 A2 e. `* W( v# s
- * @param string $aHash A图片的 hash 值 - Z! M; Y7 m2 o& v1 W5 | u
- * @param string $bHash B图片的 hash 值 0 Q# b% \5 R- z1 T1 @
- * @return bool 当图片相似则传递 true,否则是 false
o& f- Q: S- g/ c p2 r - * */
& }+ Z* Y7 Z2 B# f; [ - public static function isHashSimilar($aHash, $bHash){
+ M8 ]* b* v' D1 M1 e4 W - $aL = strlen($aHash); $bL = strlen($bHash);
; x8 y+ X( P( P0 `" g+ N% F H$ I. l, V6 | - if ($aL !== $bL){ return false; }
! ]4 K, d2 p8 m/ h5 O -
+ @- d' i4 [. Y9 C* z/ O# \ - /*计算容许落差的数量*/
6 M% F+ l* Y. X9 l$ j% v& Q( j- m - $allowGap = $aL*(100-self::$similarity)/100; & j& |9 X) y* ~1 B2 [! o, [
-
5 z ~ Q: N6 `3 x, z! } - /*计算两个 hash 值的汉明距离*/
1 W7 J I6 A% m8 f" B: H - $distance = 0;
3 R2 L* R8 O* S& g( c8 j$ s; J7 m - for($i=0; $i<$aL; $i++){
" V) L: P* y K4 F% D0 ]* z - if ($aHash{$i} !== $bHash{$i}){ $distance++; } : R4 L, K) O1 f# v# c/ P! G: `
- } 8 _. T3 N8 o# P* J! y" v$ ]
-
* H! O# l- ~6 l. J - return ($distance<=$allowGap) ? true : false;
1 K" t' k; R# m - }
1 C. S0 n4 A) a9 ~( L! Z7 d - . _& R1 v$ ~* r) v4 V
- : e. ?$ h" _+ b- Y7 K- [
- /**比较两个图片文件,是不是相似 # c0 O/ _/ {+ J8 b$ C9 r
- * @param string $aHash A图片的路径 ' M! g. X+ F* [) W
- * @param string $bHash B图片的路径 8 f1 F7 r6 K5 r- Z+ ?2 \# h' S
- * @return bool 当图片相似则传递 true,否则是 false 1 w/ N* Y/ B, W5 N/ E, Z1 S
- * */
) g/ \& n! D6 `5 X/ @. u; I r - public static function isImageFileSimilar($aPath, $bPath){ # m8 I' n% H; r% y
- $aHash = ImageHash::hashImageFile($aPath); : R4 |/ U) V" {+ T
- $bHash = ImageHash::hashImageFile($bPath); 8 ~8 \5 Y5 [7 S, g
- return ImageHash::isHashSimilar($aHash, $bHash); . A: s0 J3 I' J- r5 i
- } & J( y, T, [( N7 p9 V) C
- : Q6 W7 U3 X* T- n4 U. G- v2 Q& i" r( ?
- }
% g5 J9 f/ k4 C, X4 O/ B( j
复制代码 " ~9 w w) ^- |
5 F: l7 H0 M" q$ k, T% f* j5 K* g- ?# p
|
|