管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图! H) }. `- ~2 y. |8 b" O
% u. A, ~1 P( H9 I% q: `6 H! h8 m# t' L( x/ t9 x
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
/ X0 M1 x- }$ n* h9 p# s- <?php k6 K' B! P% k; r. Q3 ~* Y
- /**
8 U4 F% {" w7 [: x: f$ r% L - * 图片相似度比较 " S8 y9 A7 n; L8 g. f6 m- b
- * ( L! R( ?. Z% x, u, `
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ; Q8 B& z# n' b: \* W2 g3 T
- * @author jax.hu 1 ^' K/ w7 V. `" a
- * / `2 k3 s% ]5 t) k+ r( ?* F: r S
- * <code> 5 j& G" q. o. f
- * //Sample_1 1 Z5 r2 a: h$ P
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); & \7 I- K' b- o' g( R- O4 m; N
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
) {" {- N" l- x. i/ A: k - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); $ j/ m# K7 Y' {" s# P
- * 8 t" Z3 D# g6 S1 c. C4 ]' T a
- * //Sample_2 & |9 ~; e+ t& W v9 k
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ( H* o& l1 y4 z6 Q" k
- * </code>
( F' F/ V8 u% c. o - */ 3 G. b3 [# R* u5 [' O
-
" I$ Z2 C. M4 {2 D8 G3 k( X - class ImageHash { . T" z+ j# Q1 O. T: b6 C
-
0 G% q/ N! [) \ - /**取样倍率 1~10
7 `8 B/ J7 u0 u$ s) q' M - * @access public 5 m, r5 h- S. A8 L, ~% P" S; Q( l
- * @staticvar int n) R" ]& U; `3 g& a: F
- * */ # g2 w# h, Q+ Y8 u$ l" [8 g
- public static $rate = 2; - |' |( K' a" ^8 Q# C. H* T/ b
- + Z2 f( k; B/ ]2 j3 o! ?
- /**相似度允许值 0~64 ! \+ s! S* G; M) ^$ d+ m! I
- * @access public
( M8 D- _1 [3 y B# l - * @staticvar int 8 J( \1 P0 f H1 U
- * */
/ N) C8 z7 y/ e: h" I- X2 ?* R/ o - public static $similarity = 80; @, ~8 Z. m4 C! z
-
/ Y# H% M% f4 g6 k. a - /**图片类型对应的开启函数
0 L5 P& V7 D+ e1 X/ w# @ - * @access private
; W" q, r# O) n' N# p' b - * @staticvar string
. \3 H; P3 ]) Z) X% K - * */ ; q/ q7 n1 H# p6 Z/ T
- private static $_createFunc = array( 7 y. g/ r4 B% ]+ _
- IMAGETYPE_GIF =>'imageCreateFromGIF', , k6 q7 }' |7 x" ^* Z7 t0 I
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', 5 \$ h$ r, d4 _1 ~
- IMAGETYPE_PNG =>'imageCreateFromPNG', 7 U5 L7 C$ P: S* L2 h
- IMAGETYPE_BMP =>'imageCreateFromBMP',
0 x# _% y2 `5 c' q: E - IMAGETYPE_WBMP =>'imageCreateFromWBMP', ( ^4 O! A& W! E, {
- IMAGETYPE_XBM =>'imageCreateFromXBM', , e9 v4 l- [0 a
- );
5 S2 m# n9 Z: [# z -
0 c* D `! D" A -
- U. W/ @, a7 Q8 z2 a! l( Q - /**从文件建立图片
/ U) K2 s- v2 M8 E r o1 F6 y: { - * @param string $filePath 文件地址路径
9 F/ F$ D4 \$ }( w' Z; G9 @ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 2 u7 o, @1 \) J& R
- * */ 5 n9 d! N' a: R6 K3 x& M- `
- public static function createImage($filePath){
% O# b ^, G/ d: T+ d - if(!file_exists($filePath)){ return false; } 1 u4 o# e, s0 V
- 5 L: y! a0 {) z, Y. I7 J! W
- /*判断文件类型是否可以开启*/
# q; R) N6 ^; j! A' A - $type = exif_imagetype($filePath); 0 n. N+ w) X1 y! m* h
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ' u4 o' T1 P4 z K
-
1 x; d4 P, N" G - $func = self::$_createFunc[$type]; 2 w8 k- ]' G6 m
- if(!function_exists($func)){ return false; }
! T# E/ o% F" v; B -
/ n5 u% T' _$ T4 U3 V - return $func($filePath);
O& n* @8 I1 B3 l) I' J - }
4 N' s+ ]; v2 x- F - 1 C! n0 a0 W- z- p# W+ z! c
-
! m9 c6 ?/ x, { - /**hash 图片 % } ?1 x$ x8 k4 _% c
- * @param resource $src 图片 resource ID ( {( A8 B4 p0 U
- * @return string 图片 hash 值,失败则是 false ) y E8 K/ q" v: E6 i- o
- * */
" w4 {3 _. I, f9 T( _; Y - public static function hashImage($src){ 9 G! U6 \# _- h: Z- x
- if(!$src){ return false; }
, Z3 J; B. a9 H" n9 N0 Z- j; _ - 2 D% x1 k/ E0 |: l7 s
- /*缩小图片尺寸*/ 0 Y. u' ?; ?' y9 o* A
- $delta = 8 * self::$rate;
! }+ Y: B: f" o) I% y; w. c: N - $img = imageCreateTrueColor($delta,$delta); 0 A5 \7 x1 L$ T( H1 c
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
5 E" o; W' L! x! ?# j' ^) a - " J* g% h4 ^2 z* |4 N$ R
- /*计算图片灰阶值*/ / F. _6 _% \* S. f$ {
- $grayArray = array(); ( D: T9 M; a1 w! S& ]7 h6 B
- for ($y=0; $y<$delta; $y++){
) g- y9 V7 ], D& b# I: ]. \; J' C - for ($x=0; $x<$delta; $x++){ ! a. e6 r6 O4 U k( w, T2 E) A
- $rgb = imagecolorat($img,$x,$y);
4 C+ u" N& l3 R( e) A1 X - $col = imagecolorsforindex($img, $rgb); + u2 m1 p U C( ^$ v7 F" ~ N; f
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
1 O# i, f1 c A$ a) u5 W, j6 S5 r/ p: x -
0 D4 Y: t6 x# x7 j& y - $grayArray[] = $gray; z0 j& y- M, e+ X. c! y% {# W) j; @
- } ) _ {. k; b, \. j
- } ; k$ I/ \/ |( n6 y9 |* Q! w- ^
- imagedestroy($img);
6 ]. V, `8 I. b4 O# N+ O -
' P5 B6 m$ s1 s- j2 ~ - /*计算所有像素的灰阶平均值*/
* k' T8 U; L) U# L - $average = array_sum($grayArray)/count($grayArray);
9 T" t0 P4 }0 P# d3 t% I - 9 [6 j R6 I( h) r# C0 C
- /*计算 hash 值*/ 2 @6 n0 m8 J. a2 m. C" c
- $hashStr = '';
7 b! S! v! I: G - foreach ($grayArray as $gray){ 8 G# R4 ~1 K1 B& p5 V
- $hashStr .= ($gray>=$average) ? '1' : '0'; : J* l# e5 X; }
- } + s. x7 i. H5 W% F) G+ y' p
-
* U9 t8 p4 Q! K( E4 Y - return $hashStr;
3 Y' I9 b. ?( J2 P g% ]$ \ - } 8 Q* Z4 G7 `1 `/ b/ O" e8 ?
- - [" s& V# n1 K
- & S9 a' b S; _$ o9 @
- /**hash 图片文件 , N" ^. a% w- Z3 y3 i0 u
- * @param string $filePath 文件地址路径 * O) D$ }5 Q- O& g7 a
- * @return string 图片 hash 值,失败则是 false
6 U% z, F* P3 u - * */ 4 z- G+ u9 K1 E$ @
- public static function hashImageFile($filePath){ B8 f) w* Z Q& f9 J$ }9 m
- $src = self::createImage($filePath);
! w6 d. a# r7 d) L - $hashStr = self::hashImage($src); 0 N3 W! o4 I- [: S- e
- imagedestroy($src); ( K" u* ^7 i! M# `" s
-
+ {: f: C+ E; @1 _) O - return $hashStr; ( ]8 r, w: ^/ {5 ?$ C/ |3 p
- } 2 f' T8 [# C0 k
- & p; S/ I1 C7 v O& r' F
-
7 E; v- M- f/ I( e - /**比较两个 hash 值,是不是相似
/ g" [. u& Q& t0 _8 `' H - * @param string $aHash A图片的 hash 值
& e: r- w& i: C - * @param string $bHash B图片的 hash 值 + ~. O1 {2 m0 l" u$ d, E7 A2 P
- * @return bool 当图片相似则传递 true,否则是 false
9 m/ r( F/ h( T& a7 o2 g - * */
- T; Q( R, ~. C: v, I - public static function isHashSimilar($aHash, $bHash){
$ N- c) F% p9 J6 U! A - $aL = strlen($aHash); $bL = strlen($bHash);
8 U* V* n" r& J1 g5 _1 o5 o+ q - if ($aL !== $bL){ return false; }
' c9 H2 c$ J |+ B - & H6 X# `& o. w# b$ ~
- /*计算容许落差的数量*/
/ G& |3 Y9 f% o( Y - $allowGap = $aL*(100-self::$similarity)/100; , ~* u6 A' X% s( N- O
-
1 L9 j. l! [8 D v3 g4 V- K* b; F - /*计算两个 hash 值的汉明距离*/ * B0 `7 M) a3 r
- $distance = 0; ) Q& M+ F |5 o u9 t- E1 M0 h* v
- for($i=0; $i<$aL; $i++){ & n' m0 I& b+ Y
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } ) o# L- ^, A8 Q! E* q
- }
' Y/ p; A# ?$ c& y* U$ w6 U+ x - . w- u9 q! O1 E1 _4 q' t/ _
- return ($distance<=$allowGap) ? true : false; 2 z3 k Y V" v
- }
2 L Q! \9 T x) G e8 }* ] -
) B: z+ |7 f! f: b8 n - # w) s4 Y5 y" e7 U2 M
- /**比较两个图片文件,是不是相似 0 d5 B% W+ F8 @7 h: w
- * @param string $aHash A图片的路径
4 N4 U7 y, e [ - * @param string $bHash B图片的路径
1 V& M1 E4 f& [. V( b$ A - * @return bool 当图片相似则传递 true,否则是 false
9 F: r* V; |+ B - * */
# x; w3 L7 x* r - public static function isImageFileSimilar($aPath, $bPath){
2 |; P" H! v1 t0 O2 I - $aHash = ImageHash::hashImageFile($aPath);
( Y9 a) @) d4 i4 y; l& L$ [1 @ - $bHash = ImageHash::hashImageFile($bPath); " d+ G1 ]8 v5 h! F1 ^+ B: D5 S7 C
- return ImageHash::isHashSimilar($aHash, $bHash); ( Q; ?5 o! p7 r3 I8 q
- } : O- g: e1 t: y1 c
-
. _' R+ _) Q5 j7 D* W1 l0 T4 h& E - }
; D( ~ b# S- s5 x
复制代码
0 }5 i [7 n4 e& O Z m" V) D
. b: N* |" u- L4 w |
|