管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图- y% W& b" W3 C
; q, E) ], G/ X2 c8 k: |
. |) L4 L! V1 e$ B
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。8 B5 T) {/ |8 B
- <?php
2 p# m6 N4 q- U1 b/ j - /**
) W7 x; M/ v% i0 ] - * 图片相似度比较
$ T' o' B) k0 ?: r. m* ` - * $ M5 e Y7 C& c9 v+ x9 P
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; + M0 o9 {9 R; ?% ?4 W e; ^
- * @author jax.hu
& d; p: h3 \: q L, s& B - * ( Q7 {9 C3 A( N" W6 x
- * <code>
0 _# x7 l3 w }( l" e - * //Sample_1 2 q5 G; H0 o+ `% I
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 6 H3 Y3 r0 v( C* h9 a+ {
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
( s" m8 o$ J" @& j. ?0 z - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 7 I4 W' O; u/ Y4 f% p( b) Y
- *
. r/ y0 O1 i) L: Z0 o; w$ H - * //Sample_2
' r3 F6 \; I" n" W- M, g - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ) ?- K- x- e7 @/ i8 u& Q
- * </code> " [ l$ V" |" w9 u) [: d" X
- */ ! m5 t: d c! E
-
& M/ P# ^3 R/ c6 p; @ - class ImageHash { / h. @: s ]/ j I, |5 P3 B, ?
- + u- [5 F2 H$ R+ `& e
- /**取样倍率 1~10 $ w9 ^& q; d0 y. b# N; Y4 Y
- * @access public / F; D% }, H3 g# G
- * @staticvar int
& V2 H6 `; m3 \+ C. B5 V - * */ \# q, e* T; ^# H7 H
- public static $rate = 2; ' `6 X) k, t* t5 V% Z" Q
-
1 n* u+ O3 H8 h - /**相似度允许值 0~64
: j+ w. I. l$ N: ^8 t - * @access public , S% O1 v, _4 u; j9 T
- * @staticvar int % u5 w$ u. A2 A- M
- * */ 3 Q$ k% S& w- G2 x, {! |
- public static $similarity = 80; 8 ]- h( k1 W' g9 t* C* J
-
6 D$ E3 D5 b/ v' Z - /**图片类型对应的开启函数
( ?2 ~& Q- d! B - * @access private * u) q+ c' N5 ]: i
- * @staticvar string 7 }6 M3 U. U( H. M0 k: L
- * */ . q2 ?9 o- P1 f& v/ w( Q
- private static $_createFunc = array(
& ~% m1 ]: O" p - IMAGETYPE_GIF =>'imageCreateFromGIF',
2 p' ^1 ?1 n B( Y - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 5 j7 I; Z4 J) {* w# X
- IMAGETYPE_PNG =>'imageCreateFromPNG',
2 T0 p2 v% m5 r - IMAGETYPE_BMP =>'imageCreateFromBMP', # t) J! S, w1 g/ R
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', $ c* ~) ~5 \7 I; \/ ]7 f- S$ b1 N
- IMAGETYPE_XBM =>'imageCreateFromXBM',
L' Q# c1 z& n! T5 A X - );
5 h4 F9 E) }9 n - * ~0 M4 {) C6 \( N! \" |
- 6 c( K0 W0 A4 Z$ V. v
- /**从文件建立图片 ; ~. |5 B7 [3 }6 A7 d
- * @param string $filePath 文件地址路径
Y+ y4 Z. L0 I" ] - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 0 d* z* C8 a8 p; r
- * */
' Q! k( Y2 v8 v' h" Z& Y - public static function createImage($filePath){ " N' g% o) Z7 R0 }' o
- if(!file_exists($filePath)){ return false; } 6 D4 W' z b5 u1 M5 |8 a
-
# d0 b$ n. R k F - /*判断文件类型是否可以开启*/ 6 w" r" h! N" }8 Z
- $type = exif_imagetype($filePath);
3 P; h9 y) e' o: ^/ [6 j3 R - if(!array_key_exists($type,self::$_createFunc)){ return false; }
9 F5 S$ c6 R) ?% P! } - 4 `; l% ~0 q) e) @" J/ _4 P
- $func = self::$_createFunc[$type]; 3 w: s' M( L8 l0 ~! m& O$ T
- if(!function_exists($func)){ return false; } 1 Q; `1 N# h0 B) P& D1 c, a' v
-
# L& r6 } Q* Q; i- E4 H - return $func($filePath);
; }# ~1 Y2 [0 z, d4 P - } : l/ x1 u8 A9 x
- ) L% n1 b) ^# J5 D0 }$ j
-
* \1 b% @+ R$ l( \ - /**hash 图片
% e9 G5 ]1 I: z: ` - * @param resource $src 图片 resource ID , [$ q2 R6 J; Q5 p9 K% Y
- * @return string 图片 hash 值,失败则是 false , F6 B6 ^8 B' R& O8 ~# ~
- * */
/ a# }7 g0 h2 x) G' A4 h - public static function hashImage($src){
1 X8 d7 L; L" z - if(!$src){ return false; } ; L8 J- o7 Z: f- J6 S" \8 g( N) l% b
-
7 {; x: M* T- H4 }, W4 v - /*缩小图片尺寸*/
9 T9 z. u/ W8 _: D- S5 D# } - $delta = 8 * self::$rate; ; t3 L0 r. @7 T) z! F4 N% Q
- $img = imageCreateTrueColor($delta,$delta);
6 \0 I5 {' O; s; ` - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); # S7 ~" `) m* K0 ~" S
- " r) M7 q: s: N, W
- /*计算图片灰阶值*/
9 o0 J- h+ I3 n ^2 t6 {0 s5 V - $grayArray = array(); 2 S* v% g$ w7 m8 r! N8 k1 W" z9 K
- for ($y=0; $y<$delta; $y++){
, Y; |2 N& p8 m3 O9 r( J- U - for ($x=0; $x<$delta; $x++){
8 ]0 b! n+ O* x - $rgb = imagecolorat($img,$x,$y); 8 r& ~3 z ]" K( R" w
- $col = imagecolorsforindex($img, $rgb); 7 j9 ^* g- H; y5 t( x$ |
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
+ m- Y3 Y7 F$ c5 g* g } -
4 s6 C1 w" H. H" i0 u; s - $grayArray[] = $gray;
- a7 i9 {* d9 p" g& a# o - }
1 ~$ A7 S6 n) M0 `6 Z! ~8 h$ O( e - }
/ b" e) s! @4 H1 o - imagedestroy($img); ' s% E( k/ k" ?5 r* r* Q
-
. v* L6 ?) u0 x, Y: S8 R6 j, I$ G. I - /*计算所有像素的灰阶平均值*/ ; J( F7 {7 [7 e9 T
- $average = array_sum($grayArray)/count($grayArray);
$ R, t. U- U5 m9 n( X -
- K! r, ?/ ^0 F: n- Z - /*计算 hash 值*/ 9 R& J2 c7 B$ B
- $hashStr = ''; , Q* C: q+ Y5 |/ C" J# @; u) E
- foreach ($grayArray as $gray){
; @2 a7 B; ^+ j( c - $hashStr .= ($gray>=$average) ? '1' : '0'; % s& `# v# z3 v1 }$ J0 @
- } 4 ~( u5 V2 d: \$ C" {" N# b8 }
-
2 P6 O( J* [( d - return $hashStr;
# p! P. v9 X1 r0 T0 _1 H - }
" ?2 J3 G2 I9 V( ]- y2 H' h -
; J8 d0 x; ~. O( Q1 Y2 p. F' R - 9 j& }& r3 S0 S6 i5 o3 f
- /**hash 图片文件
2 g3 f7 F( F( ]5 [0 h* [ - * @param string $filePath 文件地址路径
, e/ V5 O- V6 J. y% S, V4 A* C - * @return string 图片 hash 值,失败则是 false
2 n N7 L) h F* Y0 }* c4 l - * */
- d* q x9 |, \6 L - public static function hashImageFile($filePath){
0 S- C$ b* X3 Q' }7 L9 L$ t, S - $src = self::createImage($filePath); 4 x* P8 E1 j# c. ~& F" ?' X
- $hashStr = self::hashImage($src); ( q. d9 q: G+ b! J" ^
- imagedestroy($src);
% A. P% o* z: P4 Q+ @ -
! x3 ^2 B% |( n- X$ u3 Y2 x - return $hashStr;
( G$ p- D( u, Q5 u8 O - } . M; S7 W$ `+ n7 i
-
3 E( O$ _# R' Z( X- Q* M/ g& O+ ]* ] -
$ k% L5 P3 ?( \- p/ P- H" V+ l S - /**比较两个 hash 值,是不是相似
: B: A2 I E1 v- R - * @param string $aHash A图片的 hash 值
- ^" I9 D' _7 o! X" u0 r2 E - * @param string $bHash B图片的 hash 值
' }7 l- Z! P$ ?. a - * @return bool 当图片相似则传递 true,否则是 false
: L- o9 ?# i( z - * */ % b: j: r( L# V) _) G8 c8 y
- public static function isHashSimilar($aHash, $bHash){
5 V$ ~ k, A1 N. s - $aL = strlen($aHash); $bL = strlen($bHash);
3 a# }; G8 i9 z/ j' p - if ($aL !== $bL){ return false; } ' T0 G' ^' y8 C- f$ O
- 9 n# z- I- p$ A" ]; A) i+ [
- /*计算容许落差的数量*/ 5 z% a7 G- O! }5 e. \
- $allowGap = $aL*(100-self::$similarity)/100; $ h, z7 B# R) c5 B+ a: {
- 9 e8 F3 p* S- `6 X2 N) n
- /*计算两个 hash 值的汉明距离*/
- k* x6 ]; v& @9 R3 F( z2 x" u F - $distance = 0; 0 V/ t2 ~2 }$ M: k$ S3 ^* U
- for($i=0; $i<$aL; $i++){
3 p& B' j2 s2 x, l$ S - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
! @' v1 F1 R( l, ~' ~1 } - }
7 E! b( w$ G: c |7 I - 5 M( m# x- \) y4 P; v7 F1 o
- return ($distance<=$allowGap) ? true : false;
8 W2 n4 c- V6 ]& G - } & p8 \7 V/ u& s+ n
-
5 G# [ P% ^/ Z2 r -
( ^5 `7 Z( V: V" d6 F - /**比较两个图片文件,是不是相似 9 n! P9 T3 D+ T. I, a0 p
- * @param string $aHash A图片的路径 / D! t0 X+ c$ V
- * @param string $bHash B图片的路径
) [ \1 Z0 E0 X) d: v' I/ y - * @return bool 当图片相似则传递 true,否则是 false
$ g: G6 t( a# } z! A- v8 q# g - * */ , n4 r8 B8 I4 o& z2 U
- public static function isImageFileSimilar($aPath, $bPath){ 1 d) K3 D' ?; I3 w0 ]' m
- $aHash = ImageHash::hashImageFile($aPath);
. M7 q, _# C1 o7 ` - $bHash = ImageHash::hashImageFile($bPath); " T1 u1 }* X8 _5 t
- return ImageHash::isHashSimilar($aHash, $bHash); . e5 ~' d7 I7 P
- } 9 P9 K. _0 f; W4 c& V
- % q; q# l& {0 u8 k
- }) R5 f: d/ ]. n
复制代码
: R( [6 r3 v0 j0 g u. D. i3 ?; U- L/ |+ D
|
|