管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图( A5 e+ ?9 n& \: Q$ u1 b% G4 e
, j$ T! O0 w; L2 J& @
- q( |8 s/ D" W" b! c# R由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
4 _, L6 l' G9 Y6 r; g; T- <?php
* v1 N9 H S" G$ @5 b' v - /** + v6 C3 }; q$ s# i
- * 图片相似度比较
$ {4 e0 ]8 K0 X# T - *
" o$ s! j* N! R" K' O5 t' R - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
5 e; k2 z- c8 E - * @author jax.hu
m* R! V: V, e4 Y; u& a - *
2 t' e0 y$ L$ s7 M4 j* ^: O1 `6 { - * <code>
0 Z/ w) x8 z$ c/ Z - * //Sample_1
, y( U8 b3 \- ]! J9 L7 J4 @3 q - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
`# X i* {; z6 k% G - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); Z, a$ O/ H% V% @
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); . m' Q3 V+ }' {3 d; S6 s, L
- *
# f) L# L6 d+ j - * //Sample_2
9 b$ Z2 h$ q p- @4 P8 l" ^ - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); / v: e$ \1 k5 o8 C* i
- * </code> 4 y4 a* ]& |1 W6 F1 ^: x4 k- [7 V: ]
- */ ! @# d) W) O& D+ S9 s. q
- & x9 o% O0 a9 c( R6 b
- class ImageHash {
; p; ~6 u8 i9 I7 x) Z - 8 |! F7 L3 u6 N( x8 d2 X
- /**取样倍率 1~10
7 E1 }6 ` \' k( ~8 y - * @access public
! J" {+ r+ Z4 D7 B1 x( O - * @staticvar int 8 q. t" Q6 _ Z
- * */ ( V' j5 W1 t c# D
- public static $rate = 2;
; u& Y5 L/ @2 s# ? -
) m/ a* h: j' |- e - /**相似度允许值 0~64 ' ?; V+ K- X* i& c7 |# F& ^
- * @access public
' N, n. K J0 T* K& \ - * @staticvar int
9 ?- }, A3 v5 B) ` - * */ ' i, m" V3 c+ V& n7 J
- public static $similarity = 80; 0 b; P) }) r2 d, @' ]! J
- + f: W. g/ u: k4 L! d$ l
- /**图片类型对应的开启函数
2 t& L# J* u% J3 G0 c h - * @access private # ^2 O! H, z1 x2 T
- * @staticvar string & j) ~& }. V. a! `6 H
- * */ # ?& Y; U* M* \+ W5 N
- private static $_createFunc = array( . M K6 I: D; U5 M4 D
- IMAGETYPE_GIF =>'imageCreateFromGIF',
; }/ v) b8 C! e; W7 ~7 [ - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
8 X5 h3 `8 b, O! u+ ?' T2 Z! U - IMAGETYPE_PNG =>'imageCreateFromPNG',
r1 S; ^3 \* w: Z9 {# F9 r2 P - IMAGETYPE_BMP =>'imageCreateFromBMP', / p! P, ?4 E. Y. C6 A: @
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', $ R9 X: D. M7 p
- IMAGETYPE_XBM =>'imageCreateFromXBM', 4 d, m9 G' z: Q( D, Q k* ^
- ); 2 o- y6 L. u2 O' _; u! O- V
-
c% L- y G. @- {9 R" ~3 S - ]( k2 `5 e. z E4 i* `
- /**从文件建立图片 * `; P$ g5 Y9 R/ Q# s
- * @param string $filePath 文件地址路径 # ^' j7 h* l/ s! ^7 \3 @
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
) z9 L5 N2 X& |8 ~. { - * */
* K3 O8 u" P3 d A5 L/ w5 c - public static function createImage($filePath){ 3 _- t0 N* X# S- d0 J8 O4 F
- if(!file_exists($filePath)){ return false; }
6 X( w$ ^% q7 ?5 w* V! Z -
4 h: D: T2 B6 D, r - /*判断文件类型是否可以开启*/ ) [6 h' F/ f# c% j3 Q7 d& R
- $type = exif_imagetype($filePath); " d4 ?- m6 v; P9 g' g" C- s+ O% E- H: {
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
}' Z* F$ }# e% v0 E# @. X -
" R2 d! s: l+ t+ @ ]3 A& C0 o! M - $func = self::$_createFunc[$type]; : o7 K, t3 k6 c3 I
- if(!function_exists($func)){ return false; }
3 n. ~; B5 f' q, s4 v -
6 g$ I' ?! ?( R- O( z - return $func($filePath); ) d: C$ p o1 I: [2 Q/ @1 k( t6 W
- }
/ I/ D7 r! c/ M: }+ `1 h -
: ?4 p8 C, ]$ S. i8 q1 \7 ] - ( T- Z3 o j" F: Q* p
- /**hash 图片
7 g% A$ T6 U6 _' P - * @param resource $src 图片 resource ID % F# K+ Q2 Z3 C/ Q6 a6 v
- * @return string 图片 hash 值,失败则是 false
) C$ y H% u" S. N9 w% X - * */ 3 k- \# B6 h4 J7 i9 |* T- O
- public static function hashImage($src){
, M9 b8 [: T- S9 h, ]$ ` - if(!$src){ return false; }
, H9 S& I( Y1 V1 _1 D -
! t& K3 o0 ?( l# o0 Q: _% W - /*缩小图片尺寸*/ 5 _ c5 v/ g+ j! C' J9 f
- $delta = 8 * self::$rate;
' r% l9 H( W/ m) x9 Y. W - $img = imageCreateTrueColor($delta,$delta); " J. T1 }. M' G/ U; Z; F* `3 ?
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ! N5 e2 b% w; M% `2 v7 R
-
0 @4 q- x$ X5 p - /*计算图片灰阶值*/ , h. c9 P% q$ u0 k: r: T/ K
- $grayArray = array(); 9 z. w$ x' ^2 A% p* Y
- for ($y=0; $y<$delta; $y++){ % N( g* `# v. u( `
- for ($x=0; $x<$delta; $x++){
( W# j1 K- s$ r% P6 P - $rgb = imagecolorat($img,$x,$y);
/ V7 `3 C: \3 U) W% ^ - $col = imagecolorsforindex($img, $rgb);
$ i. k- m/ S) @* A U+ h) w" B - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
5 j. ]* i6 R( w' {0 ^ -
+ b: J& V k$ ~ - $grayArray[] = $gray; - \/ l8 P+ T$ i3 }8 {
- } # M6 _$ s: ?( f" X9 s) i; O
- } / X) B% D6 j4 ?+ d4 g. y
- imagedestroy($img); # h$ C2 t+ d/ h- W; p- u4 X
- ) _- a" _7 e1 @: P
- /*计算所有像素的灰阶平均值*/
( L0 S1 `; w# k - $average = array_sum($grayArray)/count($grayArray); ) E, E( W5 _+ p# v- I' i
-
3 v1 T$ `3 G# m - /*计算 hash 值*/
' F0 g1 o, C/ ?1 |, c" b$ w# T( @ - $hashStr = ''; 0 h9 y7 `& `# ?
- foreach ($grayArray as $gray){ 4 t4 h9 X- R7 `* S4 z' a
- $hashStr .= ($gray>=$average) ? '1' : '0';
; A# }8 i7 e* o0 n2 T+ F - }
. ?' U3 Q/ ?8 ^3 X7 a2 w, M - 8 Y( X! m1 R& \! ^8 c
- return $hashStr;
5 \! K T! v* I* q( D8 m - } , P. e/ I6 g2 R9 e7 g
-
4 a8 L9 F' }" E9 {& D - ! D7 |$ o3 w1 E8 ?0 _
- /**hash 图片文件
! |% e/ S; r) @. x6 A& ] - * @param string $filePath 文件地址路径
& g, X, m, R5 B - * @return string 图片 hash 值,失败则是 false - u7 U; b2 D2 W+ H! w- {4 k
- * */ V2 q9 j( r9 I/ A5 W y2 P
- public static function hashImageFile($filePath){
& P8 d' g$ ]2 g* y+ Y- O6 \ I - $src = self::createImage($filePath);
5 e& o% m4 |9 D2 | - $hashStr = self::hashImage($src); , k' `: A4 j& Z9 u
- imagedestroy($src); % q7 c4 K$ n+ x4 |! G- ?
-
: Z, G+ f9 i/ c3 v+ ? - return $hashStr;
) `: P$ i$ d8 w* T - } 6 D1 h5 Y6 Q1 U4 q( ~* F
- ; q( i: N) C6 c7 V. p- E: ?
-
, [- {; p, F5 t2 z! f - /**比较两个 hash 值,是不是相似
8 j* Y$ i i R - * @param string $aHash A图片的 hash 值
& s1 o4 |. Y/ Q1 l8 f' Q0 z3 i2 ] - * @param string $bHash B图片的 hash 值 ! \5 w$ ~5 F3 _: p" Z
- * @return bool 当图片相似则传递 true,否则是 false % i- r) V) b- `% m* p. p8 V3 E
- * */ $ k- w6 p# ]+ o8 e M* g9 @! ^* d& N
- public static function isHashSimilar($aHash, $bHash){
5 S8 i, |; v4 a+ A4 ~! T - $aL = strlen($aHash); $bL = strlen($bHash);
$ v: H; L& q- p( } - if ($aL !== $bL){ return false; }
$ v" @% Y5 G/ x. u9 A -
3 X* B/ a4 t1 b9 ^% v$ t - /*计算容许落差的数量*/
2 v; l2 Z8 j/ ^( W, ]4 y - $allowGap = $aL*(100-self::$similarity)/100; % D! F( M+ i4 Z( U5 t2 U% {8 j
-
W, i* P! \1 P1 A, @3 H# G- n - /*计算两个 hash 值的汉明距离*/
4 M2 n8 _* l J' W V- Z, n, A - $distance = 0;
5 a% G" @! C4 ~# { - for($i=0; $i<$aL; $i++){ ( `9 h' ?, O0 @
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
; W$ d& g: S7 j8 H H - }
1 t) k6 @% n+ f) [) w: V" [ -
5 J; z+ m M0 L: ]" F - return ($distance<=$allowGap) ? true : false;
9 X2 e8 P, x9 p/ A5 M$ j: d - }
5 F( V- H# [7 v- |6 M! N - 9 Z. h- }1 h5 k( o) R! Q( Q
-
2 f9 _5 i' S) i; ]7 ]0 ~. h3 q - /**比较两个图片文件,是不是相似
1 G) ?" `) [/ S8 B- _$ n* Z; m" G - * @param string $aHash A图片的路径 & L6 a0 ]/ J! c8 _- u
- * @param string $bHash B图片的路径 - ^( S- L+ @$ |! F6 v- _
- * @return bool 当图片相似则传递 true,否则是 false ) m3 U* g: E/ v$ s! E
- * */ ' w3 z4 c+ w' A6 x& r
- public static function isImageFileSimilar($aPath, $bPath){
& m/ t" ~" L! _" r p; v3 N* Z, ?; C - $aHash = ImageHash::hashImageFile($aPath);
0 q2 Y' Y0 j3 s, ]5 k# E, } - $bHash = ImageHash::hashImageFile($bPath);
9 E7 \7 x. N5 w5 g# _! q - return ImageHash::isHashSimilar($aHash, $bHash); / P g* ?8 Z/ R- O6 m
- }
* L9 p+ R+ e0 t& v' @9 H" Y -
3 m- ?4 S' _; a7 t, F+ l - }
! P$ d$ \4 h7 q. \: ^$ X* N1 ]0 C9 J
复制代码
5 m/ V1 z8 `6 W6 x0 q* V" P
/ n6 v& {# H, `$ N1 k9 x2 K |
|