管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图) Q6 I6 b$ C* I! c. y- @. @
- u i' r& b1 w
4 X6 I- G/ g' O; }由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。, m5 y9 p, i$ j$ e( }
- <?php
) K# C+ y. }3 J% f - /** : I. t/ @4 H. F
- * 图片相似度比较 & E2 |+ Z Y/ a
- *
( [4 y6 c( h5 L0 p: R, n; {& g7 j - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; + P9 G1 y& a+ r4 j/ S
- * @author jax.hu
! B9 e" w, z" }$ \* {5 S - * 0 C ]# ?, J$ j1 ?3 P" u
- * <code> 1 ~# U6 p8 [- O$ }5 o- E1 B' u
- * //Sample_1 5 A1 t9 t( h0 y. J. a, A, w0 Z% j3 \- Q
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 7 t! P% @3 }, M
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); - H7 V7 s% ]; y
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
7 W* D& \1 ] Z# |9 i( D/ T - * ) I; F6 h* A% j
- * //Sample_2 / J7 g# _% d, ~" T3 R
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); " Z4 D* o3 `0 S, j
- * </code> # M' o6 A& `9 d4 u! M; p& L0 U
- */ 4 ]- t9 a) v1 _
- ; u! r9 g! L9 f' n% U) U; f: l7 V
- class ImageHash { ) l+ s6 z: A0 l. A# k s2 }0 o
- ! |; _% _3 I, [4 l, ~
- /**取样倍率 1~10
; D& `/ s; f; U/ l, M# M! }+ d1 R% Y, \ - * @access public
3 e9 p- }/ d+ w7 a - * @staticvar int
# ]+ Z% s; M9 e' u. S - * */
3 @$ G, P5 q+ C7 @ - public static $rate = 2; i; o3 H5 b' E/ o) l u
- ' P5 L/ }' K! g& l, e
- /**相似度允许值 0~64
- }- M( d# I/ K X+ Y - * @access public 4 ~3 O9 n, u, M
- * @staticvar int 9 d; q6 M9 {* s8 F6 ?* T4 i; j: y
- * */
* f' Q" R5 r0 ^9 b1 y# N4 ]/ T - public static $similarity = 80;
& A, y8 o- x4 _7 E -
. I5 z, B" N; G+ e5 ]6 X - /**图片类型对应的开启函数 N/ h+ v j5 g$ e
- * @access private : P" z* f7 w% d8 |% L# v
- * @staticvar string / ?2 X! z1 x. `
- * */ ' B# W4 q/ j' E
- private static $_createFunc = array( 1 R/ q. Z5 B k1 P) G0 }7 F
- IMAGETYPE_GIF =>'imageCreateFromGIF',
$ t/ D' G- Q4 A& O, J" i - IMAGETYPE_JPEG =>'imageCreateFromJPEG', . ]: J4 U' H" g
- IMAGETYPE_PNG =>'imageCreateFromPNG',
7 y. c3 A; O; n) f3 E) v - IMAGETYPE_BMP =>'imageCreateFromBMP', + S1 D X' T4 `3 z8 h6 k
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
1 X5 f8 j4 T/ T+ J: Z ~ - IMAGETYPE_XBM =>'imageCreateFromXBM',
# x+ j9 Z9 l( f$ b4 I - );
/ j/ c0 M( E/ Y- i8 F5 ^, V' ?; Q- n - + O; f4 {9 N9 W: W# m' x9 O
- + q( q5 L# p7 x
- /**从文件建立图片
1 C: F; L4 G; L" K - * @param string $filePath 文件地址路径
0 ]! x1 d/ N: r ?, T8 X( \ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
2 e/ g: P, e) T - * */
x& _: O% c m) ?/ N5 T0 e: L - public static function createImage($filePath){
/ F. a8 M: D+ P: Y" w9 t5 p - if(!file_exists($filePath)){ return false; } 2 x& L5 I2 N0 g2 t: G; K
-
]# W" A9 x5 s- r - /*判断文件类型是否可以开启*/ 0 ^3 l. V( ?6 l
- $type = exif_imagetype($filePath);
b* p: x$ q" N P9 W' w - if(!array_key_exists($type,self::$_createFunc)){ return false; }
" Y. a7 i7 H7 a" D* g& G - ! {2 f6 N4 U/ }1 j- Y. a4 A! }
- $func = self::$_createFunc[$type];
5 W) ]* s M8 P8 m! M: Y2 Y5 | - if(!function_exists($func)){ return false; }
; `' N+ r5 c8 p; P4 r" m5 o$ v -
2 K3 w% G# B% z; ~ - return $func($filePath);
- Z* s, e5 |6 u9 b" F8 g3 K - } ; p8 b# T' r D. d# U% s
- , p" P: G! n- G5 T8 {
-
8 w, O+ V" S$ K( E - /**hash 图片 3 D2 k" w3 D- @0 Y3 l: a- h' F/ K& a
- * @param resource $src 图片 resource ID
) G0 R9 m- x! a - * @return string 图片 hash 值,失败则是 false * ~; M) U( H) _- F/ M
- * */
- K% V4 I6 h5 v4 b" d - public static function hashImage($src){
. q, s: @0 O/ ?6 H7 i3 @" U - if(!$src){ return false; }
) I/ ?7 }0 m0 r4 }0 @) \ -
- U2 s# }4 C) r" z# {6 j8 A- j - /*缩小图片尺寸*/
0 t# c# \/ l9 r! K8 ]- D# z) L& ?% x - $delta = 8 * self::$rate; # G |5 r! H( K% w
- $img = imageCreateTrueColor($delta,$delta);
: z9 X1 N. @( b4 z% ?6 W8 p - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
) T F7 H: W5 f* n - 4 Q9 ~3 M3 v) |! ]; d. D
- /*计算图片灰阶值*/
0 @: M( R& S* e4 O - $grayArray = array(); . e; M* `9 {0 G
- for ($y=0; $y<$delta; $y++){ " V* w& T3 C: d7 b2 I
- for ($x=0; $x<$delta; $x++){ $ S9 \, q4 l8 ^* a# m5 h
- $rgb = imagecolorat($img,$x,$y); % u1 m4 ]0 G' z+ E
- $col = imagecolorsforindex($img, $rgb);
# D" x+ ]: h9 f; E2 O - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
3 K$ c$ J4 A ?" }6 m% [; q - * T7 b8 [! |; ~+ i3 ?, g. o
- $grayArray[] = $gray;
' D# s/ U' Z! \; F0 L - }
# O9 }4 U" _. x( I( m - } ) i# q: \- ~# a: f. |* {
- imagedestroy($img); . t+ x. T p6 _$ h, M
- + w6 [5 L& W; i! h! H
- /*计算所有像素的灰阶平均值*/ : C8 c; ?& v' X* p4 M& D+ e
- $average = array_sum($grayArray)/count($grayArray); & m& q7 t+ _9 `+ t7 n" G7 M
-
/ T2 i' l T" j6 _: j6 Q: ?2 }) p- v - /*计算 hash 值*/
, ]+ r3 R. o5 A E* X- I1 p - $hashStr = '';
/ h y8 O9 l+ U y2 N - foreach ($grayArray as $gray){
/ w# I" O: u1 ]/ A - $hashStr .= ($gray>=$average) ? '1' : '0'; 9 b/ _2 Z2 q' m
- }
% ^7 ` B4 }. x) Y, i3 u -
/ V0 g# w" S2 l* Z# e8 i. S% ] - return $hashStr; 4 O) [8 n$ R$ L: m' l
- } * ]- n& B$ ?+ {1 L+ \
-
8 v+ ]' m+ O/ J! Z' i - # T0 T6 H* G' A1 n- q X+ s
- /**hash 图片文件 0 C" I( \- m. s# Q2 s' j" W
- * @param string $filePath 文件地址路径 5 b9 c# a4 v! c% t7 d
- * @return string 图片 hash 值,失败则是 false
4 q7 e) Y O* k4 ^ - * */
/ J. u& _9 r/ |4 D - public static function hashImageFile($filePath){
$ a q4 d1 a# R" U8 W( q4 Q - $src = self::createImage($filePath); # L. _1 x) ^7 C8 D
- $hashStr = self::hashImage($src); : X& D9 m" x" g+ m
- imagedestroy($src); . ?( {% Y+ M2 H, c0 e+ j
-
9 |9 c1 j5 P* U! x. C# n - return $hashStr;
% j5 ^9 Z+ n1 A: V8 o, q+ g( w1 z3 `/ c - }
' W( y* Y5 U6 G -
. n" [5 x9 _+ I3 H -
, m; N: |5 \7 S$ B7 K4 _2 ^ - /**比较两个 hash 值,是不是相似 ; c/ U; K2 v8 Y/ R3 @
- * @param string $aHash A图片的 hash 值 ) I+ D2 f( w& [: @4 U% X( p
- * @param string $bHash B图片的 hash 值
/ j" ]5 r1 e2 d4 M - * @return bool 当图片相似则传递 true,否则是 false 8 G8 n& v! N! ~3 f
- * */ * h3 P. f o ^! U# _, O
- public static function isHashSimilar($aHash, $bHash){ / w# D- ` N. M M
- $aL = strlen($aHash); $bL = strlen($bHash); / G6 c7 U& J2 H: W+ |
- if ($aL !== $bL){ return false; }
+ u, J5 v( ~/ H- X - ) f. r% d3 O; s/ I; z
- /*计算容许落差的数量*/ 4 |* N: i$ J7 j; R* b
- $allowGap = $aL*(100-self::$similarity)/100; , W; q# H( ?8 C( v& d
- / H: o$ M: M4 J1 N: ?3 i, u
- /*计算两个 hash 值的汉明距离*/
9 g- L; N. N* x2 B- D0 u - $distance = 0; . E( v* U6 v1 f9 N/ o5 r
- for($i=0; $i<$aL; $i++){
/ Q7 j, B1 K2 ?) S( q7 P2 F - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
3 t9 a7 `# s( \8 l$ K - }
5 Q( a t3 ?# I -
9 c- a6 c9 T( m! ? - return ($distance<=$allowGap) ? true : false; % Y+ q: t. F. C" q
- } . b2 d) ^) C2 W
-
. G0 Y4 Z. z9 y0 m5 ]) f$ Z8 c -
, {& l U8 G( ^. ^ - /**比较两个图片文件,是不是相似 4 I: Y' @1 \2 Y6 o
- * @param string $aHash A图片的路径 " d w" M. c( n# E3 K G: y
- * @param string $bHash B图片的路径 3 ?5 I) h( l g/ w. v q! }
- * @return bool 当图片相似则传递 true,否则是 false # |7 r1 |& q- w3 d; o; J7 u* t
- * */
3 j: }! s$ v8 I2 E7 S& Z/ t% x - public static function isImageFileSimilar($aPath, $bPath){
) Q8 @* j1 u# h$ k- ? - $aHash = ImageHash::hashImageFile($aPath);
" g9 s( z7 X/ ?0 j& Q/ n& f - $bHash = ImageHash::hashImageFile($bPath);
+ `3 O5 ^* E, L2 U$ v - return ImageHash::isHashSimilar($aHash, $bHash);
" C! J! t% X3 w4 _! ] - }
6 b0 D) F% M1 Z6 ~) |: i -
" y9 c+ V8 R* y N$ f' | - }( i0 a! {' a3 P/ B7 G
复制代码
/ i, j+ Q$ D5 A3 O4 \5 g4 i* y& D9 E" o: D/ L# b
|
|