管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
3 L0 Z3 V. t$ o, I* _# v8 ^0 W0 [7 ~2 G- U
( k- }2 r8 z+ B" `3 \由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。0 p0 }( q6 Q ]: e" a0 l% p
- <?php
1 c& R9 u! A6 M1 f' Z$ @2 K5 b - /**
; J3 l& Y3 {' B/ Q/ d3 C" w - * 图片相似度比较 ) H& s5 y5 ?' v0 T" c1 x4 b8 }) c9 V
- * ) ]; }) F8 K ?: L% e5 G% e: u
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
2 w0 l0 D, v! }. i6 _ - * @author jax.hu # n" L. F4 v$ F: ?6 C; o4 N* F
- *
( v8 j1 Q0 c( D9 Y - * <code> 6 y1 D3 H/ ]5 c* X9 s
- * //Sample_1 # f9 |" @* i4 z2 Y8 L1 \, d
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
8 |- G; N# {' K+ ~9 q6 z# J3 B - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); + K% L. k1 Q* f( X5 f
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
5 z/ C( B: A( r6 d6 ~2 O - * 5 N% s5 |$ ]0 p/ R* Z& ?8 J E& ~8 i1 s
- * //Sample_2
% ~5 G/ P$ [' T, I; Y5 l+ r& W4 ^1 B - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
% Z+ w6 f7 l7 B" I$ X( a - * </code>
; P6 i; ]) s" A- K0 e0 N - */ 9 w/ }8 g. |& X2 |+ j
- 3 i2 W o0 t, Y: ?+ k
- class ImageHash {
( }) a: j" u a, x - : ]* \" d! p. N6 e$ b8 }
- /**取样倍率 1~10
& t9 B5 m0 w/ A2 a - * @access public
V3 a0 t$ m! D& h6 m" W - * @staticvar int y8 i" n4 q3 D6 Z( w
- * */ . @ V, X7 K, ~% L% i. y: I2 o
- public static $rate = 2; 8 u) D* z5 O6 R2 {- h
-
# w( |& l0 N. V0 y7 U; }" I. z - /**相似度允许值 0~64
; W7 |+ z2 E4 b! e0 C/ | - * @access public
) j9 H* u% u! p1 C- f/ K3 K - * @staticvar int
- j, S5 { @+ K: }% x& i* V9 }. O - * */ ( u* t0 `/ Y! ?! n+ ^
- public static $similarity = 80;
; Y2 `! f" k+ T. m& q4 M - 1 c) P- `% S. A/ w
- /**图片类型对应的开启函数 0 P) `) Q( C, G3 B+ R) }6 m0 d
- * @access private 4 z. L( c, }* G; N4 _% ^, j
- * @staticvar string
' K" p1 f" e3 o* i& ^, C( C1 m* V& W - * */ , u1 m" k. k+ D1 a: d
- private static $_createFunc = array( $ S W3 {9 Z4 {8 M: u& O
- IMAGETYPE_GIF =>'imageCreateFromGIF',
, p+ R0 ]2 ?# E$ M' k% h - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
- k7 j2 y. Q% w8 T8 A4 ~1 j% @8 i - IMAGETYPE_PNG =>'imageCreateFromPNG',
' }/ T& ?9 G( w+ d0 x - IMAGETYPE_BMP =>'imageCreateFromBMP', & P; H, ]7 s0 P+ q; ~( D0 Z) r8 v
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', " A7 \3 {. r1 |$ x
- IMAGETYPE_XBM =>'imageCreateFromXBM',
* _3 i1 R5 f2 D; Y2 |! p% F - );
0 n( G- F$ Z: u -
% a- P; ^# S2 w h8 m - ) d( o4 P' e- o2 w& v
- /**从文件建立图片 , I9 Q+ [! w/ k% E/ t# G
- * @param string $filePath 文件地址路径
, @; x- C& |& q1 {8 ^- m- [ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false % }' Q0 r* K$ e; R
- * */ ) k6 O3 a0 B0 r6 J9 U
- public static function createImage($filePath){
% _) D7 F' S6 S+ [; p - if(!file_exists($filePath)){ return false; }
# ]' c! x3 z h4 B9 P - 6 R3 i2 y3 m1 e4 C/ j# w9 v0 m4 Q
- /*判断文件类型是否可以开启*/ 1 r! C) x: i4 _# Q' g
- $type = exif_imagetype($filePath); & {) A6 R4 Q+ C# p- Y% @
- if(!array_key_exists($type,self::$_createFunc)){ return false; } 3 Y& u( P. S" O
- 4 {, v3 u: f7 L, I: o: t9 i
- $func = self::$_createFunc[$type]; N3 R4 s' Z; M# a% y' A# V
- if(!function_exists($func)){ return false; } - P( ^, h Y, }: `( |
- $ @. n; [! B6 y* f) s
- return $func($filePath); ( B# N* y( T3 F5 {
- } / j. l2 i8 j4 y
- , ?! j% B/ f3 y* d
-
5 x6 N9 A( t+ N$ t - /**hash 图片
7 V# Z5 `1 e+ t! F' | - * @param resource $src 图片 resource ID
- l- O& F% X: N, N5 k. R) J - * @return string 图片 hash 值,失败则是 false 7 t3 g4 p0 o8 D
- * */ ( s \3 Q' _) u+ { G, o; x3 U' q6 j
- public static function hashImage($src){
/ C' R" n# l' C& C) F, V - if(!$src){ return false; }
+ Z1 s# e: B6 @( l; x1 P -
. {; I& s% y# @: @8 B2 J - /*缩小图片尺寸*/ : u3 @) f8 S8 E' b
- $delta = 8 * self::$rate; & S4 ]3 E. n: ^/ t
- $img = imageCreateTrueColor($delta,$delta); # C0 z- n( G9 x$ ^) W% Q6 Q
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); * k3 w' A2 y# s, R# R& {7 ^
- ) ^3 q- H+ G/ u
- /*计算图片灰阶值*/
, O6 Y$ M" y* v - $grayArray = array(); x: V- D5 @0 m2 D
- for ($y=0; $y<$delta; $y++){ & S* ^8 r5 N5 W$ g' W
- for ($x=0; $x<$delta; $x++){ ) V2 S7 R6 u1 G
- $rgb = imagecolorat($img,$x,$y);
/ l0 s3 \6 F: F9 } - $col = imagecolorsforindex($img, $rgb);
2 K" f/ z3 F; \' x& J0 k( X& e - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
3 P' {- }3 Z; f- u6 X0 w -
2 ?+ p3 m5 E/ p - $grayArray[] = $gray; - _( @1 [ o) `
- } ! |) W8 u2 o; K. t0 [1 k, B. t
- } 2 {* W( i8 O6 V, w, H2 Q
- imagedestroy($img); , C) p% \, h# g" Z3 `
- ( {; t7 D9 w! D/ _
- /*计算所有像素的灰阶平均值*/
' s0 p' x# J7 ~6 W0 O( B - $average = array_sum($grayArray)/count($grayArray);
6 L! x% _3 n$ m. B) w - 1 K3 x; d4 G* u( U- {
- /*计算 hash 值*/ - q0 Z- E2 ?" _1 D" K- K+ l
- $hashStr = '';
, T; F% t; e) ^/ M - foreach ($grayArray as $gray){ ' z/ i3 p2 e* U( y7 D. M( n
- $hashStr .= ($gray>=$average) ? '1' : '0';
) x4 C! v. m( W - } 2 J; q' S4 K; k4 ]0 ^3 E
- 0 [- J( z) u& H r' U2 A
- return $hashStr;
+ M, y& N9 c& \ - }
. E5 o" y! E6 B0 u1 } Y; Z7 o -
- g9 O; f+ U$ O# P - # [* M3 b% L7 s, I1 i
- /**hash 图片文件 % c& Y1 J1 q4 u3 K
- * @param string $filePath 文件地址路径 3 ~1 [3 j q' x: ^
- * @return string 图片 hash 值,失败则是 false
: _5 Y! b8 W; l - * */
/ d! b. e# K/ N0 I, ]: o6 @ - public static function hashImageFile($filePath){ 4 V9 l0 e/ z" z6 j7 G
- $src = self::createImage($filePath);
& \# X2 D. I+ C: h& ]9 o6 o# j. L' h - $hashStr = self::hashImage($src); 5 n0 A) q- g0 Z5 m. U2 g) E2 t
- imagedestroy($src); - j/ x9 |* u/ U; h* X
-
[: K/ c- t9 k' T, \ - return $hashStr; , v4 E3 \0 N# R7 E4 v& g. B
- } . N- R1 ]$ R6 z6 }' Z: R
- % t q t8 A7 R4 p5 p3 @
-
( @1 o8 t; H2 c1 H$ z; L: l - /**比较两个 hash 值,是不是相似
+ C# c( j4 `' [ - * @param string $aHash A图片的 hash 值 ! U; T3 I( @! Q6 S& l0 \# c7 _
- * @param string $bHash B图片的 hash 值 0 h- ]+ o: n% W8 G7 w$ e7 k
- * @return bool 当图片相似则传递 true,否则是 false
: O U" Q$ r0 S8 R4 A - * */
2 E1 }0 a, d" a2 e) o/ M7 }8 ] - public static function isHashSimilar($aHash, $bHash){
% k! \; b% e* T8 l9 d - $aL = strlen($aHash); $bL = strlen($bHash);
) H9 h7 D9 b P, \& Q - if ($aL !== $bL){ return false; }
8 K* Q& k) d; N, `. ]1 ~ -
5 w* x) m4 v9 L" X7 X. I( p3 M - /*计算容许落差的数量*/
& ~# q$ T' O& d( a# B) r - $allowGap = $aL*(100-self::$similarity)/100; $ t8 E( X m# C4 P5 e4 \" _
- ) B# L7 J2 D/ c
- /*计算两个 hash 值的汉明距离*/ % ?2 [ A* W5 g( O* y8 a
- $distance = 0; ! o$ a3 H3 _3 }: x! }0 u
- for($i=0; $i<$aL; $i++){
y! d: z0 V1 a: |3 l; b: N' e4 e1 s - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
7 X8 w$ a& t# e! g; G7 b+ n1 v+ k - }
! ?+ I! { K, k! h! m -
8 Z9 D/ G! t0 c8 c - return ($distance<=$allowGap) ? true : false;
* {5 Y( O9 M+ i" K* n5 w; u - } 0 }5 T3 x+ b1 F! j! A
-
3 ^$ o0 q; z( v; R* L - + ~# r8 _7 o3 Y# f, l3 h
- /**比较两个图片文件,是不是相似 $ a r4 h0 K- j/ K5 E7 J0 _
- * @param string $aHash A图片的路径
9 I4 | Q+ n, d - * @param string $bHash B图片的路径 & J# a* @- b Z* |0 R
- * @return bool 当图片相似则传递 true,否则是 false 3 q% N9 V7 p/ i; S6 X! l4 K
- * */
# h1 y6 b" |# U - public static function isImageFileSimilar($aPath, $bPath){
' l; Z, C4 G: W0 R1 | - $aHash = ImageHash::hashImageFile($aPath);
0 `) ?8 b0 F9 ]! S" W \# b- A - $bHash = ImageHash::hashImageFile($bPath); 4 \5 q1 s# ?; @ H' T
- return ImageHash::isHashSimilar($aHash, $bHash);
7 q j2 `0 I/ f - } 9 P1 G5 T5 @+ ^; u& h3 F& O6 j
- : d( _8 F1 p( u0 R8 m# U; w
- }. H+ j0 e o! N9 K/ L
复制代码
8 V7 ]+ Q& T7 S h' t, v. u2 B3 _8 t) ]7 m! A, k
|
|