管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图: A3 N+ p, l) z9 Q9 Z/ F
3 j9 T0 N; |. D2 u& z1 o9 K, K; _
( e% {8 D/ O6 ?0 \& w; N由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。; k$ k& I D8 u8 T& z7 {
- <?php
1 l- w4 t5 Q% C - /** 6 u) A6 m. v/ ~* U
- * 图片相似度比较
7 y0 ~) v ?3 T8 g1 j$ G' x: G - *
f) f2 n# \- ]7 H3 m - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; $ Q! L g1 W! i7 m7 Y$ l( b6 h
- * @author jax.hu
9 [' W4 }% o! Q% o1 x - * 0 v" L# l: Z& d
- * <code>
2 p% {" y7 D; y9 G5 z; A - * //Sample_1 5 w$ [! E3 ?5 C2 y
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
, @; p# f: E) ~5 n3 A% J - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 4 z1 J! q" E, \ j x6 e) m
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ; S) z$ I/ ?" y( U
- * # t" I! S8 j" ^" J1 \) K- i2 s
- * //Sample_2 1 |7 ]: y: u3 E7 _0 v, J* L! Y% p* S/ W
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
5 d1 b. ~! p! K* z - * </code> - b7 E' Y) {1 y/ q& K2 G
- */
& z, `+ v2 D( m8 U0 g) k, ? - * a/ S. \, f. ]( Q
- class ImageHash { 4 m- J# \: O# f% |7 w; p* |
-
% @' L9 Y* K# E6 U/ U - /**取样倍率 1~10 * ^) c `( {1 f. A5 E8 T( |1 d$ ~
- * @access public
! E2 o) E% u" a2 A2 s/ G, B - * @staticvar int
4 k' @! ~" \3 u) c: f - * */
/ w" M @% I( S" U" |1 ] - public static $rate = 2; . w% G1 K9 a; A5 j; ?: V
- " C. s! }; W/ N6 b! L
- /**相似度允许值 0~64 2 f- h; V: e' O- @
- * @access public ( W m3 i2 S, X @ M
- * @staticvar int
8 k' K. t2 g( w2 Z+ q9 B - * */
9 {5 S4 Q* \% I; i - public static $similarity = 80; 5 ~- N) s x" F9 h% d9 e
-
4 Q1 g3 w7 j5 ] H( Y - /**图片类型对应的开启函数 2 y0 i, K0 M- Z. `/ E* i/ K
- * @access private
( i1 d/ g$ F& R6 Q! S& x - * @staticvar string 8 B* I- X$ \& q8 n, O
- * */ / m- s0 g2 K5 I. y
- private static $_createFunc = array(
$ V4 O. `7 P; s1 f: N - IMAGETYPE_GIF =>'imageCreateFromGIF', 7 s# x; u3 _- C* H
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
, u3 q7 H4 U2 s( q' [ - IMAGETYPE_PNG =>'imageCreateFromPNG', $ Y& i) I5 A( {: H$ \* f( {9 n+ S
- IMAGETYPE_BMP =>'imageCreateFromBMP', 5 V5 `6 H4 |% u# {
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', ( e5 B1 {% S7 |* e
- IMAGETYPE_XBM =>'imageCreateFromXBM',
0 R ^% U y" f4 y/ X/ Y1 j% W - ); 0 Q: P1 b0 F. e" S# m3 V
- : Q- ^" _; h6 e# P6 S% Z
- : j1 i7 A) n* R6 h b2 j. ~
- /**从文件建立图片 . ` G, o. k' q; q
- * @param string $filePath 文件地址路径
& i" }, n5 y K0 a - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
* Z3 Z* @) w/ T% V- X - * */
) [2 u' _+ n) v; s& s! a - public static function createImage($filePath){
; R! D6 u' n ]' `3 l - if(!file_exists($filePath)){ return false; }
0 a. F! y! S4 X( S, b& _9 _, ]6 x( f -
- z( e4 [& \- L& h - /*判断文件类型是否可以开启*/
5 a# O5 N$ E# y* o7 w - $type = exif_imagetype($filePath);
& {: [/ X: M$ Z0 d6 R" y - if(!array_key_exists($type,self::$_createFunc)){ return false; }
5 A8 s; E) j0 a- D, g P2 } - ( R1 |. P$ x. T: W! ]) o
- $func = self::$_createFunc[$type]; ( {; x8 e2 ~% `/ D+ I7 g
- if(!function_exists($func)){ return false; } 8 ~. N5 J4 ?( h7 v. ]6 j8 o, r
- " p, V: {( M1 h+ G! `. I7 `: D
- return $func($filePath);
2 w; S, G- `# X9 ~& g - }
( |& ^0 O$ _/ L( J9 ? q, C1 F0 R - 0 W' f8 r5 d2 g' b" D+ }- a
-
7 F; K! N- s. t1 b0 S2 g - /**hash 图片 4 v$ O! v5 Y) \# _+ \7 @
- * @param resource $src 图片 resource ID
' G" Q8 _3 i1 N; l0 [& |; T; H4 I - * @return string 图片 hash 值,失败则是 false
5 i5 ?' b1 N' W. ` - * */
( u2 V) u! g/ D* y6 P - public static function hashImage($src){ * s4 ^- Y; r- A% I3 `# C. R6 C# b
- if(!$src){ return false; } 2 f. x2 w/ S9 I) | c& |
- 4 F4 U. t- }% K5 E7 ^% s. `4 I' p
- /*缩小图片尺寸*/
# \, `* S, {7 }) W) X' [# H+ S - $delta = 8 * self::$rate;
. k" X0 f: d" }& s - $img = imageCreateTrueColor($delta,$delta); ' X) e- G9 w1 _. f3 H' a( b7 c
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ! O9 u' p$ c0 ^2 ^+ J$ |9 v
-
" a4 H0 X( C% `; ?; u1 g7 Y9 R( p - /*计算图片灰阶值*/ 7 `6 {0 E6 i' _5 M: P8 Y- t; k1 _# z
- $grayArray = array();
7 J4 {2 b3 Y, I - for ($y=0; $y<$delta; $y++){
" y. Y' q. l9 x+ m! a% Q/ z: \: b - for ($x=0; $x<$delta; $x++){ # E) P2 U* h" d) I( y
- $rgb = imagecolorat($img,$x,$y); * e( \3 i4 E3 ~8 A; e1 a
- $col = imagecolorsforindex($img, $rgb);
# K% c1 A5 {; @6 o) V - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; , g$ I* U: ~" e
- . R& U% C1 K8 N$ V- E' v
- $grayArray[] = $gray; / \2 u8 o) i! U% c0 H! W$ l" Q% s8 \3 T" D/ m
- }
. G0 [5 _7 x- m, }' H! v - }
7 K& ~: i" T2 k& O. f9 o4 O- m - imagedestroy($img); & i5 ^6 u" }" X; u: w$ d
- * f: E% t3 G: \% [
- /*计算所有像素的灰阶平均值*/ $ a" R; G7 ^( ?, Q
- $average = array_sum($grayArray)/count($grayArray); 7 N- g4 j' Z" t: S( C
- 3 m; Q0 B# d6 t% D. M: K
- /*计算 hash 值*/
. D5 p) g% Q. {/ w4 j8 q - $hashStr = '';
& S0 a3 E+ O5 s# ]6 K i' X - foreach ($grayArray as $gray){
% H! Y( m6 O6 O+ q; i$ |, U7 y H - $hashStr .= ($gray>=$average) ? '1' : '0'; 1 F( X) f# r8 F/ \0 v3 e- j
- } 7 w% }4 Q) D! b& @
- / B$ G# R! D! X+ d$ ?# g. m
- return $hashStr;
4 a& w' S- G, E7 X( p b" z; D - }
; y) ^+ S# m. z8 V4 k - 2 K% }% B* Y( T, h! W. x9 `( D
- 0 F2 S, y; K! w! U, r, q. K& J
- /**hash 图片文件
" p8 E; V- o4 c3 m! Q - * @param string $filePath 文件地址路径 ! R+ d5 a: P9 ?0 ~: i1 R A4 y( v4 k
- * @return string 图片 hash 值,失败则是 false
; h/ K$ l* T0 F( J- ~8 }% r& ^ - * */
' |/ U# t' J/ b. m( H5 Q - public static function hashImageFile($filePath){ / g3 m+ q& v; w% z x0 K8 J
- $src = self::createImage($filePath); # r. `1 j$ c% U2 h: R2 g; l! @! g
- $hashStr = self::hashImage($src); 6 c0 a* v- Y6 F
- imagedestroy($src);
8 Z% L1 E% q; c/ a3 u) W& M -
* O4 A# G) r8 g1 \1 ~% Q( ~0 H - return $hashStr;
8 K9 |% V. Q( l, Q- D" N, K/ Y - }
1 O# Z4 t. C8 k, x -
' K5 J7 @+ n; G: {7 G# \ -
* H- q' p* z0 I# M N0 c% r - /**比较两个 hash 值,是不是相似
0 E* u5 v7 B7 X/ p0 a0 m' c3 Y - * @param string $aHash A图片的 hash 值
" G9 Y* P6 F. D. @7 e/ J- w6 X - * @param string $bHash B图片的 hash 值 ' y0 ?7 \7 F" [4 ~9 h- p
- * @return bool 当图片相似则传递 true,否则是 false
+ K _2 _; h( v9 J( \% p; I+ R - * */ , w0 M9 B3 u) [: n5 y4 V8 }
- public static function isHashSimilar($aHash, $bHash){
7 {- H1 Z3 w, ]& l - $aL = strlen($aHash); $bL = strlen($bHash);
9 b$ M g5 k5 D8 _- J) z' h - if ($aL !== $bL){ return false; }
5 o; u0 A+ j1 G% c" W - - `0 j3 _) F, i% W
- /*计算容许落差的数量*/ / t+ q `" m7 h' c' n
- $allowGap = $aL*(100-self::$similarity)/100;
$ ?1 s) T( O8 c -
3 _* z" p$ E# h) f0 m - /*计算两个 hash 值的汉明距离*/
y. f' F) T+ G! ?4 n! Y* E7 ] - $distance = 0; & Q$ E' x, W$ k$ {; I [& a
- for($i=0; $i<$aL; $i++){
0 v- v5 r# k n! k - if ($aHash{$i} !== $bHash{$i}){ $distance++; } / ~* E$ z6 O4 T) o6 D* e) o- j
- }
, ?$ N& Y, a1 d8 [* X2 _ - ) p8 ^. a; e: O# @7 Y
- return ($distance<=$allowGap) ? true : false; 3 |( C$ b0 E8 ]2 N' x& R
- }
& P& F* A; T+ k! M* ~" h' k7 ~ - 1 E) q* v" o. D" Y+ m
-
5 {& f) f9 g- e8 [' _- W; H2 T - /**比较两个图片文件,是不是相似
5 h0 ~5 j: }% a - * @param string $aHash A图片的路径 l, |& s, W# ^. G+ i
- * @param string $bHash B图片的路径
* H5 `% N1 j) Q) J- h - * @return bool 当图片相似则传递 true,否则是 false
8 O' q. r5 Y+ u y- x' Z- l5 T - * */
# ^' m9 O1 P1 [" o) s0 k - public static function isImageFileSimilar($aPath, $bPath){
( e0 M8 N0 t5 V% L2 r6 v, \ - $aHash = ImageHash::hashImageFile($aPath);
- C3 ~$ y, O# N2 q0 { - $bHash = ImageHash::hashImageFile($bPath);
3 @2 U# c+ O/ l! P5 I. [ - return ImageHash::isHashSimilar($aHash, $bHash); 6 @/ Y' u/ j/ |+ Y1 ]5 ]2 N
- }
0 V9 g/ q4 F {0 j' m -
( Q- T0 k% s: p/ O8 R4 a5 |1 g% M - }. H" A% u8 ]/ W( k4 b8 I1 k$ ~
复制代码
8 R. f2 Q. V. B+ a9 ^+ p
# ~, B( Q* o. ] m) a |
|