管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
) t* D" _6 M0 f0 h& T
8 m v. s, K1 x8 \
" ?/ g2 E$ h. A3 z由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。2 W1 _3 R9 O) Q" P/ O4 ]
- <?php
# p. I' G& r* G5 X3 z3 R- @- \ - /** 4 J! ]& g* Q* p
- * 图片相似度比较 , d' F9 F n; q8 _. G8 F
- *
8 _" W1 x4 S% i9 e0 I7 m* H, | - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 5 W1 K3 {' W8 t1 c' @5 B
- * @author jax.hu : Z8 k3 ^- C* l: R
- *
1 @' E2 @+ g. `( c/ I - * <code>
1 U8 m3 N/ O# @7 h/ C- y! \ - * //Sample_1 7 e+ D: G$ Y6 V0 a) l4 d8 S+ {
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); + u8 m/ d: O$ z9 A6 a& k
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); l0 \! b: {, e: v1 C. j* w. E
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); |- `/ Q; E- }/ c8 U: h0 o( _
- *
3 r3 I- V: L7 w1 N2 E& u - * //Sample_2 6 g4 K9 e1 i7 o, f
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
; [- o& ^; E, P: l" @; U( _2 F - * </code>
. n% r) r1 |" o% P* N( n% M4 \ - */ ; `: ]+ n$ y. n; h! C
- 9 H8 R# n; ?& `" N) P5 r
- class ImageHash { 5 H7 e( _, ~ V8 S |
- ' s" V. o L2 x+ q0 ]
- /**取样倍率 1~10 , P% [9 z) |$ q- } D
- * @access public ; P: a! \ a9 v$ I, R
- * @staticvar int 4 |* P% [5 w3 w( r+ z1 g
- * */
! `) x1 n2 J5 v, ~# o - public static $rate = 2; 8 q" t+ D/ p( B! K- j( w
- 7 {. W; |2 _7 f, ^: m' ^8 y" q
- /**相似度允许值 0~64 , i3 Q. X& \+ ]+ p: p4 @
- * @access public & k. {8 n8 K0 p7 |
- * @staticvar int U3 x8 @* } f1 J; c
- * */ + p2 I0 G& k% h5 y$ T
- public static $similarity = 80;
3 n7 C, w X$ f7 W: {! l -
* h3 u& q' F4 l - /**图片类型对应的开启函数 0 }' t1 p: M# g* ?( \2 P
- * @access private
7 Y/ f) ]% [# }7 ]) C; o$ Z9 \ - * @staticvar string
* d6 y, g5 J5 b - * */ + f9 h) t) |) {; N9 x& F; W0 ]
- private static $_createFunc = array( - c: i2 U- v5 m6 h& D2 ?9 W( D4 `8 J
- IMAGETYPE_GIF =>'imageCreateFromGIF',
# D- G) x6 G _/ Y# D, M M - IMAGETYPE_JPEG =>'imageCreateFromJPEG', & |( Z% j- A3 d3 Q7 Y3 P7 b
- IMAGETYPE_PNG =>'imageCreateFromPNG',
$ M' G( N1 I+ H" T8 J2 `2 D9 R6 a - IMAGETYPE_BMP =>'imageCreateFromBMP', , i" Y) a/ X, N* [/ o6 D. K6 o
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
% Q8 t0 S% a1 h* {, ]9 | - IMAGETYPE_XBM =>'imageCreateFromXBM', / K" Q7 Z, A0 @$ m
- );
6 ?$ P. ]6 V+ A6 E$ O7 u -
% Q; m4 ]! O; G8 F- Z/ u -
) s! r7 s7 I: J( E1 [ - /**从文件建立图片
) i4 z9 D* o: O4 t- h$ Q - * @param string $filePath 文件地址路径
% p Q$ ~, ~. [8 l - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ) B. H4 Q; i4 i Y
- * */ + B5 b s: f) ~
- public static function createImage($filePath){ # T6 d) S, F. d, G9 X
- if(!file_exists($filePath)){ return false; }
* g! S R! i S/ i& I9 a, @+ Q! U -
0 l! B9 J- [' `# H. h - /*判断文件类型是否可以开启*/ 9 L% O I2 [ V- |* K% v
- $type = exif_imagetype($filePath); . x9 @8 t9 D! \! H( b) h
- if(!array_key_exists($type,self::$_createFunc)){ return false; } ; f) I/ P. N, Q( D" y
-
/ Y: a1 u. @! g# { w - $func = self::$_createFunc[$type];
- M1 M7 x/ j6 {4 J$ x7 w9 n - if(!function_exists($func)){ return false; }
2 p* a- C; v( c3 z1 a& x7 e -
4 `7 ?/ {1 W0 j - return $func($filePath); . N3 h- G( d0 c
- } ; I; m' m [3 P9 I' i, j9 Y
- ( g& y5 V3 {! R
-
! J8 ]( O! a" M1 s* w - /**hash 图片 6 Z$ V, z4 d* f; ?, i
- * @param resource $src 图片 resource ID * e& {1 j# i# A5 d9 }: u
- * @return string 图片 hash 值,失败则是 false 9 U$ x/ [7 s0 d/ x, r+ G) j9 k, K
- * */
0 ~4 K5 o8 v: b% O: a5 f* d I8 \5 i - public static function hashImage($src){
z: y+ {- ?7 w+ j - if(!$src){ return false; } 0 }: z, U: A- e) \6 m( C+ @3 X
- + q R$ ~& X4 Q; x. b
- /*缩小图片尺寸*/
# `- C+ k, j* U' k3 q" L" u% q( j4 L - $delta = 8 * self::$rate; 5 _3 K/ v9 g* r, g- v4 P
- $img = imageCreateTrueColor($delta,$delta);
4 t2 A0 ]% w7 P& k1 Z - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ! L1 [5 x6 }$ U' ~3 U- i } V
-
& @$ r9 e! i7 y _ - /*计算图片灰阶值*/ " W: N; e6 l; U) I. h! A6 S
- $grayArray = array();
# B, s9 c2 A3 S0 d8 n: S9 ] - for ($y=0; $y<$delta; $y++){
+ _& P; y$ E3 q$ c& ]) U - for ($x=0; $x<$delta; $x++){
/ z0 c& \ |* r& x/ l6 [( j - $rgb = imagecolorat($img,$x,$y); $ i) X( A2 T! `; g m8 Y3 W
- $col = imagecolorsforindex($img, $rgb); j6 f' [' p% }+ w
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ( {, W9 |4 r! y9 W
- ' \, q1 s) k" _5 M3 V
- $grayArray[] = $gray; 9 T3 F @ V3 ]( p; q3 m+ y" K
- } 2 L' r- a, V4 f( n6 Q
- }
7 m7 v( h% E! [: j q: z8 B4 m$ T - imagedestroy($img);
: ~, ~$ q4 W2 F/ U' w, n - \' @2 @) {0 U8 q$ P8 ~: v$ ~
- /*计算所有像素的灰阶平均值*/ $ w( W, l S2 v0 F
- $average = array_sum($grayArray)/count($grayArray);
" O; M, z$ _$ S* u8 N! M, U Z5 R -
6 H. s5 k; [/ `9 l" `( }2 h3 C - /*计算 hash 值*/ 5 Z1 V: k% w5 `; e7 p* D
- $hashStr = ''; 2 F% K+ q) k8 E) y
- foreach ($grayArray as $gray){ 5 T4 G9 L1 N6 E& I; J
- $hashStr .= ($gray>=$average) ? '1' : '0'; 6 p8 i3 E6 m+ c4 z- O: i2 ?3 f4 k& B
- } 2 K) v, W% i% \' o$ |' h
-
( N' ]6 y$ R; m6 Z) l - return $hashStr;
5 u! j1 O1 w3 ]$ f - } 9 ^: B9 v! T3 H% K
- - C, s3 B2 x% W4 N0 l( U
- & @/ r: z- A6 y" `% E, _9 `
- /**hash 图片文件 ( M. _" i7 p+ [( @$ L. V3 N+ t
- * @param string $filePath 文件地址路径
1 ^4 V( v" m' L8 r: ? - * @return string 图片 hash 值,失败则是 false
; O: T2 K! z, D- U3 e/ \ - * */
3 O# i/ {5 I4 G% B; J; C5 q! ?. a - public static function hashImageFile($filePath){ " z0 a1 A* J; F
- $src = self::createImage($filePath);
" {, Z. K- h8 n' k- m7 R - $hashStr = self::hashImage($src); 1 ^2 w: z, g& }+ j- U# A/ O
- imagedestroy($src); & P( u8 |. o1 K6 x
- 4 X0 k! w( U: [+ N4 q* ?, `) V
- return $hashStr; 8 x F# _0 T2 ?3 w u
- } $ m. e4 O# X8 _8 a2 n4 g4 t
-
1 w5 _/ u6 u. X5 J& B1 v$ b& x7 \* @ - 2 ]7 B! O! `* x
- /**比较两个 hash 值,是不是相似 2 s: Z8 Z( G' L! {
- * @param string $aHash A图片的 hash 值
6 w0 ~! b2 y& m8 O - * @param string $bHash B图片的 hash 值
+ t. b% {+ N9 ^" k - * @return bool 当图片相似则传递 true,否则是 false
& \: L) L3 @( i$ c7 Q* [ - * */
6 l8 @! [6 U X6 B1 q y - public static function isHashSimilar($aHash, $bHash){ & ]7 w" H3 a1 W; S/ S& u0 ^
- $aL = strlen($aHash); $bL = strlen($bHash); / J* {% A- H! W5 b- l
- if ($aL !== $bL){ return false; } " B O' k1 A4 y4 d9 |
- - I. e m2 k6 T5 s
- /*计算容许落差的数量*/ 2 x- n2 x/ i2 r1 e3 v2 Q
- $allowGap = $aL*(100-self::$similarity)/100;
( J: Q7 O: |& {; } -
& y8 H$ S) D& S3 `$ m2 C7 M - /*计算两个 hash 值的汉明距离*/ 9 {+ _7 f4 ~# L% {: C6 S
- $distance = 0; ! A9 z/ h& {9 K" ^' _
- for($i=0; $i<$aL; $i++){ % d; a1 w! Z: X% v! g" k
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } & t8 u7 D9 l' }" P5 z$ w
- } . @% ~0 b$ w% Z: P
-
8 p9 r' \/ u/ D) }- D% Y. } - return ($distance<=$allowGap) ? true : false;
' @& ?- J& b3 Q. z0 P# M" v - }
w; i1 F9 S; k. n% |: b - 0 u! h7 l3 E- [7 M! c5 F
- S. O$ ]3 r; J+ n4 }0 D
- /**比较两个图片文件,是不是相似
% V' T1 n; v' H' _+ R - * @param string $aHash A图片的路径 7 T5 q- d! J" I' ^
- * @param string $bHash B图片的路径
}9 E$ K5 e4 h9 Q. ^ - * @return bool 当图片相似则传递 true,否则是 false : F/ q8 g* Z8 E6 i
- * */
1 t* O5 {! r C8 P' ^$ Z - public static function isImageFileSimilar($aPath, $bPath){ 8 N* i/ [6 J8 u1 K9 P
- $aHash = ImageHash::hashImageFile($aPath);
p0 k1 b: F! ^, ]3 L3 x* s6 S/ ? - $bHash = ImageHash::hashImageFile($bPath);
: \( j; i- i8 Q B9 j: @ r! B- O - return ImageHash::isHashSimilar($aHash, $bHash);
. v4 Y: N; P' T* [, t; v - }
' o# u8 _$ x: ?! ?5 n! r6 ]: R$ R - 1 B* p6 n6 w5 u
- }, R, |* V* B) g( u! K) G. L
复制代码
" W* _0 N& b0 Q7 K
* ?! F6 t, l, v4 Q. P |
|