管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
/ O4 |' O J; z3 O& W0 n! V9 c2 o3 b4 I9 C1 n
" M- b$ R% t7 h0 s
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
+ e$ O% R7 e( V6 h8 b! F$ l6 U- <?php 4 K! z* _6 u: \2 }- s4 J& L
- /** . U* A7 B( A6 P4 V+ u' j
- * 图片相似度比较
% X( Y" f3 y/ j' M& M9 [ - *
! b/ h8 m @# @" H z) H, Q - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 0 }( \. U/ T! L
- * @author jax.hu 1 k% u6 y/ {# H7 k; L# K0 B8 y
- * 0 U* d3 z. X K0 F4 ^
- * <code> # m( D1 F& y/ q! s8 b7 I
- * //Sample_1
; N& C5 M3 n7 f1 x - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
7 R3 O) @2 O0 M: e - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
7 M5 S# f. \6 I7 n1 v1 A% m - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
: [1 R0 l" [& S5 ]/ J- F - * C3 o- a& d# @* F% ~& b/ O
- * //Sample_2
1 D$ r9 Z; ^- f$ `# b$ `' n. g( y - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
4 `4 B) [7 \0 q! ^* D3 ^/ f6 h - * </code> / z: O6 n2 N0 h3 {9 g
- */ ( i( d1 j8 K% O" R3 \6 h4 W2 M S
- O- G0 I% }7 @/ z4 T, `
- class ImageHash {
1 Q1 P. o) i: x/ r1 E5 P# R1 C- K - 1 e; j$ ]/ _; q" w2 b
- /**取样倍率 1~10 d! k4 n+ K( j. ^
- * @access public
# X8 y. `- G l+ m, | - * @staticvar int : w/ l! R8 P# Q2 n) i5 u1 L
- * */
0 T+ c* z0 y' {, M" e& w - public static $rate = 2; % I, s5 g# ^0 i* c6 E/ ?
-
8 [: A. W8 J2 x1 H - /**相似度允许值 0~64 * t7 i+ O. |( T5 X7 v$ F
- * @access public
2 y9 ~( a0 K) ?8 Z( V) ?3 V- U% v: X/ R. r - * @staticvar int + h) Y( a; }# ~2 b! H
- * */ ! q: W7 x7 n! @- n6 V& E- w+ u8 a
- public static $similarity = 80; + R$ `: X- h( j- I. I4 n5 T% i( H# H8 h
-
/ A+ ^. w3 ~' d4 a% j# H- W* J0 \ - /**图片类型对应的开启函数
+ ^; Q3 {# i" v, A - * @access private 2 {7 D/ V# X$ _5 n
- * @staticvar string
& ^! m# l% P' j - * */
8 i; p' h' q% e9 z - private static $_createFunc = array( ' I9 y3 Y, r$ {8 s
- IMAGETYPE_GIF =>'imageCreateFromGIF', ' w# ?- E6 Y& M/ Y
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ) o" t% \$ Y8 K2 s4 w3 T4 Y+ A
- IMAGETYPE_PNG =>'imageCreateFromPNG',
- Y. g% b9 J" _1 ^ - IMAGETYPE_BMP =>'imageCreateFromBMP', ) L: |% c8 h( @
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 1 A" Z, x' n: B, @2 D; K
- IMAGETYPE_XBM =>'imageCreateFromXBM', # Z# E6 O4 ~5 n
- );
- ?& h$ O' o4 D$ R -
6 s" Y, E; w7 m. B+ g& x -
1 M9 r3 @/ n. r1 i6 P5 S) r - /**从文件建立图片
9 i) \& s0 O; `+ |8 s9 p - * @param string $filePath 文件地址路径
- s; m4 f3 G" ~4 L9 m" ^ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
' F: E- f0 { ? - * */
" i- D% V3 {: x d9 t1 _ - public static function createImage($filePath){ ; ~5 W+ [4 j* o K( {* O
- if(!file_exists($filePath)){ return false; }
- z- O% s# Y& P% n) ^ -
( _" M( ]$ P N. M8 I5 ] - /*判断文件类型是否可以开启*/ 9 y7 c- P$ F& n+ e& `: t
- $type = exif_imagetype($filePath); : F/ X+ b$ h5 ~ u2 }7 `
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
# q! h* q+ E" c3 X1 C& j- Y$ I -
. U" K& j |- k# q' h$ Y - $func = self::$_createFunc[$type]; 2 a# Z6 Z! N+ X: o2 B: t& U V
- if(!function_exists($func)){ return false; }
# _, d0 ~4 a) e: }2 @ - ) r) Z" Y: ^ v1 H" }$ I
- return $func($filePath); % Y7 C9 m+ v) S7 n9 L
- } " ~" z% W% |% d+ l2 P9 ]
-
2 {. a; ], P1 m; n' V7 F -
8 C1 R% q+ {9 _ - /**hash 图片
2 A* E1 m( U, e7 R$ q5 C - * @param resource $src 图片 resource ID
0 G8 `6 p: v1 Q4 Z7 Y k& E - * @return string 图片 hash 值,失败则是 false
4 [4 } Z) H7 j; T$ A1 i - * */ 4 Y# X9 L0 H4 _- g2 r- u6 _ v
- public static function hashImage($src){
$ x% x4 G! Z: n - if(!$src){ return false; } : B9 b- Z1 U# _4 O) t% [4 P5 @
-
% E# d! K* C7 a; |! L - /*缩小图片尺寸*/ . O! V9 L# W1 U7 @
- $delta = 8 * self::$rate;
a* n0 h7 B. t* j1 o - $img = imageCreateTrueColor($delta,$delta); 3 q5 q$ B& P/ v6 H- _3 L$ z$ _ P& Q. C
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
3 x8 P. l6 F- G- q! l' Y O -
) m4 D: Y w- y% x5 l9 y6 K7 J% I - /*计算图片灰阶值*/ " O7 P8 u, p" H& s" b8 V) K
- $grayArray = array(); 5 }0 c- Y6 Q& }; T$ q
- for ($y=0; $y<$delta; $y++){
# L, u8 s- l5 v6 ]# I& ^ - for ($x=0; $x<$delta; $x++){ - i+ G: \3 F& y# e$ U
- $rgb = imagecolorat($img,$x,$y); . S- e" } p. S) l+ a
- $col = imagecolorsforindex($img, $rgb);
$ ?. c% G# q9 K$ r6 x - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
1 G# C l4 @! R. G$ P$ s -
9 _7 E/ r4 A6 e1 W - $grayArray[] = $gray;
3 c- E; _$ y7 N; _ - } $ G4 n) {, h0 g- c' D
- } # i* G$ I' u$ N
- imagedestroy($img);
s3 ^1 s4 B1 X' K; _) e - . V9 `3 i' k7 f1 k8 [& o
- /*计算所有像素的灰阶平均值*/ h8 v$ W2 z4 W7 A
- $average = array_sum($grayArray)/count($grayArray);
* f. i- d) y3 K' I -
! D# M5 e( K, ^7 [ - /*计算 hash 值*/ 1 Z/ O! P3 I8 U! \' B7 c) \$ N; G1 `
- $hashStr = ''; 9 Q4 q/ e! H& [1 y* {
- foreach ($grayArray as $gray){
' Y0 W! X3 X# Q- a - $hashStr .= ($gray>=$average) ? '1' : '0'; 4 D" C$ ^& u. }
- } 9 \) Y9 k: x. _+ i% B
-
9 R" ^. U* c+ Z3 t1 f% S - return $hashStr;
# Q8 S, z2 A+ a" m' ? - }
, Y3 h7 H: T) ~/ n3 h2 g4 P - ) ~! d: e" W, \ `- m
- $ M4 A, O! b2 R6 _3 {/ W
- /**hash 图片文件 " x8 X8 q5 G1 Z: [5 Q$ h
- * @param string $filePath 文件地址路径
6 a& a; p$ Y1 O2 @0 ^; _- A - * @return string 图片 hash 值,失败则是 false
O. X" Y2 D! _1 Q/ j% s* h* u" { - * */ $ O* w3 [' w! }4 M) d
- public static function hashImageFile($filePath){
" T, }) i! p8 Q! m - $src = self::createImage($filePath); ' U7 i9 `0 V- H( y1 {
- $hashStr = self::hashImage($src);
( N" o' j' ~. B) a - imagedestroy($src);
$ U1 z9 N0 Q# q$ r -
( r% a8 J! F' h: W - return $hashStr; z7 g; ]( z( r, Z
- } " y1 y) e$ Y( O+ m
-
* v8 k$ p; o1 s( ?" K( F$ Y, a - ' b; r, a* v1 k5 Z
- /**比较两个 hash 值,是不是相似 ' G* Q+ \' E2 e+ H' S9 b3 Q4 v' s6 M
- * @param string $aHash A图片的 hash 值
' K% B( d0 Z, w3 T- `8 J6 H1 \- n - * @param string $bHash B图片的 hash 值
2 E" J$ u! ]) P, g- w8 y Z - * @return bool 当图片相似则传递 true,否则是 false / z9 \; g9 n6 j! q6 p, @! Y: ^
- * */ ) ^- h2 l$ w7 M
- public static function isHashSimilar($aHash, $bHash){ # j* J% R" Y/ S N! o0 G( l
- $aL = strlen($aHash); $bL = strlen($bHash); + V6 T7 y, U! m
- if ($aL !== $bL){ return false; }
) a' j9 [4 J( I6 K1 s0 [; t - # M: h" ^) Q/ w9 Y
- /*计算容许落差的数量*/
0 J! C6 d: O" Z( l5 c% W8 Q - $allowGap = $aL*(100-self::$similarity)/100; $ M' a" e; d( @8 U2 }5 k
- k T s) W* g, Z3 W6 O) Z
- /*计算两个 hash 值的汉明距离*/
& a9 W5 C9 i: I" U - $distance = 0; 0 C% R# ~: W& n0 f% g
- for($i=0; $i<$aL; $i++){ * \7 L2 L" W% \5 U2 T
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
3 ~* f, B3 S( d - } ) R- m' y& Q5 J! {7 W6 M5 k, n
-
5 R4 R% Y8 X# |7 B - return ($distance<=$allowGap) ? true : false;
. P2 s! q% D. w+ O m7 ^3 E/ | - }
1 a9 A) p% I- d/ |, z* q -
9 y0 s/ O0 W& W5 } -
1 y4 P$ ?/ d6 G - /**比较两个图片文件,是不是相似 & ?- x; Y n0 q% t
- * @param string $aHash A图片的路径
* r0 h" i0 o3 A6 N - * @param string $bHash B图片的路径
& I. V0 q: p+ C5 t; y - * @return bool 当图片相似则传递 true,否则是 false
% i& A+ y2 M, o2 j: D - * */ $ n) ` a/ O2 o9 k3 U
- public static function isImageFileSimilar($aPath, $bPath){
, R+ J1 x& H3 Y7 `5 i - $aHash = ImageHash::hashImageFile($aPath); / Q; Y% L% m& o7 c* d
- $bHash = ImageHash::hashImageFile($bPath); 9 E/ L& d" \# D/ N
- return ImageHash::isHashSimilar($aHash, $bHash);
# b1 I1 h8 j" d' i) O' w V" w; C3 D& Y% B/ r - } ' k) q1 S1 ]3 W" Y5 f
- 9 C8 V/ ^$ P% W. J! D C" ^
- }
5 T* b- g0 t) V: y; w& v# ~. G4 D. e! K
复制代码
8 k( \* x1 q+ ~6 d; }7 M5 U. A, a. ~$ z
|
|