管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图5 T9 x1 Z4 [- D5 R/ b
7 ~4 g2 d+ r D& D }& ~8 A$ u% A2 c% L$ z* R% c$ G
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。. N3 m3 q3 h! Q1 j. d% M0 k% F
- <?php
; E$ ]$ R+ E3 K5 [: O - /** : n8 B1 e/ {2 H
- * 图片相似度比较 ) P- L3 ^3 ~# C* [4 H9 c# R2 q
- * ( _9 V7 Q5 r& H2 O3 A
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; * e% b) G; Z" W1 G5 w" ?" G
- * @author jax.hu 7 ~0 l/ \4 d. \/ P; Y0 P
- * . x" L" h1 s/ u: {6 f) c
- * <code>
! F; W) e* o) C; t6 f - * //Sample_1
5 j0 J4 u0 W8 r$ j: h- P. i* ? - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); , U* }! E- O2 q
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 8 C4 t( R1 l$ l% b& g, ?8 m
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
- _$ v+ o' l7 W9 v; j% N! C* \ - *
3 L; _$ D ^2 ]) { - * //Sample_2
/ `5 p1 C C G! u" `" y - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); - t; }% t5 o9 Q
- * </code>
) k0 S' a H' ` - */ 9 w0 X% Z& v, ~* Z
-
4 ]! T& N. X% Q R' a1 b# R: T - class ImageHash { % ?0 {. G: }/ m5 `
- " _0 R6 S" u, L' T: r# \- Y8 S) {
- /**取样倍率 1~10 " {! z/ _/ m- v4 D7 d, @ n A
- * @access public
% H- t4 T* S, j3 r( D - * @staticvar int
. _( w% b2 [% r: C. P7 y2 v9 r - * */
$ w# \- R( ?' ?8 O4 l - public static $rate = 2; ) _, N2 z; R6 Z6 b. `
- - l" H/ G/ h1 S! i$ f6 K
- /**相似度允许值 0~64 ( U4 Q' [4 l) Q" x
- * @access public
1 m: B1 ~5 C5 ^ - * @staticvar int
! V' G: E P( I8 F! B - * */ 8 H/ _+ H0 u) X n( A
- public static $similarity = 80;
; ]3 I1 u* \- P% O0 f8 R( `5 s -
. K7 u$ d% z5 u* s9 L" R - /**图片类型对应的开启函数
/ m4 o. W4 m) e4 P - * @access private ) C" ~5 Q3 k2 j1 M; J
- * @staticvar string / Q# x! h" k- z, Y
- * */
' f% D Y- I. Z4 p' n, @- t6 P - private static $_createFunc = array( # @, B- c( y t8 V. A5 y( ]
- IMAGETYPE_GIF =>'imageCreateFromGIF', 8 F. ~7 Z9 a( }+ I* r# O
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', . O' H, W6 S3 m) C f
- IMAGETYPE_PNG =>'imageCreateFromPNG', % l7 r; w% Z U5 c; T9 _
- IMAGETYPE_BMP =>'imageCreateFromBMP',
: Q) E5 P$ e; B7 S3 h - IMAGETYPE_WBMP =>'imageCreateFromWBMP', $ K# L! W9 ~4 e
- IMAGETYPE_XBM =>'imageCreateFromXBM',
; P" L: i6 |& Z0 N' T6 @% r - ); ( G- l- y' R9 R0 N! `( O
-
- T( U) ~& @$ B& y) M - + `' U" B' A( k' c
- /**从文件建立图片
' L, N" T5 K" w6 P0 M, M - * @param string $filePath 文件地址路径
+ M3 w* A2 z1 u4 ?7 c - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
; w* b, p3 \; G- b( m: t - * */ " i; V& v& n) l; L
- public static function createImage($filePath){
3 S2 @; ?& S9 J" n - if(!file_exists($filePath)){ return false; } : D0 ^; [5 ]0 c7 \8 S4 S
- . {7 c w: C' @$ j: F% L' P3 \' {4 Q
- /*判断文件类型是否可以开启*/
1 C( ~" i& k1 ?6 F7 r0 w - $type = exif_imagetype($filePath);
1 a! j, }0 c1 o8 s! u/ k- X% e - if(!array_key_exists($type,self::$_createFunc)){ return false; } 1 C; X H# O1 M# `4 N
-
& u5 |( d% f7 E) A' N! E# z - $func = self::$_createFunc[$type]; c0 |+ a: @ }7 @- c% D- i
- if(!function_exists($func)){ return false; }
# @1 K% `) Y; M' n( G -
$ y0 \+ b# B4 W+ v; @ - return $func($filePath); ' n% D7 ^5 ?: K8 c
- } " l% W1 [/ T/ I$ ] u4 Y: d
-
) ?/ t0 i; N) Y! s5 _) p0 n- I - ' a& O+ x/ E) E) D5 u% @7 ?
- /**hash 图片 / G6 H( p# b" W1 m* c( p
- * @param resource $src 图片 resource ID
! H* R4 [, k- S8 m, P - * @return string 图片 hash 值,失败则是 false
+ n! D+ e5 u. D" f8 r' d - * */
- z4 Z$ D: T" s& r2 \ - public static function hashImage($src){ - Q3 ?( p5 t6 q; u' R8 F
- if(!$src){ return false; } # n* F5 h r3 \ O7 y- F
- : h- f( P% ]+ s% F# ]4 h( q
- /*缩小图片尺寸*/ 4 W- w3 e- t/ q v' x) j; a
- $delta = 8 * self::$rate;
3 ]" q. s$ A& m) D - $img = imageCreateTrueColor($delta,$delta);
4 ^5 c) D' p3 Z0 l! B; x: B - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
. P1 z/ a$ u- E/ C! e" P3 V -
( g8 g }( x; Q0 v0 U' B - /*计算图片灰阶值*/
. ?- o4 L8 |- P" R+ T# V7 D - $grayArray = array(); 5 c. d% t5 T6 `; M4 U2 N$ }' d
- for ($y=0; $y<$delta; $y++){ ! @$ y0 C9 F8 J3 c
- for ($x=0; $x<$delta; $x++){ 3 ]% J' b( w* @' w5 [
- $rgb = imagecolorat($img,$x,$y); , f6 n$ \; X! x: u/ g. F
- $col = imagecolorsforindex($img, $rgb);
' H! |) |+ S5 r2 Q! O# Q - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
# L# ~+ P s0 P -
/ }, g5 e! K4 y% f) O2 V. i - $grayArray[] = $gray; - F' n' d+ I! {$ i G: J
- }
l1 Y: F2 |" K* x; Y6 ^ - } 3 \5 H; C2 J* j" g/ C2 ]
- imagedestroy($img); * u6 r- f: m7 H( {6 E
- ( {( G3 _/ V* t7 h" Y
- /*计算所有像素的灰阶平均值*/ 8 `$ W d' o' e0 }
- $average = array_sum($grayArray)/count($grayArray); . z7 `# q) u8 a* l1 C
- 7 t0 k7 M+ j+ D: G
- /*计算 hash 值*/
$ V7 b0 p9 t" x9 P/ Y& t, J - $hashStr = ''; 9 @% M# X: k C, d3 n' T
- foreach ($grayArray as $gray){
7 }- G* g. B% p" R, U9 w, U - $hashStr .= ($gray>=$average) ? '1' : '0'; # O( P3 x! f3 v, Z4 r* A
- }
4 N. P/ W% F. p8 c -
" p! Y( d8 u: T3 `. u9 X, U - return $hashStr; , z* |0 T9 k( |2 {/ I e, L
- } - ^3 Z4 J' y$ O# E- _1 p5 t
-
! ?; ?1 U, k" E' T -
0 c: Q0 ~) t2 }2 b8 g8 b - /**hash 图片文件 6 V/ j6 w) r5 q" Q
- * @param string $filePath 文件地址路径 5 I" W/ z& J: B) m( V' j2 |! a
- * @return string 图片 hash 值,失败则是 false 2 s2 ^3 }/ D K% x
- * */ / t6 C# _3 w* a0 V' |& g# K& [% M
- public static function hashImageFile($filePath){
1 [! @8 J) }" | - $src = self::createImage($filePath);
Z% @3 a' l1 c/ k9 S - $hashStr = self::hashImage($src);
- L/ [5 L' p& |2 B% y$ t( o4 ^! P - imagedestroy($src);
# S9 @# a, C. z8 O* L7 Y" \ -
' Q3 z' O [8 G0 t, r, B, I2 \: \ - return $hashStr; ; ?/ h3 Z5 y1 b7 N8 ^ [
- }
+ ^# i! g3 v3 {$ J9 s1 r4 m - 7 n; Q0 Z" s ]: [1 P; T( h
- s1 ~* D% a& H) b
- /**比较两个 hash 值,是不是相似 $ K. T7 N8 z6 M
- * @param string $aHash A图片的 hash 值 ' q& ~9 @2 n) ~8 o' r
- * @param string $bHash B图片的 hash 值 ) q& d! h! R" } @
- * @return bool 当图片相似则传递 true,否则是 false
; I' l$ q, w, q/ G. y7 \ f - * */ 8 b7 O( m! t( k
- public static function isHashSimilar($aHash, $bHash){ " A" |. C5 g& N. e' y1 X
- $aL = strlen($aHash); $bL = strlen($bHash); + L1 ~1 z, t& k9 F
- if ($aL !== $bL){ return false; }
" m8 {; @- r; k+ j: S7 z. q. c" o -
; h) o; D1 @4 G: V+ p( H - /*计算容许落差的数量*/
l0 e, }" ~/ K' k$ B1 `8 z P - $allowGap = $aL*(100-self::$similarity)/100;
9 b( \0 }3 K! L -
& B$ E# @- C! [7 n" m! H! D' c0 v2 M - /*计算两个 hash 值的汉明距离*/ " A4 m, f; L& Q+ J2 S
- $distance = 0; ( v' D2 ~' K4 V/ c. W
- for($i=0; $i<$aL; $i++){ 1 V+ T, V, q' r6 `) w# \0 O
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } : F; y# f% ^; g9 S, s$ v8 C8 G
- } 4 `1 R- k6 }$ v
-
+ Z2 ]3 R" `. U. s) A x# d - return ($distance<=$allowGap) ? true : false; n/ O U9 @' E" H u
- } 5 {: E& h) e* x* A( F
-
4 M+ d* S. }% p6 J8 \' C# g - 4 j$ o3 F! x- \
- /**比较两个图片文件,是不是相似 9 T" ~" K2 t0 h+ Y9 d2 a; f4 D
- * @param string $aHash A图片的路径 1 `3 G) d2 Y: y# s
- * @param string $bHash B图片的路径 ; l- _9 v/ l! h- _9 f7 t: p. h, \
- * @return bool 当图片相似则传递 true,否则是 false 5 n, E1 [* z7 s( B: g$ G# [
- * */ 2 E j/ P8 I4 z; x) U- e5 g
- public static function isImageFileSimilar($aPath, $bPath){ ( }# L1 I/ @* p+ M* L
- $aHash = ImageHash::hashImageFile($aPath);
. \8 j& I" h- X6 m9 a$ q5 [3 R - $bHash = ImageHash::hashImageFile($bPath);
2 _5 u) N& O0 d/ b5 O5 O, ^/ W - return ImageHash::isHashSimilar($aHash, $bHash); # a u2 n U' N2 X! a
- }
/ J" |2 c2 J8 T. R -
9 O k% u" P$ L - }
5 F8 {3 {- R* n( g4 {% ^
复制代码
3 E& v* H, J- I! ~9 `' T3 Z5 {4 B" T6 ]% b% L2 Y8 ?/ W
|
|