管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图, D" O2 j B( t; }$ V3 A3 t
[3 ?0 j# i, p
: n5 i0 E/ w6 F. q% M; f# q) v6 J! p
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。4 p) v5 o" K+ g% `" v0 h8 f' x
- <?php 1 U: O8 S! B L# K# K6 G0 H
- /**
# R/ P4 y7 t' s7 R h - * 图片相似度比较 % Z! g# Q2 P! b* P
- * ' n* L! V1 X' H& {
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
3 _" U1 O5 o! _) s" c8 ~. r+ }' ^ - * @author jax.hu
9 P# X5 z3 S0 [$ V$ |% \ - * 9 a) V& ?0 j& w6 b5 Q' C4 }7 u# c
- * <code> 2 o: Z0 y! N" D
- * //Sample_1 # P1 x7 C. h3 `" Q, j
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 9 W. T: a1 H5 P' X1 s0 V
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); % j* r5 N, k$ F) i. V* [# S
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); % D- r* U/ ^4 J6 R
- * . n! k; [% v5 y$ C; R- h+ M! t
- * //Sample_2 ( k2 L/ u2 Z; M' L' a
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 2 V+ V! t! V2 s$ w6 W: E6 E
- * </code> * T0 E' s/ s1 X4 u {# S6 |, h. g9 j
- */ ! Q1 x$ \0 v/ ?+ T+ g% ~3 P
-
; A) f; h/ Q5 Q% r- Z! j* A - class ImageHash {
+ O! T6 @0 }1 Q% q - 1 R6 K: p- y+ v/ T# P# x" t
- /**取样倍率 1~10
* Q) E3 i& l8 f7 |3 o2 `% ?4 e - * @access public
! J8 I- I! y2 i b' I; h5 U2 V% w - * @staticvar int 6 ]7 I) P+ I# D' V# B2 G! i
- * */ 1 h- r% T. l& o6 v( J5 V4 D
- public static $rate = 2;
% l5 d- ?/ M L - ; Z# {& O/ c( e% W* c
- /**相似度允许值 0~64
) [2 }, g$ k3 M/ t - * @access public
* R- z* J/ Z- l% r - * @staticvar int 2 \1 W; s$ B* j6 y
- * */ 8 i8 Z0 N% x! ^, h
- public static $similarity = 80;
/ g3 h8 U% j! D" T1 X9 R7 Y - & `1 I; ~. a. e+ O# b* K- P, N
- /**图片类型对应的开启函数 ) ]7 \6 t. T1 Y& m, Z( c
- * @access private ' |4 E) m4 S0 L5 ^5 k/ n) X
- * @staticvar string
4 i) r7 V5 R8 r7 L2 Y4 E! ]( s - * */
0 l ~0 P+ D0 W% { i2 c - private static $_createFunc = array(
0 }, ^$ }0 J! j0 z; T - IMAGETYPE_GIF =>'imageCreateFromGIF', 3 k$ M' h5 d! H1 ?& O2 l% C2 G
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', / Z( T# k* R9 O
- IMAGETYPE_PNG =>'imageCreateFromPNG',
_4 @; s, r. u& O - IMAGETYPE_BMP =>'imageCreateFromBMP', # A0 `+ u- k1 b2 x
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 0 a! _3 e3 K4 {9 R# e% C5 \
- IMAGETYPE_XBM =>'imageCreateFromXBM',
3 Y' M& b) C9 S" y - ); : j2 G: ]% r8 z4 [: H
-
# `; V" P- l1 i0 D5 N) U' | -
$ v0 V$ V( L O& Q% n5 X+ E# v - /**从文件建立图片 ' ?" ^! {* c, r0 t+ {4 F
- * @param string $filePath 文件地址路径 / _, |! n( ~4 {; _, y
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
$ ^7 ]; I1 n4 K4 N* v4 c - * */
, n. s, Z. _0 U; q9 \8 K: G - public static function createImage($filePath){
# O& m* J* h# ^4 n" E - if(!file_exists($filePath)){ return false; } / R! a3 l+ y6 a: C& \
- . k7 x4 k. Q1 k( W
- /*判断文件类型是否可以开启*/ & ?# j! F0 _0 Y! s
- $type = exif_imagetype($filePath); / z/ j$ y4 Y! \% A; r
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
+ Z; F+ ?4 N, h/ P- k -
: R8 [- A& B$ c1 ~ - $func = self::$_createFunc[$type];
- D, g7 n% k* g" \3 `/ Q. o0 E3 K) b6 Z - if(!function_exists($func)){ return false; }
5 E$ [4 j1 o; {5 L# g$ \4 p -
9 _) @" O8 C+ p `! ~9 K# Y! E - return $func($filePath); |2 V c/ Z* U! U! X+ ~; p
- } ! O6 \4 }' n/ j# U
-
: s7 U) {4 i% o& x u - 0 i; z9 k3 @% m
- /**hash 图片
: L5 |3 o% q8 H( y+ X$ P5 E0 l - * @param resource $src 图片 resource ID . U$ Y3 ~# \/ J$ B$ M) q# h
- * @return string 图片 hash 值,失败则是 false
* V h9 G9 S: }+ [5 O! E: A6 r - * */ 8 `: H6 o2 e; ~* h( O1 o
- public static function hashImage($src){
1 y7 X5 _- l& J$ l - if(!$src){ return false; }
* p+ ^0 J. U5 e; S! y& m- W/ e0 w - : | d6 j- D/ {* M/ G, w
- /*缩小图片尺寸*/ : P u8 e/ W8 u& A8 p
- $delta = 8 * self::$rate; * \9 {1 d4 x. m L, U" T. \
- $img = imageCreateTrueColor($delta,$delta);
# e7 _ F, J( J7 \7 N/ N - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ! M9 w( d: `% W
-
, J; N8 D+ A+ Z; i6 O9 s9 B7 f& h - /*计算图片灰阶值*/
0 z$ [6 P, Q6 _# `2 _$ ?; J4 x3 t" J - $grayArray = array(); 3 ?* F( D* Q8 d; f. }, @
- for ($y=0; $y<$delta; $y++){ , y6 K) ?6 ~0 b1 b! C
- for ($x=0; $x<$delta; $x++){
; p/ V% y/ ^$ U# v - $rgb = imagecolorat($img,$x,$y); 4 m* c. {) y- a& B2 B( P: e& G& L8 F
- $col = imagecolorsforindex($img, $rgb); " }/ H' {, X/ E( p4 z
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; W3 d) q _9 Y; {
-
a5 B$ N1 _" n7 Q8 s4 M - $grayArray[] = $gray; 9 \: E9 U" ^5 n3 S+ p1 u
- } $ a* x; z1 k8 p' E
- } C4 `( H G) `* u7 r! r
- imagedestroy($img); , A) J5 |8 E0 k g
- : k+ ?7 D; V0 j1 k/ F' S
- /*计算所有像素的灰阶平均值*/ * D# I/ ~- `( g' B0 E+ z7 P, Z+ { z
- $average = array_sum($grayArray)/count($grayArray); 9 J) _5 G* c0 E- i/ o9 I
- 0 v9 m3 I& a; o
- /*计算 hash 值*/
1 Z' K1 n# Y# Z- s" P - $hashStr = ''; 6 K6 h D( h$ S- A
- foreach ($grayArray as $gray){ 7 R; @7 A3 j, `/ @
- $hashStr .= ($gray>=$average) ? '1' : '0';
$ Z* X5 a' r3 _+ G& n) k6 w - } . s* T& Y7 m3 L) a
-
f7 z/ i- ^- m+ L; o+ O - return $hashStr;
5 @8 O8 p1 p% A; S& w - } ) j. K! Q* Y1 W. O5 D; o
-
. ?4 D0 S, y: `, F) e% W2 U - $ v; x# P' D) ?9 {& b+ M
- /**hash 图片文件 D& h, T' L& \% A2 u: a, _9 V$ i
- * @param string $filePath 文件地址路径 4 x' `) S+ H2 I* N# K& B Q
- * @return string 图片 hash 值,失败则是 false
/ z, \2 w, {) H+ U" J4 e/ C - * */
) d S3 A' p; c1 z+ ~- G - public static function hashImageFile($filePath){
0 c/ t8 M2 ?* V6 k - $src = self::createImage($filePath);
3 ]' ^/ ]8 x. b, f - $hashStr = self::hashImage($src); " }: t8 {. c! U, Z" A/ q
- imagedestroy($src); 4 q9 E4 O) [* A
-
! L7 V8 |& t# l& T8 M - return $hashStr;
% \! c8 G2 ^/ ~( I - } 3 K/ k0 S* ]; e* m- {
- : C, Z) z# V- M) Z
-
( t7 m( M/ m0 z4 a - /**比较两个 hash 值,是不是相似 3 l3 B! F! x9 y: {' ^
- * @param string $aHash A图片的 hash 值
+ {* J2 F; ^8 Y( x: N - * @param string $bHash B图片的 hash 值 / m& Y# V, V" J
- * @return bool 当图片相似则传递 true,否则是 false
. r7 n" J6 }( q - * */
; s% \$ x0 |& o( v: f - public static function isHashSimilar($aHash, $bHash){
" O8 [2 G* V/ W: q& R0 `9 u- q - $aL = strlen($aHash); $bL = strlen($bHash);
: c. R7 q: n3 S' B5 } - if ($aL !== $bL){ return false; }
" X8 C2 \6 X5 ?, o! m3 \7 w* X% m -
+ _5 ]$ i6 ^1 q+ b - /*计算容许落差的数量*/
3 K/ C) ~8 W! _ - $allowGap = $aL*(100-self::$similarity)/100; 0 l+ |+ n8 _0 z2 b) n5 G
-
3 j% P3 W" O7 _- U) i9 f - /*计算两个 hash 值的汉明距离*/
+ @" u: b8 ]4 g- F5 s$ O - $distance = 0;
) I3 I+ t7 _) u3 k5 R3 A' g - for($i=0; $i<$aL; $i++){ & j8 _ C2 N3 O& S4 [/ B6 }* A" X' Q
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
* E0 O1 N5 J! L; b8 ?; m - } & H- g5 D2 X( S3 Y
- + I) ?3 J9 b! J# K! o6 B$ [0 [
- return ($distance<=$allowGap) ? true : false; + D& n7 u% r0 x; @
- }
, \/ V/ w: ~: w+ R( r7 m( b, ] -
) m! @7 o' v) S4 T1 ^5 i - % r, e; ` p6 ?
- /**比较两个图片文件,是不是相似 # S) W7 N& q' n k( n( B
- * @param string $aHash A图片的路径 + J/ U' h" g, U. l, O
- * @param string $bHash B图片的路径
2 Q. ?* w7 D+ `$ y+ }8 N& ` - * @return bool 当图片相似则传递 true,否则是 false ) w7 d. W& S# A7 ?4 y
- * */
2 K5 Y+ M7 U. a& a, ]" X% p3 k# z - public static function isImageFileSimilar($aPath, $bPath){
# q) t$ c2 e. K( @ - $aHash = ImageHash::hashImageFile($aPath);
Z, Q/ M' ^- `! O7 Z5 P! @! r - $bHash = ImageHash::hashImageFile($bPath);
. @, X/ _0 {) X( E0 H - return ImageHash::isHashSimilar($aHash, $bHash); . e) l' K: b/ Y8 i, f
- } ! ~4 M: U# m. d7 |& f
-
, I, \* b& v N1 g2 y9 i - }
! B: {2 }- {4 P. T0 F6 C0 h
复制代码 $ ?) Z/ Z! g3 M$ D" m
1 X! B/ h# q. J# k5 |* h1 b: Q |
|