管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图/ x) f! D5 q$ [# Z [9 C; G" R
" E( g7 Q1 M9 X: y" i8 I( N5 @
, w1 B8 [) x- r6 c. o6 j由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
8 u. p1 r9 \# ~/ b1 N% |- <?php $ M' ]4 l+ M2 L+ y
- /**
2 x$ E& i$ P# Q) l( U1 r3 s - * 图片相似度比较
, c4 h8 C! s5 r8 t - * + M$ z+ P& C6 _" S, u
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
* X: Q, y$ F. ]+ K( E0 }: U l - * @author jax.hu
: k9 A4 t7 V1 X6 e' p) r - * ! k% s# i3 x5 ^ }" u
- * <code>
& }" a) S( O" _+ B3 j5 c9 u' K - * //Sample_1
7 m* ]7 V- N5 O7 N6 V - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 2 _9 s8 E$ y: i# W; E; D# V) o
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 0 `; ^. S) u. _" R3 R* U2 f
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); & ]: u* o6 k, c$ R# j6 L' ]
- * 8 d4 L; J/ h7 g& G, C* _0 D
- * //Sample_2 ' |2 _7 T: R8 o
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
2 r/ s, F. _2 Q3 g6 k& P, i$ C/ B9 X - * </code> 3 u8 ]# I6 ^4 K7 i, r" H; S
- */ 8 O# ~$ y- o( R
- - i2 O5 d$ ?0 `2 m
- class ImageHash {
8 ?0 S, ~( Q* o4 j0 f5 w8 G - m& x' C. J" g1 t, D" z
- /**取样倍率 1~10 8 q, @3 h# Z" T
- * @access public 3 V5 R9 a. h. I4 X; `
- * @staticvar int 0 e2 K& U- {# \7 }
- * */ " R2 j% D. D' G
- public static $rate = 2; / N; B8 r8 n- i0 C# o/ o
- / |* E1 j" Y3 P: G5 V
- /**相似度允许值 0~64
8 N- u2 K9 O4 U4 P, O. K1 X# Y+ R' r& F - * @access public
% h& {* c S) E - * @staticvar int 2 X) Q( E- X$ L. L+ p: C
- * */
& G' e% K# E9 Z' @ - public static $similarity = 80; ( @( ^4 j2 `4 v
- " z* k) H+ M' e* [7 q
- /**图片类型对应的开启函数
; L7 {0 d7 X& h: [ - * @access private G Q4 ?$ o. O" u) s4 g
- * @staticvar string
5 E) B% L% n) V- y - * */ ) P8 Y' E/ }! Q$ e: J( v" v. x5 `
- private static $_createFunc = array(
( m8 t6 {" I* Q- \- I6 d% a) B - IMAGETYPE_GIF =>'imageCreateFromGIF',
2 R0 M. }4 E& G; y2 P) h Y( @ - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
7 L) b% V X9 p - IMAGETYPE_PNG =>'imageCreateFromPNG', ) K) ~) y) M# {& t: {1 I
- IMAGETYPE_BMP =>'imageCreateFromBMP',
% k8 M; f0 K' r2 n+ c! h8 E - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
- u4 E7 ?/ ?6 J; j1 @/ r - IMAGETYPE_XBM =>'imageCreateFromXBM',
0 ~6 }4 O/ X- W3 B/ U! K8 o, `' E - ); 6 t: v; j. Y. P' |! N; }0 d
-
2 m, D& m: Y+ a. \$ [3 v% v, N -
9 M! F( T$ A2 [; b! I- k7 \/ [. m7 B# Y9 v - /**从文件建立图片
' I0 a* o1 G! T" e - * @param string $filePath 文件地址路径
* o$ h: w' @7 F& {- |% ^ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false . H, M9 B' L( X7 h F& }& Y# z
- * */
) T, t: `. G3 u8 z% y8 l: y9 r; j - public static function createImage($filePath){ 3 B" o, x. `9 u
- if(!file_exists($filePath)){ return false; } ! F6 ~+ r/ P- |/ c4 V
- " d2 m! L, e% U8 B9 e2 A
- /*判断文件类型是否可以开启*/ ! u0 N; J) y/ i0 Q& t
- $type = exif_imagetype($filePath);
( b1 f) Z. z9 I* ?+ c - if(!array_key_exists($type,self::$_createFunc)){ return false; }
/ ^( s3 p, L: Q8 `6 Q$ Z -
' n/ V3 J( h2 ~* n) a- Q% E2 A. S - $func = self::$_createFunc[$type];
) K5 U4 S% P6 G0 u; F& U - if(!function_exists($func)){ return false; }
4 y% A4 j' Z3 Y/ U -
. x% ~( A% p* N$ E2 |2 i - return $func($filePath); ' w/ u3 O9 `' ]% p1 U+ v
- } + Y8 ]8 e7 S1 X2 u. C
- 8 G3 ?9 W* l0 ~
- 3 c! [+ f* z+ L' P0 e$ e
- /**hash 图片 3 [3 a: n3 j5 {
- * @param resource $src 图片 resource ID - p; n% X/ s3 O
- * @return string 图片 hash 值,失败则是 false ! j$ P: f8 D- n" m3 @# @
- * */
; o* X/ U8 O+ C, j! I - public static function hashImage($src){ ]# d4 b" \* Z7 t
- if(!$src){ return false; }
" o: r$ m C, u" c - & I( g# y; ]# D# G5 {
- /*缩小图片尺寸*/ 9 g* {1 A0 `$ @$ ]7 z1 j) e, O. z# ^: L
- $delta = 8 * self::$rate;
/ Z" O; g- c+ }' m8 K3 A& O - $img = imageCreateTrueColor($delta,$delta); 8 U. Y0 Y1 R5 w
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
# D! E5 e6 m( i8 q4 s( F) m- S8 t - ) A6 ~. ~" P6 h0 v( h
- /*计算图片灰阶值*/ / h$ C" d1 Z$ |: U/ j* e0 f
- $grayArray = array();
* ?1 V; Y; K0 A* N8 V; F& t - for ($y=0; $y<$delta; $y++){
: x4 k0 z5 F4 r3 F4 a - for ($x=0; $x<$delta; $x++){
& t4 J$ I1 H. L1 V% c. L# n ~ - $rgb = imagecolorat($img,$x,$y); , i% ]7 h$ n+ @1 y8 n
- $col = imagecolorsforindex($img, $rgb); . m6 q. y- w( @& S" L3 ~
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; : Q3 G& ]8 W% G6 F+ Q& u' z/ U
-
$ x) [8 f+ I& L6 T( ?; f - $grayArray[] = $gray;
! O5 w& Y% j3 i/ n - } 5 o$ }$ z" k2 f C$ w* k3 N! u
- } ( R) r- V3 C7 f2 ~7 H
- imagedestroy($img);
4 J/ {$ M! r( G: i, r - ! n# h9 o. [ i" |0 o
- /*计算所有像素的灰阶平均值*/
/ v- O3 R, K% d3 `8 C3 L( Q - $average = array_sum($grayArray)/count($grayArray);
4 S# C i4 R/ r - 6 w% Y# G. G- o: c7 N
- /*计算 hash 值*/ / ]: i J5 _. t! g1 Z, F
- $hashStr = ''; & o _ `# l3 J9 Q" G8 L9 r
- foreach ($grayArray as $gray){ U5 _, C+ k1 O5 F2 L' U
- $hashStr .= ($gray>=$average) ? '1' : '0'; ; K$ ^( N3 X& J
- } " K( [" z2 Z+ \; ~0 h9 h
- ; {; z: Y* a2 ^, ^0 _* D( c3 S# d U
- return $hashStr; 8 t# |- J" g ]$ d0 k
- }
; r" z% [, X! V8 v+ X - : U. e, G2 d9 @) M1 @
- & J5 \) z/ A5 g) ?, a; \
- /**hash 图片文件 $ C/ V1 Q4 ^: t: I! x$ h
- * @param string $filePath 文件地址路径
% j) ^% Z" j; k( E - * @return string 图片 hash 值,失败则是 false
2 N7 A$ d+ e) ?& A - * */ 2 I: m+ |" q. n) c* [' D, n
- public static function hashImageFile($filePath){
$ }) |# B0 k: F6 Z4 i2 h6 w - $src = self::createImage($filePath);
6 s9 X7 G! {) V; G* b3 y# Y - $hashStr = self::hashImage($src); * l' d) W% L1 l# `5 o
- imagedestroy($src);
- }# g( l9 o0 L( v - ) k7 k2 y! @5 L8 L! K" t0 n/ ]
- return $hashStr;
6 c6 c+ i+ }3 r. W- p - } 2 i% }5 ]5 z N0 F& T" `! H; q
- / s3 ^. {, a/ l3 A! [
-
, c% S$ l3 u$ r" b& f4 {6 n - /**比较两个 hash 值,是不是相似
y, |& A/ I" q* g$ A/ m - * @param string $aHash A图片的 hash 值 9 H7 X7 Y/ z& u9 i& Y. z
- * @param string $bHash B图片的 hash 值 / c/ c4 X, k) |. i7 E; ~4 U
- * @return bool 当图片相似则传递 true,否则是 false 2 z( n: }) X# K# h% \
- * */
( s Y9 Z$ I& t( s8 j- A, [ - public static function isHashSimilar($aHash, $bHash){ 2 v( T0 T4 s, N: \+ J! i
- $aL = strlen($aHash); $bL = strlen($bHash); ( X7 u( P5 M6 {. w% _. Y
- if ($aL !== $bL){ return false; } 4 [- ]3 u7 A& b/ o6 r
- 4 j* D5 Z3 X- n Y1 C
- /*计算容许落差的数量*/
! T- \% e# g8 Q1 r* l6 i/ O - $allowGap = $aL*(100-self::$similarity)/100; * E( L1 `9 s3 d' I- z6 X w! E8 Y
- : R) b) r; o% _2 R4 F8 @3 I: }
- /*计算两个 hash 值的汉明距离*/ * X2 ~) E- @7 k" _
- $distance = 0;
# O* ?, N: q; J3 C5 b - for($i=0; $i<$aL; $i++){
2 f$ a8 t, d5 g; v/ T - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
3 Y$ d5 j& u: y% o4 w1 J - }
: l9 H, [# w7 r7 \ -
7 B3 L7 R3 G* F5 }8 b+ M6 D - return ($distance<=$allowGap) ? true : false; @! A. r, G. K+ h* A- l% p
- } ; G3 K# A+ B5 S( [% v4 N
- : g, Z% Y, d! ?, Q2 s
-
8 p, V5 U& C; P* K: V! | - /**比较两个图片文件,是不是相似
. X- |/ [+ R5 O/ p* t6 M - * @param string $aHash A图片的路径
1 j+ _% D' R J6 m+ ] a - * @param string $bHash B图片的路径
; O& [% \2 s+ I# v4 }) `8 q( N9 R8 g A - * @return bool 当图片相似则传递 true,否则是 false
" d# k# }8 ?0 G5 y. h y! f; a+ x - * */
7 g* q2 u) o" d+ k1 {4 n - public static function isImageFileSimilar($aPath, $bPath){ ) [+ h1 _' h' V
- $aHash = ImageHash::hashImageFile($aPath);
8 |6 X# Z( u. v+ [ - $bHash = ImageHash::hashImageFile($bPath); 3 C* Z9 @4 t$ w7 N
- return ImageHash::isHashSimilar($aHash, $bHash);
9 ]% X% k0 h' [) R - }
/ M# q/ c% E7 D3 r9 j" d - ' }4 t7 y; }9 J
- }
$ |; C; y. ]: W% u$ p
复制代码 ! y W) E5 L3 |/ A% Y2 H1 M
( g! o6 k! c. ]% D! G8 T
|
|