管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图8 G( J. p, y3 }; q# ?
4 l' a6 \- u7 X
; ?: P: S" ^0 P# l& H3 ^由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
8 r4 w9 T9 o% H. x$ p! d- <?php 8 k! w. d5 ]4 _- b0 t
- /**
% {, x8 i1 v P( P* H - * 图片相似度比较
" [% i: T% B9 |/ ?! T% _7 k - * $ p) P/ b i7 N
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; % V4 ^ g0 r: J" O3 o6 R
- * @author jax.hu
" {# Q9 F" D/ @ - *
5 T7 S4 F( b( R! a$ {' T5 @ - * <code>
1 L; X' L( f* E: g0 | {2 _ - * //Sample_1
# z9 x9 H# j" Z - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 9 B; o( M7 h4 w' V) t4 G
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
5 p& C8 O6 A0 t2 N x - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
# s r8 [; m% P% K( J4 x9 F - * 6 q) ]" G5 u1 q3 q
- * //Sample_2 - \ w4 Q% i3 [3 J' G
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
4 h& D- A6 ]+ n0 b4 V3 O5 W% i Z - * </code> : w# G" k Q0 {. u$ m9 p
- */
) {4 \6 D4 ^! A - 7 b; }; z! H3 ^9 }. ^/ Z D
- class ImageHash {
5 R% Q0 L4 K/ J2 G9 ^ -
- Z$ n0 s) P2 | - /**取样倍率 1~10 1 s5 m {5 x" l0 v) s
- * @access public ' Y C' \3 | p: M- c, X
- * @staticvar int + {$ ?; D" n% D
- * */ : P2 i1 U# ]- W9 H' E! `) B: `
- public static $rate = 2;
# W7 I" @- O8 K( N* K$ n, K -
+ c# N& t8 a9 [+ D - /**相似度允许值 0~64
; d' W4 m5 u2 W - * @access public
U2 d1 q8 Y9 x; J! N - * @staticvar int ! a$ e$ [' c# v% S: r2 W
- * */
) d9 t0 z, F0 g - public static $similarity = 80; ' T. V4 i" F# T! y
- 1 l( g2 C, \+ S2 g# V: t, r
- /**图片类型对应的开启函数
* W9 e1 g2 W0 S& g, b \' K - * @access private 7 g% f/ w- \& V4 f3 l, g
- * @staticvar string
7 F8 w2 @6 I0 R! I) b: R/ \* R - * */
5 @$ p; ^. M1 {; O+ e - private static $_createFunc = array( 6 d2 k) F$ M. V n
- IMAGETYPE_GIF =>'imageCreateFromGIF',
& y6 w1 l; n* h. M( F: s - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
$ Q; |# [9 w1 O4 P* f - IMAGETYPE_PNG =>'imageCreateFromPNG', ' A Z$ _* Z v& q: o( q; K
- IMAGETYPE_BMP =>'imageCreateFromBMP', ' D; j+ N1 o: m; K$ N$ g
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', / C# O5 w. a4 T' Q
- IMAGETYPE_XBM =>'imageCreateFromXBM',
, ?& Q2 `9 [- P6 |4 W - ); : o- L6 {' T( N- r1 D$ B ~& r! V
- 8 o5 m7 v P& v; W
- 6 d! v5 j( w4 k/ E/ p
- /**从文件建立图片
3 k6 S' }/ }' {7 z- l - * @param string $filePath 文件地址路径 9 P5 J7 c, v$ w" E E' Q, I D# C- K
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
7 Z! P9 Q3 N E' b: M1 g - * */
) b6 |) e" V9 ?, b. q - public static function createImage($filePath){ 6 Z- |7 E, [) X$ |6 m
- if(!file_exists($filePath)){ return false; }
% z/ m2 i8 c5 D A - , k( j+ \& ]- v
- /*判断文件类型是否可以开启*/
( U( r. {; y, x6 v- R9 U* c - $type = exif_imagetype($filePath); 3 S) u8 h# a. W1 b# h/ ~3 B
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ) f' I1 P# L* M+ H n
- 2 r) f2 k5 s9 c+ ^8 b7 {
- $func = self::$_createFunc[$type];
4 e+ _7 _' j" ?/ J% D - if(!function_exists($func)){ return false; }
g3 R8 O& o9 t+ \- z. m! w$ O - 8 w- c% _0 d g- o3 ~
- return $func($filePath); 6 `+ `* z7 a" L8 F) B
- }
% P1 w! v0 W; c5 X } - 3 A* C |% D% O( R' Q& c3 ?
-
( X S! g* u" G6 K5 k7 E - /**hash 图片 7 U0 A7 o; B+ G, n3 E1 H2 l
- * @param resource $src 图片 resource ID 7 i* g9 b* }" B) e* O1 M
- * @return string 图片 hash 值,失败则是 false 7 t) d, b& @3 g* i- ]
- * */
0 Z) ?) M* q- |9 p/ y6 Q- p - public static function hashImage($src){
; o8 d; |$ D9 p* j8 A - if(!$src){ return false; } ; E& C8 ^: A' x r$ J
- " \: A" |" L! _0 E8 u4 I
- /*缩小图片尺寸*/
, @' u- e8 [: G- f - $delta = 8 * self::$rate; ! D4 W. B% a' R$ A N
- $img = imageCreateTrueColor($delta,$delta);
4 h2 r: \% i9 d! `. O. G - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
/ h1 }8 U. `8 y: r! P7 \$ F -
. ~4 n; t( k. N1 ?$ v - /*计算图片灰阶值*/
- t; b; n- o% ~5 b, ^ - $grayArray = array();
6 c! M1 @* |: O; Z - for ($y=0; $y<$delta; $y++){ * D+ f1 F+ A0 q! H2 V
- for ($x=0; $x<$delta; $x++){
4 z( q) ]# t* F3 ?# O O6 \ - $rgb = imagecolorat($img,$x,$y); & t& [6 J/ d1 E, O
- $col = imagecolorsforindex($img, $rgb); # a% \; C9 M, b0 H
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
' L) q% s4 Y) K4 z9 B - # k+ s" [9 D, s* H, L
- $grayArray[] = $gray;
, }) }( h- S* h% l0 ]- M& | - }
9 c4 H& Q2 g( p( T: p! C5 x - } 1 H; N# y/ d* E
- imagedestroy($img);
@, ]/ m0 H/ K4 j! T) K -
& F0 b& X: t3 X - /*计算所有像素的灰阶平均值*/
! M) y! \% G1 v( m9 @: {) T+ O - $average = array_sum($grayArray)/count($grayArray);
7 L7 J& J- [: E7 Y - & F$ f' r0 c( R, K; r
- /*计算 hash 值*/
5 N+ _$ F+ d! l o0 r - $hashStr = ''; + D3 }2 T' |5 C* u# P) Q+ N
- foreach ($grayArray as $gray){
4 |2 n s0 E6 Z% U) |' u - $hashStr .= ($gray>=$average) ? '1' : '0'; 1 a% e3 y4 q9 [3 X# D
- } ' s5 U3 j- O- E8 x! p. E$ V
- 3 W3 n& ?4 }- c3 K- P& _
- return $hashStr; ! D5 Z$ ^% }# b5 X& a! I
- } & X) C' z4 z- w) s0 p6 R9 _! |$ S2 G
-
% y' f" d5 ?6 n9 d" s Z -
5 i0 b& n! @; S# g1 |: Y$ z" j - /**hash 图片文件
C$ a$ L2 [8 m. N( ]5 V9 g - * @param string $filePath 文件地址路径
3 S! l7 D+ H( S6 b; q - * @return string 图片 hash 值,失败则是 false * u+ `1 z; T9 {; d3 b
- * */ $ @" |/ k8 Q. T1 s/ R9 l& x( V
- public static function hashImageFile($filePath){
: ?9 O$ x4 W# y! H - $src = self::createImage($filePath);
1 t( n% c0 Z, X) v) s* N - $hashStr = self::hashImage($src);
( r I( q- S: b. s$ M - imagedestroy($src);
- ^7 ~' c3 l* ?% f -
5 [, H& W0 X; k - return $hashStr; ) K) G6 r q6 ]9 @0 I
- }
& _) A" w; E3 I. M" L/ r -
7 L" L6 ^/ }8 o9 s. R - * h2 w' R: E* |( S7 H
- /**比较两个 hash 值,是不是相似
; U J" {/ ^5 v+ p# G4 ~ - * @param string $aHash A图片的 hash 值
' S+ z$ I, ^5 r7 R6 } - * @param string $bHash B图片的 hash 值
8 W# O4 ]2 x/ D - * @return bool 当图片相似则传递 true,否则是 false ) [* {5 _5 M8 m2 k. I$ ? |
- * */
: c0 `9 P: K8 E& m3 q/ ?% k - public static function isHashSimilar($aHash, $bHash){
7 ]7 @0 @ \6 |$ D5 H, Y - $aL = strlen($aHash); $bL = strlen($bHash);
& O0 q- q k+ b - if ($aL !== $bL){ return false; }
+ ~: \1 t8 ^3 {1 Y+ R - 5 G# K' g- I+ E* x
- /*计算容许落差的数量*/ 0 u) i9 r/ F7 _0 v0 f# ]
- $allowGap = $aL*(100-self::$similarity)/100; , T% v( F( b" H4 Z
- * G% P$ V" Y, l B6 q
- /*计算两个 hash 值的汉明距离*/ 1 k4 Z5 x( o8 R6 b& a8 {" p
- $distance = 0; . J# ~+ C# {: N j/ y
- for($i=0; $i<$aL; $i++){
% Z: k# B8 _( o! B" P - if ($aHash{$i} !== $bHash{$i}){ $distance++; } + G& J6 ]+ ]3 p- r
- } 8 n7 w: V7 V$ b" \9 H& |* T8 ?
-
( X+ w+ F8 f/ b2 U: ~ - return ($distance<=$allowGap) ? true : false;
, w" M# ~3 _+ `2 c+ J: @2 w( M9 T - } ! }9 e5 ^+ a# ?6 H5 a
- 6 _% d, S: _% F p
- % k3 @5 k4 g6 q1 E. y
- /**比较两个图片文件,是不是相似 6 J+ A8 P; S. u; X2 f+ I
- * @param string $aHash A图片的路径 ) d5 H5 L3 e' ]4 C# {9 b! {
- * @param string $bHash B图片的路径 4 p! o* ]( m% j" r. I i& x! V3 [
- * @return bool 当图片相似则传递 true,否则是 false
( a+ H8 B6 N. X( O" {' K - * */
q0 h3 i m1 u4 ?9 O4 b; p - public static function isImageFileSimilar($aPath, $bPath){ ; B/ p+ o- {- A! t7 Z
- $aHash = ImageHash::hashImageFile($aPath);
|' ?1 r8 J) T - $bHash = ImageHash::hashImageFile($bPath); Q6 c7 N1 a0 K/ M
- return ImageHash::isHashSimilar($aHash, $bHash); 1 `) [8 N3 j- N5 }/ d" x
- }
* ]: u. O1 l. t2 Z6 w# N3 P - 1 o5 H9 E7 D1 v) G
- }- F0 T9 X- ]7 D) N
复制代码
) r$ Y, k( O$ h9 n* z; \
& b5 S9 N m P' I" B7 | |
|