管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图3 ^. }5 {# U4 l( e2 p
2 P+ c8 ^7 R' L& O7 w( a5 H2 ^- W0 P1 H, _+ U/ H% I
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。7 A& K# C4 e/ K9 t
- <?php
0 ^9 o* V M: | - /**
4 O, j8 d9 K1 e& m - * 图片相似度比较
T$ h( ^% `7 W3 \ - * 0 C) G5 r: c/ A
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ' s) ]; h. Q5 }4 \
- * @author jax.hu 8 l! B1 q. Y, \) Y4 Y' M
- *
5 u1 G% B1 i# h: }/ W4 \ - * <code>
5 [7 f% r- t \0 G" r7 q- T2 K b - * //Sample_1
1 S! n# S- H6 g. ^+ q- d - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
) J3 k8 [* l& \) D( ^ - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 2 i# O" A4 `, `! U. V
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ( |2 I! ?- v9 ?, }3 R N8 b
- *
. z! P8 `0 U7 }5 R3 `; h/ y) Q% H3 t - * //Sample_2 % q7 I" d7 v+ ?9 [) ^& C# f
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
/ k9 v+ {% [! D9 { - * </code> ' s% ?4 j" H: Q6 f! s: @
- */ 3 L! Z' E( g/ {( B4 w( s
- 5 m: F f8 l/ M. m8 @& ~- D6 Z
- class ImageHash {
: K- K) b; \* r2 u - 1 `" r* z4 r) W) u& `
- /**取样倍率 1~10 " K8 u! c D# W `6 o' m
- * @access public
0 v4 x* p3 o; k" C e - * @staticvar int
, V+ }3 o, ?9 Z( r/ _& q - * */
2 u1 U$ h0 P. s4 o5 b7 o - public static $rate = 2; . o0 Q" g2 O, _4 v+ D# O& c
- * n8 u) L: K) u) Y
- /**相似度允许值 0~64 , H; f8 ?) B; K
- * @access public
, i4 F) `- Q! A - * @staticvar int ) e, ^5 _' K5 E3 Y
- * */ , L' b% b, G% R; A! p, c
- public static $similarity = 80; $ F8 T6 S' r; a8 T* s
- 2 j0 }: m* _- L- @6 f$ c0 L/ ?
- /**图片类型对应的开启函数
" u v' p$ H1 R& n6 ?/ Q& a - * @access private ' g. A% C+ j" Y" _3 q
- * @staticvar string
3 L2 c" Y8 w; |& d- ~ - * */ & y4 L1 t! g3 o5 M g0 Z+ E
- private static $_createFunc = array( ' j" a2 X; D. P, t9 I0 z% C$ ^
- IMAGETYPE_GIF =>'imageCreateFromGIF',
* v$ S- I* }$ ^ - IMAGETYPE_JPEG =>'imageCreateFromJPEG', $ s8 A8 w: Z1 s: K5 S! G
- IMAGETYPE_PNG =>'imageCreateFromPNG',
- U& d: X: l5 G - IMAGETYPE_BMP =>'imageCreateFromBMP', / l6 f' `# X! d v* \
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
" S( A1 v ]- _. R5 I& @ - IMAGETYPE_XBM =>'imageCreateFromXBM',
# H" C, ?( r2 C1 e3 z - ); 3 `& z6 w7 l3 _2 | b2 d
-
q) ]9 O5 n, ^, S1 P - 6 k9 H& `) g1 o. D0 Y6 }6 Q
- /**从文件建立图片
, h& Y! T* h; l1 e7 H3 [; i - * @param string $filePath 文件地址路径
: \+ z0 `) F, L: r5 i - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 6 ^% d: R$ B% [# h
- * */
0 S- r1 o) r' @/ M+ w - public static function createImage($filePath){
{, b7 y; |5 P7 U+ S - if(!file_exists($filePath)){ return false; }
' Y" @7 J$ ~! g9 H. j* t$ ? - 6 |$ Y9 X" h1 \" W1 T) ?
- /*判断文件类型是否可以开启*/
! o% b. u+ K& X/ M" _ - $type = exif_imagetype($filePath); $ v! ?) r% p! t
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ; C8 P8 @' [6 y4 n( C: q
-
5 r9 y' u: E2 |# m5 z8 d* T8 _ - $func = self::$_createFunc[$type];
& \5 Y9 C. G9 O1 n1 c, u4 n - if(!function_exists($func)){ return false; }
$ `" h* J2 N5 V - 3 }; y; ~/ w. ~$ C
- return $func($filePath);
) i* V' `* s! t* c* m0 W5 e - } 4 `3 Y6 d" g) E! `
-
1 Q9 {. b4 |2 r" I0 I+ I - + z, _5 }3 E9 J5 ~* s
- /**hash 图片 9 ~, x. M, }0 j o/ {+ U7 }
- * @param resource $src 图片 resource ID
; ~4 p1 I$ W) t' y& q - * @return string 图片 hash 值,失败则是 false 7 M/ y4 S" I" q9 K. I. @- Z
- * */
8 ^5 o( V/ n% L0 ` r" ~ - public static function hashImage($src){
& X; j% o) n# A# K a0 v - if(!$src){ return false; } ) M9 w- ^7 n) R, X! R6 w
-
3 Q4 C; {/ C7 e/ w: M - /*缩小图片尺寸*/ # Q, F; E- K; `
- $delta = 8 * self::$rate; , @6 i) @- M/ o9 M
- $img = imageCreateTrueColor($delta,$delta); : c2 v" V: k& a; R
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ( ]3 m! M4 b3 r" k
- 2 E @5 i6 _! t+ [" l
- /*计算图片灰阶值*/
' t5 P5 v0 I/ ? - $grayArray = array();
- V8 ?0 H! D6 x+ g5 W% C5 q" W% r% S - for ($y=0; $y<$delta; $y++){ . }; V0 ^3 E1 S+ \+ _# d: {
- for ($x=0; $x<$delta; $x++){ , F) f7 `: Z9 O7 I/ W* [
- $rgb = imagecolorat($img,$x,$y); + m( I8 Q* D, ?) J
- $col = imagecolorsforindex($img, $rgb); ( a4 {) z; C4 H/ n& `' ?
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; - {7 L2 F( J+ O+ @" b9 |$ O
- ( `4 Y* d1 P3 }+ @
- $grayArray[] = $gray;
7 m3 l/ t3 Y" n: ?4 h* b: l - }
1 x/ z( E7 Q1 P - }
. M4 v9 |8 o" U% K - imagedestroy($img); 5 P' q! c/ [+ a
-
6 \7 S) |! s4 x: a1 ]+ q) h. M/ d - /*计算所有像素的灰阶平均值*/ 0 j9 i% D/ X* |
- $average = array_sum($grayArray)/count($grayArray);
8 V1 n& m* r8 j' A! L -
3 L) Z; `* ?$ p$ j- p7 X* G- p - /*计算 hash 值*/
, P3 a; |! o* Y) z( d* I: ~ - $hashStr = ''; $ Y4 y" o5 N4 y2 p8 }+ Y
- foreach ($grayArray as $gray){
$ B1 {, U& u8 n7 A) @0 { - $hashStr .= ($gray>=$average) ? '1' : '0'; : s+ G0 ^1 i* w6 E" X
- }
3 B1 `8 i5 L* p, }; C9 T -
( ]6 F/ q# m. O; D& I - return $hashStr; 4 T* E$ E6 Q% ]: d
- }
& h3 a' |9 E! V+ Z -
. c! P( E+ |4 n& p5 } L -
, B, ~, G( q" w/ F; Q - /**hash 图片文件 $ R! c% b% K% j8 s
- * @param string $filePath 文件地址路径
) o) u+ h2 S, g9 f$ k5 [ - * @return string 图片 hash 值,失败则是 false
( n1 F9 A9 m5 v - * */
/ ]' Z7 W0 {3 o9 h* {$ t - public static function hashImageFile($filePath){
7 Z9 I4 j- V% V7 r - $src = self::createImage($filePath);
+ V+ w2 h. `# V( b) W - $hashStr = self::hashImage($src);
+ Z4 w4 s+ q" z& ? T - imagedestroy($src);
& I2 u ]7 N4 n# p8 u% r9 P - " z6 h9 O" ^! W3 k
- return $hashStr; 7 z# T) J2 ^8 A( g& P, M% z2 D
- }
& L' _4 U* F" G7 q) b - 9 f! q. G( X% f
- $ W+ u# f3 Q2 W( H B( U
- /**比较两个 hash 值,是不是相似 + y8 Y% D Q2 ?+ t
- * @param string $aHash A图片的 hash 值 " [0 I# G k. q5 I7 B4 _
- * @param string $bHash B图片的 hash 值
% U1 A& B8 z7 q5 j8 v- {# [1 m1 j% ` - * @return bool 当图片相似则传递 true,否则是 false ) i) N; x% e5 \4 R
- * */ 8 x- _ W$ }, F9 ?, S" D; a9 d
- public static function isHashSimilar($aHash, $bHash){ 9 |2 ?' [ d) k! R$ G, F
- $aL = strlen($aHash); $bL = strlen($bHash);
4 o! J" Z5 w$ C* J& C' O! j - if ($aL !== $bL){ return false; } 2 M! \4 z1 j7 H+ f
- ! ^5 {4 T. M" P! i9 K
- /*计算容许落差的数量*/ 3 C J/ J2 O" K+ F- D0 e
- $allowGap = $aL*(100-self::$similarity)/100; / m1 w- [! V4 K4 t: J
- ! u7 J" V$ m9 y; @: x. q* i
- /*计算两个 hash 值的汉明距离*/
& q1 d. W ^" R$ L6 O+ ` - $distance = 0;
4 s. R6 w T# e3 i- c+ |/ J0 H - for($i=0; $i<$aL; $i++){
8 S+ I5 r% o( Q, |/ X - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
7 q s" e. C* Q6 v( O& T - } ( F) n. y0 y/ \$ ~$ V. A
- ' m t3 H! J% n3 S: T5 `3 e& U( ^
- return ($distance<=$allowGap) ? true : false;
$ k' @; P; u# M# M" M2 z. \. h - } * d9 x! v0 A0 p' x3 v! a, ~
- " t w. Q) H1 U% w m( x4 o8 J/ I
- 4 ~* |1 O* ?) {- w" i/ x, F
- /**比较两个图片文件,是不是相似 ( P" A( g: H3 @0 \( C& t8 w
- * @param string $aHash A图片的路径
8 U9 t3 e d4 B t2 z- L - * @param string $bHash B图片的路径 ' q6 W) Q! h$ `0 U4 N5 d
- * @return bool 当图片相似则传递 true,否则是 false
# E1 X/ K/ j6 N8 K. d - * */
3 [9 I$ j% A8 E - public static function isImageFileSimilar($aPath, $bPath){
' P. t! W6 f" ` - $aHash = ImageHash::hashImageFile($aPath);
6 e# c- c% J5 q - $bHash = ImageHash::hashImageFile($bPath);
0 Q; e. b2 {) `) E3 T - return ImageHash::isHashSimilar($aHash, $bHash);
* l$ k2 ?5 A* d1 u+ J# z - }
s9 u# ^! S" {& U -
S6 j: H$ E0 Y- E# ] - }
, [4 q/ P) @! V5 g- c) ~
复制代码 ( ~( D. _$ v/ T( E4 a
: W) `( f: r/ Z9 g7 M" n' r |
|