管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图 ^; O2 h+ K+ J2 L5 Q6 y% d6 n
" Z/ \, S8 @+ @8 H
5 a+ d, r4 r; d! I- ]由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。7 }! D. H* q) ^: V, u
- <?php
1 L! B" W- n: G; i" p7 Y0 B# ^ - /** 8 x1 ?. L; t( ~( G1 S, P) M
- * 图片相似度比较 ; l6 s+ L* [, t; i+ K7 R' L* b
- *
, a+ ~% f/ y% y# \ - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; # |" q" s4 i% O( }& l9 G) b/ c1 \
- * @author jax.hu
- X) `, A( u1 y, h3 z& h& b - * ( ~! L3 i' Q7 Q, R# p9 `6 G/ e
- * <code> + v. F/ F2 t( u4 r
- * //Sample_1
5 g5 K) R+ S1 v6 b: G5 X7 @3 ` - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
! J7 Z* Y; Q: [+ q* h0 r - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
' |5 `& F) A7 y3 a) z y; s$ W - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
% r2 R7 e- j I2 Q4 O- a - *
5 i) L" }& j j9 ~; N9 M' {/ X - * //Sample_2
. N$ j+ n- O" a+ z& Q W - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
& a, K" U0 b4 y( I2 y- [- e9 H7 T - * </code>
* H3 B- {' u9 l3 E; y - */ & U/ A" s% I# J8 c! g
- 4 o4 p/ l4 u, Q1 E6 i
- class ImageHash { 4 Y5 @- b2 C9 V9 b
- 6 G$ a g. z0 W7 K
- /**取样倍率 1~10
. C3 T- }7 Z8 ^2 C$ a' G - * @access public
" P6 T' I& X$ {4 r3 G# e9 y$ P: \ - * @staticvar int
1 y: a) x* g, m! [ l - * */
0 ?- h$ @9 ?. l T4 c$ s - public static $rate = 2;
, }# W' h" Q' I; f8 q - + h2 |0 _4 h% e+ L
- /**相似度允许值 0~64
9 Q, C" Y9 t. g - * @access public
( a: q+ @+ l1 t4 w7 n! v8 T - * @staticvar int
+ b& J# o9 {" w y. j8 J8 Z- x" z - * */
# v* R& N$ G7 ~ - public static $similarity = 80; 9 `* b: Q& `; u/ Q/ C
-
3 t! z7 D! w9 ?* [6 g, u E9 i - /**图片类型对应的开启函数 & L7 I% M+ e- B4 ` t% C: D
- * @access private
0 x: c1 z5 {5 E3 ] - * @staticvar string ! t/ @/ ~5 l, S7 s
- * */
/ U3 p* I- Q# }: B3 |6 i) D4 } - private static $_createFunc = array(
, ]1 { \9 E" k: @( J9 o2 m5 } - IMAGETYPE_GIF =>'imageCreateFromGIF',
* o# a9 G; b+ t5 } - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
5 @, ~, K4 a L/ g - IMAGETYPE_PNG =>'imageCreateFromPNG', . X8 D- Y- V" W R
- IMAGETYPE_BMP =>'imageCreateFromBMP',
# F1 W. t- D8 I0 m- F$ K - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
5 S- L! e: l& k0 D" Y - IMAGETYPE_XBM =>'imageCreateFromXBM', - H- l3 o- y( V& i
- );
# h4 }% F) ^' Z7 C; F n" P% p& f -
3 A6 s j7 @9 }1 G6 G - ) c" m K$ s5 K1 Z
- /**从文件建立图片
( }; K! M3 D% X4 E5 [) M# W4 D9 B - * @param string $filePath 文件地址路径
6 h9 e0 p; ]6 b) j% P# W - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
% j7 h$ u L/ k+ t. E; A: c+ ]% I$ w - * */ 3 W$ I, }2 z# N1 u; k
- public static function createImage($filePath){ % a ~3 _# j% P8 A7 j0 M
- if(!file_exists($filePath)){ return false; } 6 G0 X/ R, ^0 ~/ e
-
& P5 @7 E! S& }8 J+ {( n( i - /*判断文件类型是否可以开启*/
9 O' Q/ E+ |4 i( j- d' A% d B - $type = exif_imagetype($filePath);
6 \1 d! P6 I. U' D9 D - if(!array_key_exists($type,self::$_createFunc)){ return false; }
( C; a- V2 ?' @! D; V' N" ?. q - 2 h V' Z0 B. x! T. a
- $func = self::$_createFunc[$type]; / r0 ^& z$ r7 B. _
- if(!function_exists($func)){ return false; } 7 J+ V/ g0 Z6 O0 r/ j
-
: t8 a0 |% @" c7 r8 W; }& ] - return $func($filePath); ' j( m. b( p( [- U; c4 X
- }
5 r* {/ R! N! v% f! P) ]! g2 M! S - % ]) M4 Z5 t# a( L
-
' B, r0 [3 W$ O$ c - /**hash 图片
; Q# s( a0 ]* K, i* A2 K" \ - * @param resource $src 图片 resource ID
5 z) \6 r' Y$ [( ]5 l" g - * @return string 图片 hash 值,失败则是 false
* t+ K) Q2 t: H1 p! X& n5 d - * */
" p# _* A. V$ W, ^/ m - public static function hashImage($src){ 7 Q4 [3 ?* M( u$ n5 p
- if(!$src){ return false; }
* F/ G `; @5 o4 L -
- Q9 Y. Y9 ~; ?1 _9 k9 X8 B - /*缩小图片尺寸*/ % Y, W* O3 E% W, T
- $delta = 8 * self::$rate;
* \, E2 I0 \( v( @ - $img = imageCreateTrueColor($delta,$delta);
- N+ p7 ~3 P5 k W - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); + w" Y# ?7 U, H2 _) A+ G
- & t; k8 S& u, O% [1 R
- /*计算图片灰阶值*/
3 i; Q) [% b6 S6 C: g) ? - $grayArray = array(); 7 r( c7 P* X; _! A! [3 X& T, t/ L |
- for ($y=0; $y<$delta; $y++){
$ t% H# s Y$ Z4 H/ B( F - for ($x=0; $x<$delta; $x++){ 2 ]. }3 S: s, w& o4 H8 `$ B
- $rgb = imagecolorat($img,$x,$y);
! }6 _1 j8 f+ q; g, P9 e/ K! \ - $col = imagecolorsforindex($img, $rgb); / y1 I! J; _& F; ~$ G
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; # M0 G4 |' P6 U6 w6 a9 v2 |
-
0 [3 ?1 K. H9 B/ h* _ - $grayArray[] = $gray;
: B8 R4 {9 }- ?& s; R" V9 L( | - }
" F2 L$ ~ N( H& {9 E# D - }
) X% z9 D, O1 a! R; e' `% V - imagedestroy($img); * w+ M6 e- G+ U7 y S2 H
-
% W' v0 f. e7 l) }% e2 s - /*计算所有像素的灰阶平均值*/
7 y' J9 e* B: i - $average = array_sum($grayArray)/count($grayArray);
3 j! Z, _4 {6 g; S5 U' B -
1 M) V4 p: k/ V7 J+ K+ U. V& n/ E3 g - /*计算 hash 值*/
5 s4 @# G- a" U - $hashStr = '';
n, ~5 b; a) Q! _# x( I - foreach ($grayArray as $gray){ . l2 ?3 v! G3 \! Z4 b4 }- ~2 g5 t
- $hashStr .= ($gray>=$average) ? '1' : '0'; ! d2 D( i! x/ W6 f% }. z3 N) O
- } ]% T6 K1 u% P( Q# ~
- 5 y' v7 Y) X& u6 t+ J( p
- return $hashStr; ! B B6 h, Z# z0 J. u5 _
- }
& N& g8 r0 w' s6 l! z( s -
6 b0 y( ?7 t7 u" ^- A7 ^6 c - + M+ k3 W: p6 y9 W1 `) W3 _# z
- /**hash 图片文件 : i: T1 y- {* ? z0 j
- * @param string $filePath 文件地址路径
. E+ O0 z& I8 c$ H/ h+ x3 Y - * @return string 图片 hash 值,失败则是 false
# t8 L1 t0 d' w8 Y6 Z2 j - * */ & H1 I4 {3 e+ I+ F( d: P' @
- public static function hashImageFile($filePath){ {6 `- ?$ f; w8 L5 z2 t/ o( Y" \
- $src = self::createImage($filePath);
+ l. z- H" a7 M* }4 d( X8 A& ? - $hashStr = self::hashImage($src);
3 b9 }8 U S6 k* C& r - imagedestroy($src);
$ g2 P) l1 l8 F/ v0 A* e: l r -
! X8 e* @, P3 k5 q; [1 S - return $hashStr; ( w, C/ j' r+ R9 T- P
- } 5 Z; P* c* m0 n" F. e& C
- 2 d8 S8 N7 i2 r' p5 g
- ' |: M/ O! n3 o5 Q
- /**比较两个 hash 值,是不是相似
! F' }! P4 f' R5 I! q# F - * @param string $aHash A图片的 hash 值 , _: g! I7 o1 X- c* p
- * @param string $bHash B图片的 hash 值
! u/ y/ C9 ~- X4 |$ X' {: }' P - * @return bool 当图片相似则传递 true,否则是 false
. r7 p' H! c, b: T - * */
0 L% [8 q: H7 i) L3 `5 e- U* q - public static function isHashSimilar($aHash, $bHash){
& l9 Q' P& p2 X% S( F& u - $aL = strlen($aHash); $bL = strlen($bHash); % V* d& u. x& y5 t/ \
- if ($aL !== $bL){ return false; } 0 e5 Y, A# D, P( a, F( B4 }
-
, R! t4 H1 p X3 V - /*计算容许落差的数量*/ 5 _4 f: ~$ v2 u. V- j- y i
- $allowGap = $aL*(100-self::$similarity)/100;
1 E5 M' }" I" r' V. S& H0 R% @3 W; ] -
& c `( Z, j0 G - /*计算两个 hash 值的汉明距离*/
# E7 w) X0 {, E" k0 `7 g% w# } - $distance = 0;
, H4 I( Q. m! `( i - for($i=0; $i<$aL; $i++){
8 R2 i6 r# J+ O6 o! g% ] - if ($aHash{$i} !== $bHash{$i}){ $distance++; } 1 P6 j9 E7 K8 ]$ _5 f
- } 2 |7 x4 [3 U+ E ~0 g7 [
-
2 G! x7 m5 O' D" [/ [ - return ($distance<=$allowGap) ? true : false; / C9 x1 V% W1 K4 f' e& N3 i8 h
- } " z7 w+ c: _( p% i/ U" s2 `+ x
-
: Y2 Z* j& M6 R- B7 l -
+ Z3 w. T5 E$ m& R8 n( d% K - /**比较两个图片文件,是不是相似
$ A8 `. V4 d/ s+ H& ?. l% r3 H - * @param string $aHash A图片的路径 * B* R/ l4 v9 m- t- [1 W% ~6 z+ [
- * @param string $bHash B图片的路径 5 e. v' e c5 ?: G3 C
- * @return bool 当图片相似则传递 true,否则是 false 6 s9 n3 E! Q9 w K! f
- * */ - k& L5 m r" K+ ]% b* t
- public static function isImageFileSimilar($aPath, $bPath){
- [' I% r1 Y* x) o/ h5 D5 S8 O - $aHash = ImageHash::hashImageFile($aPath);
0 p0 T- J1 Q* L! Y. U - $bHash = ImageHash::hashImageFile($bPath);
4 L/ P3 ]8 _# y. Q8 T& g - return ImageHash::isHashSimilar($aHash, $bHash); * I: G( S- g7 S4 q6 h2 F' t. J
- }
# Y' P; o0 X/ v% Z - 7 q% T! W, w, _- B* A
- } J! C$ f$ j; {- K% z( i0 ^" f
复制代码
4 n# V" k. w# F1 Z/ T/ j! v' k- X; w+ E) s7 Y- r
|
|