管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图& c5 T+ C1 f2 G. u; u ~- R6 M
$ C2 r; a7 N' v1 s
/ C5 P% q8 E. ~- E# ^8 J, _由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
" X& W: u7 G! ]" r! F- <?php 3 h# \( ^7 U# j
- /**
7 E; N! U: a7 P - * 图片相似度比较
# j6 \# ?6 y- ]* P. _( a - * . U) p' O7 Q' @
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; : e9 i5 c& _* l
- * @author jax.hu
; K* R% u- {' L& S5 } - *
m' G/ H- U( |6 s5 f5 @ - * <code>
! c; ^ o4 t6 _4 U( J+ ~4 T- T) [ - * //Sample_1 - u' K# i+ M2 Y
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 0 B/ l3 k; C4 v7 d( V& w, L5 R r
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
# O# q7 h4 c; y6 Z s* o0 z, Z - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
4 [9 T/ O; @3 [ - * 3 ~9 O+ E. ^& e( b" i( c0 `
- * //Sample_2 ! k* _$ L0 L0 X5 i1 i$ u [
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
" m, P# {+ r5 N, V4 }3 ] - * </code>
; L2 {. J( Q# m8 T# J. A5 } - */
7 j( _. {* r0 b% ~, V9 F9 z$ r0 k: d - . @- Z( J3 S6 R E
- class ImageHash { 5 s5 V! ^" ^+ g! j
-
! |; q1 X- c7 m3 D. N - /**取样倍率 1~10
" b; q/ z) N1 F$ _# S8 Y, J* } - * @access public
" I7 [- _1 e7 [& P- _1 S# V; a - * @staticvar int
' r7 H3 T- d1 U+ | - * */ . B% ~2 T1 ~/ H" g; H
- public static $rate = 2;
. o2 F6 `; W% x8 x7 a' W9 Q) ^# m -
3 i/ Y2 i& ?! S' a( {3 P, G - /**相似度允许值 0~64 8 ?" l& {4 A" s; Q
- * @access public
5 I% P& m0 a+ W) [0 Y4 r - * @staticvar int % i1 ]# t6 M2 a" R' Q0 N' t
- * */
* h8 n# N# b: V - public static $similarity = 80;
7 ^" [; Z* L9 ]. \ - - }9 z9 @! u9 U
- /**图片类型对应的开启函数 u E9 s. i$ Y6 _
- * @access private ) F5 s! H" H" z% u" w8 F6 Q! U- l
- * @staticvar string
" W; D5 q+ k. U! y7 y/ B# X! B# P9 U- W - * */
2 r& q; x/ B+ x4 p$ V - private static $_createFunc = array(
L9 `0 J- J/ A, t3 Y - IMAGETYPE_GIF =>'imageCreateFromGIF', 1 }* r2 W0 B7 R$ n9 m7 F
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', # F' m7 t+ h; L. k7 l6 g e0 \
- IMAGETYPE_PNG =>'imageCreateFromPNG',
& O3 Y' D2 M2 Z* x - IMAGETYPE_BMP =>'imageCreateFromBMP', * I4 ?! M' s7 ~; O% V7 c+ k% U
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', $ x. ?5 N/ Z, x/ \0 A0 ?
- IMAGETYPE_XBM =>'imageCreateFromXBM',
) Y$ q' X% L8 S3 ?) J+ e - ); - n, R% P+ q$ @) m1 b: P% g9 G u
- / b& B: @+ h$ j$ c4 v2 _7 e: l9 }6 V
-
4 y; Q( Z, R3 I$ V+ a; e - /**从文件建立图片 w1 r' k; V$ g( _# d! n! u6 l+ U* m
- * @param string $filePath 文件地址路径 , Z. k1 @/ n- a, Q. o
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
3 z" k: L H+ g1 ^$ z - * */ ; }: d& A- M7 V9 H0 s" v1 W
- public static function createImage($filePath){ $ q; H; y2 L# @+ K) S
- if(!file_exists($filePath)){ return false; }
S) Q* V5 N F! W$ | -
f" e) T! h7 N9 Z$ W) P! L - /*判断文件类型是否可以开启*/ / L, F+ |: G8 s: c) f& T
- $type = exif_imagetype($filePath); : {7 f7 e ?8 c- ^4 S
- if(!array_key_exists($type,self::$_createFunc)){ return false; } - N7 h1 Z$ C& n/ V& V7 P. d5 f
-
- o. L1 L x! U. x. o0 P. T - $func = self::$_createFunc[$type];
' \$ }5 a$ ?% G7 S - if(!function_exists($func)){ return false; } ) B9 ?! Q6 I; Z: T! J+ O
- ; X' U7 \: [ H0 L7 \# Q n& u1 J- s
- return $func($filePath);
5 ~# K- g+ z. ~1 Q- t- Y - }
7 v4 A* U0 g+ U - * ^+ u9 y$ Z' O3 o: U( E2 ]
- & n( a, w8 a% N$ ^. R
- /**hash 图片 Q: x4 M9 l& o `0 Z
- * @param resource $src 图片 resource ID ) `* u( B9 g c; c/ R6 Z* V; H
- * @return string 图片 hash 值,失败则是 false
" a4 @6 T8 d# E6 v - * */ 5 K; T! D- e- o% v( @+ s Q
- public static function hashImage($src){ 7 @9 ]% n& N. b7 K+ d
- if(!$src){ return false; } ) X+ a0 N a' X- O" x2 u- Q5 V
-
$ {0 o- r# Z7 n - /*缩小图片尺寸*/
$ G$ X$ e' N5 U# r9 ]% \ - $delta = 8 * self::$rate; $ H. h9 l F/ q7 s v. ^' [/ X
- $img = imageCreateTrueColor($delta,$delta); $ m& c d: z7 k8 I5 B
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
6 b: ^1 U* n0 V3 @- A - 3 T4 T3 d, S2 H+ G7 Y' C
- /*计算图片灰阶值*/ 7 @+ z+ ~2 h% I- K
- $grayArray = array();
! t7 v; Q* e2 b8 N8 G. O. z - for ($y=0; $y<$delta; $y++){
, m& {- R4 T- `" g: T - for ($x=0; $x<$delta; $x++){
: {5 e0 _6 _4 K5 |4 N& I$ e - $rgb = imagecolorat($img,$x,$y); 3 q* [ m( |2 Z3 ]; p
- $col = imagecolorsforindex($img, $rgb); 5 P8 F5 P, i7 z5 p
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
9 Z: {' b5 R. {- g3 a - & \ k. \+ o. m$ B8 S
- $grayArray[] = $gray; 1 a6 ?8 m; Q5 p B! M
- }
8 N, A" T/ w! E, n. b* C' P% C - } 7 |1 U/ ^& \) L' d" B+ |
- imagedestroy($img);
J1 y2 E/ T' @7 e* ^1 y3 { - 0 S& E: f* i% S) t5 {$ U8 S
- /*计算所有像素的灰阶平均值*/
. Q: M( y& k, G3 R; g, D; x - $average = array_sum($grayArray)/count($grayArray);
' m/ T" }4 Q$ V( { -
8 B' _9 F" `/ e. v - /*计算 hash 值*/
, o3 u, q# E7 O( {, f - $hashStr = ''; " p' D# v" [- {' W
- foreach ($grayArray as $gray){
- a7 l! k/ f3 n0 M' N - $hashStr .= ($gray>=$average) ? '1' : '0'; ( L% |2 H6 @% l; L5 N! m
- } ! h% n; @& x' \% k# r/ f
-
) j+ w! u3 w$ A* _" z2 k0 _ - return $hashStr;
& b4 K2 i @# {0 A5 c8 l+ Y. ?3 q3 r$ z, @ - }
+ L# W, S6 q1 S -
7 @$ h% s! q9 I, C: i - . {; l$ v, q) w) [" B$ Y
- /**hash 图片文件
; i; O7 d7 x b5 U$ U3 s - * @param string $filePath 文件地址路径 ; G, @* M: z+ H0 s# b& c/ N0 O
- * @return string 图片 hash 值,失败则是 false
k+ w% o- j& B- P6 Q - * */ . e- v2 Q8 u9 t
- public static function hashImageFile($filePath){ ' ?3 H3 W8 J. c+ O9 j5 ?- T$ a
- $src = self::createImage($filePath);
. Z3 b9 h. x" A' k' {; H - $hashStr = self::hashImage($src); 8 [; E. F7 T* M
- imagedestroy($src); , ?- K8 m C2 u% m, J" B {
- 0 I; E; m' O, U8 H
- return $hashStr; ( X" Y' N7 M5 a$ ^. {. }
- } C' k- _7 A% ^! f: j
- ( w/ f4 b3 j5 l" Z2 i+ e, ~0 g
- , U: R, N& j& g, G
- /**比较两个 hash 值,是不是相似 / J: d: B- `0 S0 A' Q
- * @param string $aHash A图片的 hash 值 . N; g% w0 t# L P8 ~
- * @param string $bHash B图片的 hash 值 - U% j" ?) v' e$ c, C
- * @return bool 当图片相似则传递 true,否则是 false
5 \( _, O/ d i$ |7 \+ N$ ]* m - * */
) K+ l- G6 P1 d1 j' G - public static function isHashSimilar($aHash, $bHash){ & g; |" @: R# X; n5 L
- $aL = strlen($aHash); $bL = strlen($bHash);
1 D( j: C7 ]3 I) l" B/ | - if ($aL !== $bL){ return false; } 4 Q* J: y, j& v2 V
-
7 ^" m' g0 v: y* @ - /*计算容许落差的数量*/
% q2 V T3 [, P - $allowGap = $aL*(100-self::$similarity)/100;
; @" t( ^' Y9 w, J6 ]2 n -
; A+ @9 }% a( G D - /*计算两个 hash 值的汉明距离*/ & x9 G9 S% H& K" [
- $distance = 0;
" I+ J& N2 _& t7 X - for($i=0; $i<$aL; $i++){
3 L2 w/ c. j3 h N7 k - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
& J$ S/ _$ @9 s: J; K - }
$ s& L' o3 o4 e$ j -
$ n, v2 _) r+ e$ M) T. H - return ($distance<=$allowGap) ? true : false; * Z' S( G! I& j( }
- } ( Z6 N+ a2 a2 g5 N. p% v
- + @ H* K: M% X. p9 Q/ ^7 K+ u
- 7 N9 H! l+ W5 ?; p
- /**比较两个图片文件,是不是相似
# w! \! e% D# G1 ~ - * @param string $aHash A图片的路径
- K# W1 q' E( W& s% _! \ - * @param string $bHash B图片的路径 6 ]" W# R8 }& t: `
- * @return bool 当图片相似则传递 true,否则是 false " C2 s1 k Z7 S
- * */
% E' M- B' X8 C* m% n' n - public static function isImageFileSimilar($aPath, $bPath){ 5 G5 b6 l9 O& ?% b H1 q- `
- $aHash = ImageHash::hashImageFile($aPath); / T \9 q+ w6 p. ?3 h1 m
- $bHash = ImageHash::hashImageFile($bPath); + _% c, z! K: G( h. J# _+ s
- return ImageHash::isHashSimilar($aHash, $bHash); - ^8 q0 K+ W1 ~& g9 R8 w% ]4 v
- } * B! G: r N* {: J5 h0 y% V8 \; _
-
5 _7 B) z9 q# b" c( m4 s3 S& s - }
- L) N) M5 H) }3 V+ D5 N7 t# t1 w
复制代码 : v& j# k g3 v# L! P
2 z. \! E, \3 K: j9 x6 s7 I s; ?0 q
|
|