管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
& m4 F8 h' O# |7 v8 d4 {( @4 @& F9 Z3 y
$ |# P2 G# f$ B" C2 m
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。- w6 H1 b- _ n9 @! U
- <?php
2 b U' S% u4 ~. x0 E1 |$ ?# k - /** ! s$ x2 F% l V. Q" |4 l
- * 图片相似度比较 ) i/ u6 F+ C4 w' z
- * ' F$ c+ Q: r2 `
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 0 T) ~" o9 {% x; {4 f& W# G2 d
- * @author jax.hu 7 F* N/ U" T/ V& v5 t' r7 o
- * 7 [$ Q: g Z4 Q( w( P% t
- * <code> 7 M E& `" U4 O# o, K4 a Q. r. a& b
- * //Sample_1 - n4 C* e) l A; q
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); ^2 @* m& K: K
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); * k* w0 x- l0 Y7 p$ i1 E4 E
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ' ]+ |4 T- R, ~
- *
" b. B3 j% X0 K8 T) B - * //Sample_2
6 u# U+ U) n4 Y/ v: f - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ) v; L3 _# N* d- Y
- * </code> # e! t4 o8 c: Y1 p Z6 X( d
- */
% ^0 }: ~/ [ A6 U0 z# V* E/ O8 Q - ( n, ]; Y0 D6 r' h1 y6 J
- class ImageHash { - V2 k( k9 N) H3 m* e( q3 ~
-
( Y- H& {% E/ R2 W. b. n - /**取样倍率 1~10
# Q) n+ D- o6 E& M& x5 y - * @access public
6 A- Y. v5 Y# Y/ ? - * @staticvar int ; F3 S9 M j' j% C7 ]- ?8 I
- * */
* k. K' a) b7 @! d$ E: \. T! } - public static $rate = 2;
, p% `! o" A$ ] - " ~2 N+ C- A5 ?6 v& k8 s. X
- /**相似度允许值 0~64 / [+ h" C6 R7 [ N
- * @access public
/ Q* D! u! v. n - * @staticvar int
; K; |; ?& q7 @4 x! j - * */ ( k$ H5 a ]1 e6 S
- public static $similarity = 80; - p( G" M- S( s4 i! {1 s
- 0 u: G* e$ {* R% u6 ~" }
- /**图片类型对应的开启函数
& m. e2 }' D7 U: C* h - * @access private
8 K) b' o8 {8 R; d. {/ w8 A, q - * @staticvar string : N- J3 h& Y+ f u; H' F6 g5 Y, s
- * */
8 {. ^7 e. O. S% ^$ t, F - private static $_createFunc = array( 9 z9 J$ M% H/ B
- IMAGETYPE_GIF =>'imageCreateFromGIF', " X- x8 }3 T& t8 A7 J
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ; U- y! n7 x9 o- i( W
- IMAGETYPE_PNG =>'imageCreateFromPNG',
: \1 u n( ?: F v8 V - IMAGETYPE_BMP =>'imageCreateFromBMP',
* B( `! w- ~7 G: H0 @: r6 r - IMAGETYPE_WBMP =>'imageCreateFromWBMP', ( q9 u! i8 Y- l- G( f
- IMAGETYPE_XBM =>'imageCreateFromXBM', ; ?, K% |9 r3 R# @
- );
7 B& x9 y, ?- X$ t. H -
5 d) q5 V- C2 W6 @; {4 F4 U -
/ u' L y+ q Q) H+ k9 A9 G - /**从文件建立图片 $ f5 x6 z- t& A
- * @param string $filePath 文件地址路径
0 F5 V4 ~+ i C; J0 T j - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
# Q; b0 u$ r7 B - * */
7 b9 v, G* c) l* T5 r4 P - public static function createImage($filePath){
- I$ g1 ~: W' k& u& m* `9 [) Q - if(!file_exists($filePath)){ return false; }
/ D& W; Y# m% b+ v- N+ }6 A -
; B" U" _" Y+ C% N; l - /*判断文件类型是否可以开启*/
. Y% ~! b8 y3 _# r) { - $type = exif_imagetype($filePath);
: A5 L, v# O' G7 Z6 t - if(!array_key_exists($type,self::$_createFunc)){ return false; }
7 L7 W- W' k6 C% F$ V+ F! ^, i - 4 d0 S) R0 |% Z( S1 j
- $func = self::$_createFunc[$type]; 7 _' y, A# w$ ]& x$ @. S/ B
- if(!function_exists($func)){ return false; } + `3 W! H. l C8 m, P
-
( N1 s _* q& D- m+ N - return $func($filePath); & f7 O6 Z: R3 W f
- }
. b: T6 s4 f( @ - : r+ {. z1 ]0 K6 _7 k" u
- 0 s: d$ z6 B4 ?7 n/ `8 [
- /**hash 图片 . S# ? u; W7 P0 q N4 I
- * @param resource $src 图片 resource ID
4 s) W/ |" [/ \6 D- @2 {7 N- K - * @return string 图片 hash 值,失败则是 false 7 O: N3 H' I/ H/ v# B2 b
- * */
% [. l8 x: d0 s: |6 e5 g - public static function hashImage($src){ 2 \! a. q: \- }& C2 s1 c; k# Z
- if(!$src){ return false; }
- M0 I! _% j9 h, Z) K6 M p6 r - # ~9 S% c" ^$ O% W& z( |
- /*缩小图片尺寸*/ ( o. ~( d+ P9 r2 n4 Z, x& d
- $delta = 8 * self::$rate;
) B* w! k* [) ?1 _. o" k - $img = imageCreateTrueColor($delta,$delta); 9 E6 }2 c3 Y6 z, G' r7 N. T
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
) u4 n7 {, M1 a$ B0 N6 j+ G3 V2 c - 5 x% ~5 s% d0 ?% @
- /*计算图片灰阶值*/ " f8 I$ q* l* m! p% K# E! G
- $grayArray = array(); % h* N% W7 l2 h. h9 d+ H
- for ($y=0; $y<$delta; $y++){ ! e8 I, g: K `/ Y! c
- for ($x=0; $x<$delta; $x++){
9 h* G/ A/ P" A8 t. q4 M - $rgb = imagecolorat($img,$x,$y); 4 @& F/ d) `& Q" ^! Z- S; V9 ]
- $col = imagecolorsforindex($img, $rgb); & I/ c8 l6 n' x) {: O3 e. w% K
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ( {& n, b) t$ C A
-
7 u. f. i- A7 l3 @, n - $grayArray[] = $gray;
& c& B& |9 L- z9 ~; D. P - }
/ d/ i: n/ z: a0 b+ ]9 v$ E - }
7 k e1 h. a& ^" q# s9 P& p( w - imagedestroy($img);
$ ~: p9 I6 ]/ L% H( |9 N - $ T2 u; I3 D$ ^
- /*计算所有像素的灰阶平均值*/ 0 U7 T/ Y# j1 h4 o+ h) B- @
- $average = array_sum($grayArray)/count($grayArray);
9 C7 ]7 V$ y" i7 V6 y, L7 h+ ` - 8 L7 S4 s: ^% C1 |, _5 ]) C2 P
- /*计算 hash 值*/ 5 Y" r4 F. r) t1 ~4 e1 J2 s$ p
- $hashStr = '';
# k1 o" `5 ^5 @) o6 e8 j- f; U - foreach ($grayArray as $gray){
8 j5 x' c0 r( U3 m6 D& D - $hashStr .= ($gray>=$average) ? '1' : '0'; 7 y% O" v& a$ t! Q" S6 J8 z, D
- } 8 G' x& q G. Z9 Y
- / U6 ^1 d y. x7 R! O
- return $hashStr;
* W. @6 W& E/ i - }
: G8 R; o, ]2 _& {- s -
: b _8 M) r) a& K5 P& m, v -
; b- v& H- w# H, n: H - /**hash 图片文件 8 o6 V* o+ L3 m: v. O8 l" ^4 v
- * @param string $filePath 文件地址路径
3 N; T' }2 x2 ?! Q! `/ g - * @return string 图片 hash 值,失败则是 false : ]3 y# J) z' Q) Q- Z
- * */ 0 w. Q" a$ I8 L# r, f2 x8 X, a9 l
- public static function hashImageFile($filePath){ % Z; p7 l# u1 k' T( @; t
- $src = self::createImage($filePath);
4 z3 I: i" o, v8 o - $hashStr = self::hashImage($src);
4 W0 i. C# K9 _& H q5 X5 Q - imagedestroy($src); " {- Y2 Y8 @5 U- i
- $ o* {3 r3 S1 g0 i- y
- return $hashStr; 4 [; y+ p) a0 W; Z' }7 Y* T0 f
- } % `% P% S$ c8 A$ d5 j( [7 Y
-
m6 Y# ]$ @2 {" e - |" s3 ~, l" A+ X8 Y0 h
- /**比较两个 hash 值,是不是相似 # e% a7 D1 y5 P7 m
- * @param string $aHash A图片的 hash 值
, ^. P n: Y f( V: @( H4 p - * @param string $bHash B图片的 hash 值
3 {. D) a3 b) J - * @return bool 当图片相似则传递 true,否则是 false , ]# }1 z7 G( @ Z
- * */
- x/ h2 B5 k4 V+ l7 Y- f - public static function isHashSimilar($aHash, $bHash){ 5 @. ~& _/ E4 \
- $aL = strlen($aHash); $bL = strlen($bHash); 3 F5 A( r# _( s0 m- Z
- if ($aL !== $bL){ return false; } " v; F& P1 @5 Y$ L
-
. J/ _2 o9 G, q. c* w y: |$ S9 v - /*计算容许落差的数量*/ " F' d x+ }; l" a
- $allowGap = $aL*(100-self::$similarity)/100; , E/ q1 s4 b6 p0 Y
- 7 m2 L- L/ h4 @( L( Z4 F+ f& R, W
- /*计算两个 hash 值的汉明距离*/
* @) O8 F T: @ w) P. x, C4 } - $distance = 0; 3 d3 A u; N3 T( c7 N9 [; Y1 L; u
- for($i=0; $i<$aL; $i++){ 6 u3 p' N# s. o$ F; f0 H& Z
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
% g; E! t' V% E; H% ~1 n - }
4 j& z0 _# X# U2 a) y3 x -
) q: [/ j' c- z/ s) Q - return ($distance<=$allowGap) ? true : false; 7 e4 b/ h& K8 k- _; V8 \' O' u& U; }
- } 8 ?# I4 K, u0 Z) u' i+ ~6 _. j9 t
- ) Q4 n& V" L- n r+ }9 I3 O: q
- % n, }8 S G' N) ?; u$ f
- /**比较两个图片文件,是不是相似
+ H! @0 K1 a! G2 e$ l - * @param string $aHash A图片的路径
5 \8 I6 ]( d1 z) m - * @param string $bHash B图片的路径 " ~5 f. s8 B7 c+ L/ v
- * @return bool 当图片相似则传递 true,否则是 false 0 B Z0 n$ j! ]1 i7 M+ O
- * */ # Y' b0 A8 Y3 m; R( \! s
- public static function isImageFileSimilar($aPath, $bPath){ ; a2 |; ^0 n5 F6 P- e
- $aHash = ImageHash::hashImageFile($aPath);
6 p: S% f! T* ]8 z: m8 _ - $bHash = ImageHash::hashImageFile($bPath); ! A* o+ d3 R* o6 h
- return ImageHash::isHashSimilar($aHash, $bHash); % X* G& ?" k/ Y" z8 F4 N4 _
- }
" W3 u% D5 D6 s -
/ R. t$ C7 i6 E, y, ?8 ^5 `6 k4 v - }6 Y; P5 O b' N& {7 f4 u# O
复制代码
( q1 u/ J4 `3 R$ T
2 d& B' m( j, E$ I- d/ k/ Z; D |
|