管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
" j h) t0 i, m: C
+ m" s8 J" @7 P) q
0 Q% k* r; B# K由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
4 d1 X( _" m6 E- <?php 0 J& \! |" Z, M/ Q& E2 g
- /** ) n) K( s, s& R; e4 ` d
- * 图片相似度比较
% h5 d( U: j6 W3 O - * ' i6 {& L6 `3 ~
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; . E$ d5 I$ f: k/ I0 o$ ~
- * @author jax.hu
4 @' v9 Z P l- j ?/ r - *
h3 m) ~2 f" m/ G* w - * <code> 1 k& j" b5 n" n! m$ t
- * //Sample_1
4 {& z9 @' M! m7 B - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
, e* {) G8 O% j0 Z( C - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ' |. S3 y1 q$ k
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
* H( n, A2 a& { - *
" t; V( i$ T m, `: t( d# L - * //Sample_2
9 Y' O' x5 T4 w: g( g - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ' F3 {" w" h3 y* ?
- * </code> 1 R1 t1 Q, F) z* b* B# x
- */ 2 I% I0 J$ K5 g% ~$ P
- 0 t3 o/ |4 `, h) @
- class ImageHash { 1 G. Y# v1 o6 R8 j1 W5 k) k* k
- : N1 b. {9 @; S/ V3 B$ F* X
- /**取样倍率 1~10
% y0 |* U/ j2 J. p, C) g - * @access public
- K- H2 I* u; K: _' W5 W - * @staticvar int ; }7 `" A d/ f9 s4 h
- * */
4 P. w5 L! g! j0 {% g - public static $rate = 2;
8 n5 f4 m, f0 R( R0 J( a/ m -
3 P! z% N6 ^) `% S - /**相似度允许值 0~64
$ T8 ]$ K- I/ r7 d - * @access public 0 K2 W0 k2 ~( e; |1 s9 M
- * @staticvar int
& f0 d# c: g! M3 o9 W5 z0 `$ o" q - * */
) z- }8 D" s* L Y; k% B - public static $similarity = 80; ' n2 N( Z4 N" X
- / u N- w# C0 B6 p* ^: Z1 W7 k
- /**图片类型对应的开启函数 2 v8 U$ D$ _& e/ Y, Q9 K
- * @access private
& y) q) J$ C) S+ _ - * @staticvar string * [; `6 X9 W& q4 c# z7 X
- * */
% W' x; v0 L2 Y1 t# n1 Q" s - private static $_createFunc = array( 8 \0 @' a5 J0 c; i+ i* R1 h v
- IMAGETYPE_GIF =>'imageCreateFromGIF', 9 g n0 f9 o! G! S' h& _& d+ Q# C
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', " C* P; P& c& |' G
- IMAGETYPE_PNG =>'imageCreateFromPNG', ) g) g( f4 W* I
- IMAGETYPE_BMP =>'imageCreateFromBMP', 5 a7 b9 ]* a# D
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
1 Q7 A2 v; T5 z4 k( S2 w/ T - IMAGETYPE_XBM =>'imageCreateFromXBM',
1 Q, U2 V. o5 o) X - ); ; R6 S$ Y. }& |" ]
-
! C* y5 [+ e' H% y) p9 l2 E - * T1 b+ ~7 J- Q9 ?2 @
- /**从文件建立图片
& b; U: I# i6 N K/ d - * @param string $filePath 文件地址路径 + v% C$ p3 i) P: @! x
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
9 h% _, V) [. { - * */
7 \4 P) H' I% ]" I" D1 R - public static function createImage($filePath){
7 ?$ x4 I$ @. O! r - if(!file_exists($filePath)){ return false; } 4 L6 z' X# e# w9 p! H7 d$ `' s4 J
- : H; t9 Q/ v% ?! ?
- /*判断文件类型是否可以开启*/
, t/ M" S- [5 L - $type = exif_imagetype($filePath);
& b; D4 C# [( }2 R1 ^. V! W - if(!array_key_exists($type,self::$_createFunc)){ return false; }
' V8 o O9 d$ R2 q1 A - & J* x6 N& i" { }" v9 n/ x, q
- $func = self::$_createFunc[$type];
9 m0 O! @. k! k: { - if(!function_exists($func)){ return false; }
& `8 t3 n/ C. J; v7 L1 z* l - ; j+ O" C- |7 B& @' e! F; y$ D& J
- return $func($filePath); - O j" s- X; i0 t
- }
1 u3 Y4 z% Y$ E5 S( A( v3 N; h -
* V( j7 u5 U$ `, A; g -
* q5 M! Z$ y" b3 S0 a - /**hash 图片 % \. v$ t9 D% W D' d4 u8 F% \9 F
- * @param resource $src 图片 resource ID ) }; d7 m' x. o: k
- * @return string 图片 hash 值,失败则是 false
) C* p, Q" J2 E; t& s3 C - * */
( }9 ^3 O4 g& u2 v0 \; k' f - public static function hashImage($src){ 2 ^1 o* m# w7 P0 A2 [6 q
- if(!$src){ return false; }
& z! [, A6 _1 [7 h* h, f: E -
' i. @) z9 G2 ^: L9 a3 L3 c, T - /*缩小图片尺寸*/
7 u9 [- A; c, L- {6 X+ ^ - $delta = 8 * self::$rate;
) y) W% G$ v* P* z! w - $img = imageCreateTrueColor($delta,$delta); 2 e& m4 o ?0 v& ~& U! q( @! X
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
+ x5 |4 u/ a7 G. n" n1 L - 6 X0 M0 F' O+ B
- /*计算图片灰阶值*/
5 K8 G9 x- t; n# l1 N; ?& s x - $grayArray = array(); ) {7 I$ Z9 H' k+ r" L+ N
- for ($y=0; $y<$delta; $y++){
# O1 a2 u/ R: g$ h - for ($x=0; $x<$delta; $x++){ G. J- G3 h5 N V% s
- $rgb = imagecolorat($img,$x,$y);
$ I0 w4 p, h3 s - $col = imagecolorsforindex($img, $rgb); , s: i% i4 o/ ^3 E1 H) i
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; & B% y- V8 X* j3 W3 C! ]/ A% T+ K+ j
- ! Y2 W7 J* k a$ z. C4 A- |
- $grayArray[] = $gray; " C3 a$ \7 R1 v& G2 W9 i
- }
. y! {* C( d. B+ } - }
0 t) \) U% L8 ], P. |+ g6 ` @1 r - imagedestroy($img);
4 s0 A( |+ X& m% {6 ^ -
, L! l; ~7 B2 `& h! G$ J/ [ Q - /*计算所有像素的灰阶平均值*/
. f) R0 p) m; |3 I0 T: x- E - $average = array_sum($grayArray)/count($grayArray);
. x# a/ w/ ~, ^% {+ p9 l% m0 g3 i- {- z - 1 H; X2 I7 `9 L# R! t! t7 j
- /*计算 hash 值*/ 8 G1 N! T7 [+ }
- $hashStr = ''; : u: B. z& G. L1 I
- foreach ($grayArray as $gray){ ( m2 ^! } y6 Z" [; E
- $hashStr .= ($gray>=$average) ? '1' : '0';
- |# ~! J' G! n1 c) y0 {8 W+ H$ G! K - }
' T, O) e d ~( ?; p -
/ q1 _1 j' O% t7 I - return $hashStr; * |8 O& Z! I) K2 o$ ?
- }
& c0 _. ]3 n B# P* j' @ - o% V! P) e2 @' s! W% C; ~
- + F t: _( r/ ?5 _
- /**hash 图片文件 # |* y9 }3 m4 j+ o1 N0 ?0 {0 a9 J
- * @param string $filePath 文件地址路径
# G# W* @0 y. x0 |1 k: \$ s/ k9 c - * @return string 图片 hash 值,失败则是 false
# I6 j8 W( F5 P! a1 n# P4 x - * */
0 }" g" v* Z1 ` R* [# f& W- M - public static function hashImageFile($filePath){ / n3 p& Y9 z5 y% [1 X
- $src = self::createImage($filePath); ! c1 ]1 o! \. {
- $hashStr = self::hashImage($src);
1 ^% z$ R5 w7 B! U0 v" Q1 q0 V - imagedestroy($src); ' Z! G. G: H7 h* w0 h
-
2 a U2 Z' z9 v' S - return $hashStr; # R4 z. D2 _3 U( x& D* \" F" G A
- } 6 \! K L& j6 E
- ) r9 Z) d0 u- v/ h- H* Z0 Y$ Q. G
-
' Q3 o# _" s% u3 p, b$ I* S - /**比较两个 hash 值,是不是相似
% N" W# K, Y9 i1 u" S( J - * @param string $aHash A图片的 hash 值
7 z, y2 b+ @) _6 I- p# U - * @param string $bHash B图片的 hash 值
; O S' w( D7 a; G3 n - * @return bool 当图片相似则传递 true,否则是 false , X9 ?5 _ ]% M! U- S+ @8 s- r
- * */ r) ^ \$ n* p: m9 J$ [5 f* S
- public static function isHashSimilar($aHash, $bHash){ $ {, V& D% I' L9 W, j
- $aL = strlen($aHash); $bL = strlen($bHash);
i5 I2 X4 K/ }0 `' @7 a7 ]/ |! R - if ($aL !== $bL){ return false; } 6 ] v1 E- \3 z
-
5 W: w0 y' }8 E1 v/ ` - /*计算容许落差的数量*/
& b) H* ~9 A6 f - $allowGap = $aL*(100-self::$similarity)/100;
% `+ T7 \1 ]' Q/ R -
) I% t; Z" U& Y+ N: [6 t - /*计算两个 hash 值的汉明距离*/ 9 i7 I8 C2 Q0 k4 Q9 q6 }' v6 ^
- $distance = 0; ' {1 r4 C+ Q; c; |- T
- for($i=0; $i<$aL; $i++){ 9 g$ b3 k! l/ I
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
: b$ _1 s- L7 y8 e - }
# S, w2 A% A. H9 u% m ]2 v" c8 ]8 H C6 o - ( }4 \ L& r7 G% ^& k+ v2 i
- return ($distance<=$allowGap) ? true : false; - e1 ~6 X+ L. A: @) \4 J/ ^
- }
7 l6 \: B) r v -
* k: j' @+ w0 n: l# l# G# u -
3 K5 F2 l7 \% v7 Y5 d1 X4 r - /**比较两个图片文件,是不是相似 $ S; `- @) B( v& h5 [6 Z- F! {
- * @param string $aHash A图片的路径 8 W% m7 D$ Q. w$ F2 l5 L
- * @param string $bHash B图片的路径
/ P. w' ]% R0 O: H% v x- g% K; _ - * @return bool 当图片相似则传递 true,否则是 false
+ K) c6 A( U- o) h - * */
( t0 {4 u8 @) ]7 L - public static function isImageFileSimilar($aPath, $bPath){ 8 g& p1 A3 @) m" |% b/ }
- $aHash = ImageHash::hashImageFile($aPath); # A3 V4 h8 I# Z9 F0 O$ P
- $bHash = ImageHash::hashImageFile($bPath);
) t7 L/ g$ U6 \( S' x* G- d - return ImageHash::isHashSimilar($aHash, $bHash); - p% l* E7 Q5 ]5 t: J8 U
- } ; M* F6 d g& V+ O
-
7 j e. Q' A: L9 ^ - }4 @9 U, |* s! P! H" t* D2 l; y
复制代码 % p* H; C6 x: \: F/ H: c! e
. `, N" q+ ]% t) |
|
|