管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
- i8 c3 l8 F/ c% L; m6 \, j" X. P2 }( ?% z
; e2 ?8 P6 r( C' Y* l4 X" Y
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
( u" G* I% J; b* l, \: k- <?php
* r" Q: j [9 e" T1 P - /** ) k) H5 O g9 A) i0 k2 u1 D! ^
- * 图片相似度比较
# w8 a, W1 c2 C; S t - *
* m4 [. u/ c" ^% l* B5 K1 g - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; - f; h0 F5 K4 p8 U% K# I0 A3 _7 O
- * @author jax.hu
+ v* J' Z1 W" f1 N3 I" v - * 8 L |( X5 q3 s% X
- * <code>
l* B- @6 j& |$ O/ S% A( l - * //Sample_1
$ \! P2 }6 x( l/ H2 g - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); : p( G7 y$ z* J ~
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 0 Z2 F' ?3 n: c6 u. x2 \
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ! G* R+ @. h" y* ?
- *
% {! J+ S1 F) U* Z( g5 \+ T, j& m - * //Sample_2
' X$ R- {) y7 O. ]1 V: z( q( { - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 2 X3 a2 Y* j9 l m$ I' C* Q/ @
- * </code>
& u V+ u) a3 T" g0 @6 h3 | - */ ( E# r7 h5 y' s
- ) _9 f5 y/ E* M7 o" R
- class ImageHash { 6 |! W2 W0 i8 {
-
2 j5 d1 X Y4 } T/ u0 L - /**取样倍率 1~10 " f+ V6 x7 d' j
- * @access public ) `( u6 @& @: T2 k. j6 u" e
- * @staticvar int A5 f& e) X T
- * */
* A7 v% [* x& L& \% K: Q9 Z - public static $rate = 2; 0 r: X& Y4 v0 [! x; g) Q, b R
-
/ U$ L1 @) o6 ^0 _5 b - /**相似度允许值 0~64
# a; d% ?% m( n - * @access public % G, O2 k- j2 @( A) g' Q* e$ \+ K Q
- * @staticvar int
$ ?9 y" g8 s3 V8 Z j* ` - * */ 7 m* B" S4 y8 f& R1 N6 T- I
- public static $similarity = 80;
2 C: A5 \; }( z+ j } - : p6 Q) Q3 e `
- /**图片类型对应的开启函数
+ r, k K- C6 y. c I5 d - * @access private 3 }9 g2 K1 ~" K P+ f- {' W
- * @staticvar string 6 ?' @, A$ k( f- `2 r
- * */
( f% _5 ]$ Z u, x* B4 O - private static $_createFunc = array(
1 J3 |) k5 t( Q( I - IMAGETYPE_GIF =>'imageCreateFromGIF',
" ?! X2 {: f. S6 E" u - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 9 u2 b) q4 z6 K$ W
- IMAGETYPE_PNG =>'imageCreateFromPNG', + |( K, m( s2 O) K' b9 @6 s* x
- IMAGETYPE_BMP =>'imageCreateFromBMP',
( k- u; ~3 G) |; r: G$ s2 a - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
/ Y D* R) e8 P0 }1 i - IMAGETYPE_XBM =>'imageCreateFromXBM',
I) p$ [9 d" T - ); 0 `" q; v7 s! T! o @- a/ i
- $ f5 e5 F9 i) s# t0 o3 B
- $ Z8 _8 N; j0 |3 _4 s1 b$ G
- /**从文件建立图片
" \. Z+ Y3 E6 k% Q& _ - * @param string $filePath 文件地址路径
. X$ j, t8 |9 D! G - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
& l: @2 f' ]8 X0 M- g - * */ \# `& U' F8 R0 h( C
- public static function createImage($filePath){ i! N1 d% k) P7 B
- if(!file_exists($filePath)){ return false; } 9 u# U/ [9 w# q( a/ r# |
-
, {3 F$ H& ^* a* h - /*判断文件类型是否可以开启*/ 8 q7 q9 d* _7 e5 n
- $type = exif_imagetype($filePath);
9 F& H0 h) l5 P% ~ - if(!array_key_exists($type,self::$_createFunc)){ return false; }
: o8 @& f# h/ x- z d -
% o1 g1 o" ]% y6 o8 j5 ^ - $func = self::$_createFunc[$type];
; q. T5 N! j' s4 b - if(!function_exists($func)){ return false; }
$ Y) q/ ]& g; ~; H -
B7 p, p& ?$ U( v- G - return $func($filePath); 4 j& a& e( G' i+ ^; T# d) J# g
- }
) U" i* G6 Q) I/ d; V1 ~ E# m0 t -
5 X: U2 }' o& s! V5 X7 ` - 0 h, Y( o7 z# [8 Y6 }3 A3 Q! K5 V
- /**hash 图片
2 K# z/ g, l5 X4 E - * @param resource $src 图片 resource ID ' M3 ^/ ?) F/ C+ T
- * @return string 图片 hash 值,失败则是 false
# d6 ^# M, l, J- E; J7 n - * */
/ Y; V, U% j" J; [4 i$ @ - public static function hashImage($src){ 3 `2 G0 b2 i% b! U3 X& X) w8 ~/ S
- if(!$src){ return false; } / i+ Y% }2 v% x
- ( L+ J0 ?* Z, s. ~) P" ]. [9 ?
- /*缩小图片尺寸*/
2 l0 U3 l. i4 ^0 J8 w/ m - $delta = 8 * self::$rate; 1 u) a9 n- z( T) o
- $img = imageCreateTrueColor($delta,$delta);
! q1 _9 q: @7 b1 z! M B - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); . o4 l) D3 L& r$ ^# S. m
- * g9 S; N+ @7 Y; I- c) B; H
- /*计算图片灰阶值*/
7 M# L! J0 O) L2 g7 z - $grayArray = array(); ' r F' A' Q0 r( A, \
- for ($y=0; $y<$delta; $y++){ 7 ? a$ o& R# H
- for ($x=0; $x<$delta; $x++){
! p+ ?' x% Q% a3 h! r1 w, a - $rgb = imagecolorat($img,$x,$y); 9 V3 M/ I3 }$ b$ z! o' G
- $col = imagecolorsforindex($img, $rgb); % X: n, h b3 k5 u
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; - E" k0 e# M7 e: U a. C. K) I+ b4 a
-
+ D6 J0 m4 {! s* R+ } - $grayArray[] = $gray; 9 }2 e) i5 \9 @+ J0 j' E
- } 6 ~, K3 l* P+ R( i" K4 W; Y
- } ) _- i( ~6 ^0 J( U
- imagedestroy($img);
1 _( r5 O' L, O. v% Q0 f' B7 [4 d -
. `- T; z# t* ]- i - /*计算所有像素的灰阶平均值*/ 7 }( ]; j* F2 x1 j$ t
- $average = array_sum($grayArray)/count($grayArray);
7 x8 h z* v% r( Y" f _+ @5 j -
$ E) {$ Z1 O# P( ~' n9 `# H - /*计算 hash 值*/
. `& a+ p J0 V2 {$ ]- m8 w/ n# g - $hashStr = ''; , N5 K: ^/ |8 r0 v2 M
- foreach ($grayArray as $gray){
5 t# q. [+ ]8 u& N0 h$ b - $hashStr .= ($gray>=$average) ? '1' : '0'; ' n/ H" r2 r+ V6 q5 U# ?" h4 K* V- ^3 u
- } / `' ` L/ H# I) R4 U% m f
- $ W1 O4 ^: c- B' ?) { J
- return $hashStr;
6 r/ g- Y- f0 p2 W0 ~) t - }
3 Z8 e% p% D/ q- }: }$ m -
/ d; L- C6 C0 \1 M - 2 J' A/ o7 @" D7 T+ h
- /**hash 图片文件
, @/ g/ i) Y. J" o7 |. ]2 o' r# _ - * @param string $filePath 文件地址路径
5 O$ h. C% m* W1 Z# @5 n - * @return string 图片 hash 值,失败则是 false
$ ?! k( Q+ u# Y* _0 n - * */ , I/ z' n" R( d3 l
- public static function hashImageFile($filePath){ $ w7 S; t. [% W/ K5 P# s9 O
- $src = self::createImage($filePath);
7 h# |& Z/ x0 B% b' y1 { - $hashStr = self::hashImage($src);
2 }$ O6 i+ e5 G& m* B' L - imagedestroy($src); ! G: Z! N8 f J5 r, q( o
- 0 Q- a5 p; \! O" W s7 [
- return $hashStr; ; k# l5 Q3 A! e9 H2 o! g
- }
& r+ @* R; l5 j/ D" r# c( l8 L - " [' _8 h2 w# t# T7 I
-
+ V9 l6 _( N# I3 r# B( @6 V - /**比较两个 hash 值,是不是相似 7 G c4 h O$ k% [9 J( s
- * @param string $aHash A图片的 hash 值
# K8 u( o* N7 u" l- K5 T# ^ - * @param string $bHash B图片的 hash 值 3 a! H+ O, ^! ~4 @8 }, o7 m9 z! x
- * @return bool 当图片相似则传递 true,否则是 false 4 O2 n( [7 v" e) B
- * */
- W5 w! ^* i. e" t) | - public static function isHashSimilar($aHash, $bHash){ $ N' Q# u4 M: A$ g' w d
- $aL = strlen($aHash); $bL = strlen($bHash); + [7 J, R. g( n+ K" r% N1 l
- if ($aL !== $bL){ return false; } 6 S$ S M; a" W/ V
-
0 W& t% }$ p( }9 M - /*计算容许落差的数量*/
! v! }' b O3 U0 P8 t# w$ r: Y. _0 s - $allowGap = $aL*(100-self::$similarity)/100;
5 I6 g) o; ]. {; m3 y" U1 `3 A1 q - : i6 Y0 O' M8 E* V4 O9 r/ N" M
- /*计算两个 hash 值的汉明距离*/ " H5 D! s. T, i7 d+ `
- $distance = 0;
9 `- o2 K* k$ y - for($i=0; $i<$aL; $i++){ , y* M8 m, O+ `! H' L
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
! m& a8 E% @) |. q4 f# G ~ - } ) t) }* |( L5 V$ [$ A
-
8 ~" e$ l6 a: d' H+ B - return ($distance<=$allowGap) ? true : false; % D/ _. |. K: U6 N2 Y) }; ^# r7 q
- } & P# h8 A5 S. z: s
- 8 M) ^+ }5 K4 d6 F+ b9 [8 @
-
4 B. u4 S4 _0 `/ H& h% W1 p - /**比较两个图片文件,是不是相似 , w, I2 U) Y( M! U5 W
- * @param string $aHash A图片的路径
0 |9 g5 n: [. \* X L: T - * @param string $bHash B图片的路径 ' ^7 M; ^ N( Q% J5 B6 z+ w) W, v
- * @return bool 当图片相似则传递 true,否则是 false
+ o- ~; l* i0 s2 }0 C3 X2 U0 V5 f4 | - * */
4 _" y" ~0 C! h! a# a2 H3 t - public static function isImageFileSimilar($aPath, $bPath){ # \! s. @9 W. i( m$ w
- $aHash = ImageHash::hashImageFile($aPath);
- M( \+ u- Y/ q! q - $bHash = ImageHash::hashImageFile($bPath);
$ M( M) m+ F: g9 X - return ImageHash::isHashSimilar($aHash, $bHash); ) U, _* @, d( n5 t
- } : V- I$ C2 o& I1 s0 h( G r
- 9 S$ |! g, G5 x( G" o
- }
' }4 @8 `3 f1 v0 W( Y Q
复制代码
0 L' u1 ~: A# X y# J
3 m& ]1 [" v6 z1 S. G& L3 j7 K |
|