管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
* ?2 Q$ W8 f7 [8 w6 ?/ \3 k( m5 ]5 |8 X4 t1 @) [/ c3 i0 u
: a% Q- s( o% S: D由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。& S5 z0 {4 I U, ~+ }
- <?php
- U$ Q: G/ \' b! j& U6 N! e l1 ? I - /**
( r1 n: \- U: I6 |' u0 r - * 图片相似度比较 9 j, z/ W2 f- T3 f6 N4 u$ h1 C
- * , s f$ M2 u9 Y5 w S( f7 G' {* w( N, K) D
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ' W3 Y( Y# s- m5 o! p* ^% j
- * @author jax.hu
+ U. f1 P$ y6 N) {' I! | - * 9 m7 N7 g0 h- V% x/ v) _5 i
- * <code>
$ u- G* I7 B- [' P( o5 ]: R - * //Sample_1 4 E7 B, _ Q0 n+ K% i
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 9 a5 Q3 B2 ~4 r1 x) K% ~. Z& W
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); " [( X6 ~" P1 q+ s4 B. S; ?8 H
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 7 O3 a! S8 L: f3 Y
- * % a! s/ y L! J$ q* I' i
- * //Sample_2 $ q, i6 V) ?2 D- ^5 v3 }1 W
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 0 r; C8 O+ \6 S- i
- * </code>
5 k( O: j0 C' z7 B) d - */
6 w4 L' E+ K. _" `3 a2 `6 y: r2 j -
: m! J" b( p2 ?+ q - class ImageHash {
4 R6 t: l0 K* H" }' L# V) x) V4 c - 3 n0 L$ G8 S/ I- Y! s. B- `$ G9 Y4 t
- /**取样倍率 1~10
& {4 E( R( `) P! W; i - * @access public 0 a {) U C& S2 y
- * @staticvar int 7 g: u, p& T2 E$ k. s9 V8 j7 g# w
- * */
% z8 w$ b! K4 E, w4 I% G - public static $rate = 2;
; _6 |0 F& M5 o, w2 B - : v# ?: J9 a8 v4 h& r6 A* U
- /**相似度允许值 0~64 9 y3 F5 C% P& a$ D
- * @access public # J( T. {1 T4 e
- * @staticvar int
! y* {, I9 `1 I - * */
4 }& ^" |# }: ?" Z - public static $similarity = 80; * j; O l; \: r
- ( c1 q" d6 u5 h
- /**图片类型对应的开启函数
; ?$ A, l# Z1 c( g, u - * @access private
/ `: F# t; A4 `5 S" j$ [ - * @staticvar string
+ v" o4 p8 Q+ v: |! z! J9 _; P - * */ & Y% \1 Q; l3 ]
- private static $_createFunc = array(
2 N. r3 O* L$ W1 h - IMAGETYPE_GIF =>'imageCreateFromGIF',
$ f4 N" B% s/ H - IMAGETYPE_JPEG =>'imageCreateFromJPEG', - E7 Y1 @" |$ ^' B, r
- IMAGETYPE_PNG =>'imageCreateFromPNG',
9 h$ e. s, M/ R! o5 R - IMAGETYPE_BMP =>'imageCreateFromBMP', 5 U+ V2 J+ S, z7 ?
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', , n! S( P, ]6 l c7 B3 |, }$ ]
- IMAGETYPE_XBM =>'imageCreateFromXBM', 9 d0 f V; f, |& I* E
- );
7 {9 |- O+ B4 j, L6 } -
9 l" V1 G8 n* l$ o4 }. X -
6 i4 e' ]+ e5 ^1 R# y; N - /**从文件建立图片 : l4 w; D% e) o+ o
- * @param string $filePath 文件地址路径 ! D# ~( p" Z9 y' Z
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ( |# E8 J+ K* B4 ^4 W, ?$ F" V9 d2 G* W
- * */
/ I3 F& F6 C Y8 S# B h - public static function createImage($filePath){ 3 [; y: b) b2 R" X/ r1 b2 Q+ y
- if(!file_exists($filePath)){ return false; }
" I1 z- D9 P8 |, T! ` - ; t1 ^: M4 _. _$ K) ]- G
- /*判断文件类型是否可以开启*/ ; P. h4 X, ^$ ]$ s
- $type = exif_imagetype($filePath);
6 i0 J+ K0 f: T) G* N - if(!array_key_exists($type,self::$_createFunc)){ return false; }
5 t" ?4 l# M! \9 ^- D& I -
" L( Q! Y g5 `* | - $func = self::$_createFunc[$type];
7 I$ y* H4 c1 Z - if(!function_exists($func)){ return false; } * J; i$ O% N; `* c6 u: h
-
9 ?# C8 ~& t, L! S - return $func($filePath);
9 X: H2 u: y2 G- c) I5 R: w- x - }
6 P" M6 i& T$ a, P -
: [: j" O6 q6 y; X; \8 M - . f9 a+ \6 l5 B
- /**hash 图片 6 {6 |% k6 X/ H0 H
- * @param resource $src 图片 resource ID ' w0 p5 s8 u/ ]1 {( t
- * @return string 图片 hash 值,失败则是 false
7 L6 `- y- z6 f, X% w3 s& C L - * */ ; ~* o2 M0 q! e$ q8 X% `7 b
- public static function hashImage($src){ Z: l2 n! o( W8 d4 @
- if(!$src){ return false; }
- s1 U4 x/ q. A+ x6 O0 m -
( d. J' b w `1 F& q - /*缩小图片尺寸*/ , v2 y/ U1 O/ t8 s0 G
- $delta = 8 * self::$rate; ; e8 A% }& m, t
- $img = imageCreateTrueColor($delta,$delta); ( f. j# M3 @6 q% c, O3 {" b. f
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
7 e, w+ K0 ?9 d% ]3 O; L3 G2 S - 5 Z) K# j* x0 _0 u( ]3 q# X. {
- /*计算图片灰阶值*/ 2 S7 A: x8 }9 Y1 _9 K
- $grayArray = array(); . k8 W" @+ R e. ]6 R& f
- for ($y=0; $y<$delta; $y++){
; B0 u$ t$ w7 \7 | - for ($x=0; $x<$delta; $x++){ * }& X: t0 t- _( A/ z* Y
- $rgb = imagecolorat($img,$x,$y); 2 @) |) d e, R& {, ^
- $col = imagecolorsforindex($img, $rgb); 7 T, _/ L0 b9 U
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 7 m! s, A6 C( O4 E( i
-
# ]$ n" L. p# t - $grayArray[] = $gray;
4 y1 I6 ~# s( |0 C% A6 S - }
" E( [. l5 j$ x+ u - } 4 _$ T! e/ h2 B# b
- imagedestroy($img); ! L2 @3 t' K+ T
- ) O9 M9 D( E6 v& E" A. E- L
- /*计算所有像素的灰阶平均值*/ , x+ B1 ]# l8 G3 `
- $average = array_sum($grayArray)/count($grayArray); ; T6 f; W/ V) E
- 2 I6 c- w- J& Y7 L8 A* \ D2 h- ^
- /*计算 hash 值*/ ) `9 e; T" N0 p9 L. B
- $hashStr = '';
) J2 u* }4 `( ]3 _+ g - foreach ($grayArray as $gray){
) a, l9 J9 X% e6 ^' O* \ - $hashStr .= ($gray>=$average) ? '1' : '0'; " n5 J+ F* N, W, j2 E1 a! ]( a
- } 8 D5 [. o5 e% A" T1 j# F0 O- O
- ! V: n8 z6 J8 F) n9 u, c I
- return $hashStr; * d, t E2 {# n; L0 e
- } 8 t2 Y5 k! w$ R( a* w
- ' A: x+ D7 I& s r* ~9 F+ d
-
3 E5 }* p6 e2 \9 p& c' I- K - /**hash 图片文件
]2 i* s8 {& x" p6 I - * @param string $filePath 文件地址路径 / Z* m. V) F4 N3 q
- * @return string 图片 hash 值,失败则是 false
. G4 j: `- o/ _7 o. Z - * */
y3 ^& ^/ y4 t3 d. S3 f) K {! D - public static function hashImageFile($filePath){ : Q* g: H; }& q. f j$ Z
- $src = self::createImage($filePath); h k$ S! q; H& D- A' S1 G
- $hashStr = self::hashImage($src); # g3 m) G; L. n6 z; ~& [# N Z
- imagedestroy($src); 9 S4 G7 T) [; b. Z
- ( h7 O0 U g7 A" ?! k: e
- return $hashStr; ( ` A0 Q |) V' y6 j
- } 5 T i! r1 T( k! N
-
3 ?4 K2 I7 l5 W - 1 U& J7 g( k7 r8 h' \ B5 n
- /**比较两个 hash 值,是不是相似 , p6 Z* y( v( g5 K4 C8 L
- * @param string $aHash A图片的 hash 值
2 N! \' c b! Y6 ^* e - * @param string $bHash B图片的 hash 值
0 w8 b" V0 C) P r A - * @return bool 当图片相似则传递 true,否则是 false
6 R/ x7 ]; U/ v M" N& O - * */ 2 _" d/ q. E8 a
- public static function isHashSimilar($aHash, $bHash){
' h5 \9 O4 x1 ?& d$ o) V - $aL = strlen($aHash); $bL = strlen($bHash); 5 h& o. M+ N) O, `! s+ C
- if ($aL !== $bL){ return false; } # h+ l* A8 ]0 Z/ L8 ?: ]
- 9 G: R- W* ^3 i/ L6 u& V
- /*计算容许落差的数量*/
; n6 K! j6 k8 S - $allowGap = $aL*(100-self::$similarity)/100;
3 o- {; {- G% I; e - % w- u S4 R! p# V, A
- /*计算两个 hash 值的汉明距离*/ : e2 _2 _& G- B! d% {* h- F
- $distance = 0;
2 N/ k1 M' H- z9 m' l0 P8 Q- x - for($i=0; $i<$aL; $i++){
* Y- u( v: ^. G' u# t# V g& O - if ($aHash{$i} !== $bHash{$i}){ $distance++; } ' K7 R- P0 r0 W- Y; m
- } $ g+ X$ n5 P% S$ `2 K& W @: h
- ' v9 z L g# ?% x* W( a/ k
- return ($distance<=$allowGap) ? true : false; ! B, g( @& N. C9 ^; D
- } 1 S3 r& ]& a3 j( [; x7 {
-
; ~, U) Q3 v- T: k - : L( k0 y) D! P/ H% F% p/ D
- /**比较两个图片文件,是不是相似
/ e6 k+ h: o1 W3 W8 u6 i - * @param string $aHash A图片的路径 ! f) v2 ~! F# m
- * @param string $bHash B图片的路径 1 D' b+ B& d* g5 s
- * @return bool 当图片相似则传递 true,否则是 false
: w& S4 R/ @( c+ p5 E9 z7 d - * */ 5 q- E. ] _4 T _# O
- public static function isImageFileSimilar($aPath, $bPath){
' u; X( v: w% _4 w# m- F+ F1 L - $aHash = ImageHash::hashImageFile($aPath); # C' {) D- v, b' x- n
- $bHash = ImageHash::hashImageFile($bPath);
. ~8 A; H, D% n/ Y# ~( p# S; ~+ J - return ImageHash::isHashSimilar($aHash, $bHash); 5 P& i3 Y4 P5 h# j/ o+ y
- } ( R8 r2 N. g1 c- K$ k
- 2 \% l# l8 G5 R1 [+ Z( r/ |
- }1 m ~2 ?5 h: j/ h7 z6 S
复制代码
+ V9 ]2 I; i3 ]' ?1 f: b
8 o( S5 | t L |
|