管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图' R" n1 w; A3 ~7 o/ e
1 _# ?1 t( v5 _% C: `& X7 L. i
6 z) A4 {: ?0 [, S8 l4 @" n$ r
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
5 }) Y! s. ?* c- <?php
. H6 s# k6 ]5 _6 m% K* f$ @ - /** . ^; I8 }/ \1 N+ U
- * 图片相似度比较
6 m& H d, q4 _: e3 d. C' N - * , G/ B; A$ i. y
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
# `4 w/ h, H& H2 K2 P+ W* O" s - * @author jax.hu
) Q/ [4 c; F: P2 V. S, q7 {4 K! X( @ - * + m2 L: q! E. q" q j$ Q
- * <code> 1 p; E2 _+ |, O- n5 x2 k
- * //Sample_1
4 N1 ]- U0 ]- G) c - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
- H3 \2 w7 O! M& N/ j! f0 E% K: L - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
, _" X# _: N. b* X9 j - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ! z: `# G( ?/ v- O1 g" [
- *
* d9 ~2 z; ~% R3 h5 ~+ c4 B: u - * //Sample_2 ( g* }, R+ {( F) ]! B0 R0 W0 T
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
/ q5 b/ U6 o( U: U - * </code>
0 U5 Z- W6 E/ ] - */ 4 P0 P, i$ f5 h2 j
- 3 L. x& `1 y# M! u5 E4 J+ H
- class ImageHash { $ i4 M* `+ F; q1 h3 T2 u
- ) X" Q; o/ T$ }7 G* ~
- /**取样倍率 1~10 . K& i3 V9 V3 G/ Y3 b3 a) ?
- * @access public
( S G' r+ W2 C/ m. V - * @staticvar int ; Q+ h3 }9 X4 ]+ ]' F: W. G5 M
- * */ i( O+ K6 { ^( X1 @# e( u# d* A
- public static $rate = 2; 6 {2 z0 J/ t9 v
-
* E% z; ?8 S$ @ - /**相似度允许值 0~64 5 e8 y3 g6 h& k% _8 e% R
- * @access public ! s1 c" A+ P8 v" d% ?0 C
- * @staticvar int
$ ~% T' V7 |4 o - * */ B. p x) Z6 q3 g5 L: Q% N
- public static $similarity = 80;
' R8 O4 h. N/ {- H5 j2 ^3 R l - 1 u8 x1 F( y6 y) s0 j
- /**图片类型对应的开启函数
& ?! f' a% y4 H9 ~7 m8 Y - * @access private 6 F# ^6 e, o/ H+ v3 Y" l
- * @staticvar string
# ~ ~. G$ l, C5 }* K% G/ e& E - * */ $ @( Q1 O0 ^% y/ I
- private static $_createFunc = array( 5 i/ E ]* G8 e' S3 b
- IMAGETYPE_GIF =>'imageCreateFromGIF', $ W: O; [2 {* ~
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ( R3 ^2 d" e3 U3 a! `3 s
- IMAGETYPE_PNG =>'imageCreateFromPNG', 1 n# i; Q) A8 ]6 R, N
- IMAGETYPE_BMP =>'imageCreateFromBMP', : E2 j) G7 o; k+ I/ \: G( a
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
3 x9 p# x4 j# v% H - IMAGETYPE_XBM =>'imageCreateFromXBM', 9 t% r$ i, ^) m5 [- i8 G: y6 l7 P
- );
' }5 Q5 K! @1 k -
$ v7 R: P! }# H8 a# `' S -
: c, N1 f+ Z6 s/ k" | - /**从文件建立图片
& m3 R2 Q5 V8 O Q1 p: a - * @param string $filePath 文件地址路径 + q, c& E# X6 M$ S! A
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 4 J' A# y& X8 }
- * */ & e( C: J+ u- [* B) N
- public static function createImage($filePath){
1 v/ ~) U3 e0 S) O - if(!file_exists($filePath)){ return false; }
; B/ C! N0 m! P `3 T - 5 h6 l5 A8 s% o2 c" o. s; [1 p
- /*判断文件类型是否可以开启*/ 1 Z" q: o& w, |2 K3 o/ d
- $type = exif_imagetype($filePath); % W5 R: `, {: W0 F$ i
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
5 b" {6 g: ^! ^2 P - & e/ H* n" X0 U7 X H
- $func = self::$_createFunc[$type];
( L. C/ P9 ^, A/ g. ?1 n5 h# T - if(!function_exists($func)){ return false; } 2 y: w, \& _2 h' g' T; g, ?; h3 O* U
-
; c6 E! W I. ]* D$ e( m- {6 a - return $func($filePath); $ r( d& u# P$ G
- } 7 B5 x8 d4 y2 I2 |- {, K1 r
-
7 b5 N+ l6 u* ~, U: U. b0 k( y - - R5 v4 S1 ~6 W& W8 E' l. Q
- /**hash 图片
& w9 Y( d1 K6 ]# i R) H - * @param resource $src 图片 resource ID
& O" X [$ Q; ]+ G5 Z" S - * @return string 图片 hash 值,失败则是 false ' B1 E0 H) {1 q" Y7 Y* C5 y: W5 g
- * */ # q0 f" A9 p" O3 s2 D: w
- public static function hashImage($src){
5 r; o; y: X9 a+ I( x3 b! r4 x+ j% U - if(!$src){ return false; }
# S3 h5 `4 I0 _9 }7 D% a" y -
, Q( o+ _* f% \- J - /*缩小图片尺寸*/
* @0 ~; ]1 ^% N1 r- E( a8 z - $delta = 8 * self::$rate;
5 ~6 X& u2 x& g4 C$ d - $img = imageCreateTrueColor($delta,$delta);
! r! V3 x) ]% Z5 u6 ]9 q$ V - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
; b% ]( p% {: f0 y- J. ~: Q" H -
2 O, a; S2 f q* c - /*计算图片灰阶值*/
. @2 ~! ]* H: S S$ m/ D/ T - $grayArray = array();
( R6 g3 k) d2 C( d0 U. j) ?4 F7 v - for ($y=0; $y<$delta; $y++){ ( m: ^3 }' N, F# R2 W) \; V
- for ($x=0; $x<$delta; $x++){ ) R, o; g( j+ n0 S1 [
- $rgb = imagecolorat($img,$x,$y); ( f* L0 k& c/ X7 S Y
- $col = imagecolorsforindex($img, $rgb);
. n1 B: x0 p; B$ u1 z2 [ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
, I0 [ @; S0 V. {/ {+ N& T K - ' v8 p2 F- k/ H/ w6 N
- $grayArray[] = $gray; & g6 g- e. h8 ]9 m7 F) y9 x
- } * E- I& ?' L8 y# [( }
- } ; n9 ~4 ]# V! x+ U0 z
- imagedestroy($img); 5 F9 U2 [9 T9 L" O% F$ v
-
/ A& r( [1 s+ t7 y5 B( m - /*计算所有像素的灰阶平均值*/ ) E& `. p! [ D1 Y
- $average = array_sum($grayArray)/count($grayArray);
& ]9 G& D1 R% y8 v% Y) E4 j' i - @, U R$ N- C
- /*计算 hash 值*/ + `- H, }1 {# z6 i" S1 `6 P4 s
- $hashStr = ''; ( Y. V8 X6 n2 ` B1 T4 I8 `
- foreach ($grayArray as $gray){
7 b% o1 a7 B$ n& w9 S F - $hashStr .= ($gray>=$average) ? '1' : '0'; 7 b+ \3 ?( D5 J5 t8 m1 J
- }
9 ]2 q& }! P$ P1 `7 S - 2 x+ z, N: ^+ ]& J# j# X- Q" Q
- return $hashStr;
: ?1 }( z ~; u( j; f, O - } 5 [( ]. U u3 n
-
5 X4 y4 v: R3 i: g -
% Y+ J- A3 C, h" m8 p% A* x0 O - /**hash 图片文件 * h6 S# p- W! T6 ^5 m! `% ~
- * @param string $filePath 文件地址路径 - m& `5 |* K9 m+ ]: _# E) P
- * @return string 图片 hash 值,失败则是 false % K( O) x. |% _8 v3 q9 V# S- W
- * */
5 r( C8 t* A' K6 C' l, G) c - public static function hashImageFile($filePath){
+ e1 D# D1 W+ L+ ?, f; E6 _' D - $src = self::createImage($filePath); 8 X' {7 l. Y$ d+ N8 e" e
- $hashStr = self::hashImage($src);
/ R& s! c% b, G$ q9 ^/ d# V4 L - imagedestroy($src);
: t8 m$ Y, } K% {( k& U% s - 8 T0 _# t! |) r8 k
- return $hashStr; 1 O3 o7 ?$ i& D O9 r" u
- }
+ R# j* Q; S1 E8 M+ W. f/ S1 F -
: d: F( }$ F) l; ]9 V. V -
* Q- ~7 P! o! I& J9 b5 U5 J8 Z$ R - /**比较两个 hash 值,是不是相似 ! n8 {1 N' x$ Q( f$ N
- * @param string $aHash A图片的 hash 值 % R0 g& ?) e; o4 K# A5 V( |
- * @param string $bHash B图片的 hash 值
* k3 H/ H/ k8 \* T. O - * @return bool 当图片相似则传递 true,否则是 false
* |- q* o9 e- l* m2 i/ l3 L* k - * */
1 S8 R3 p* G4 K2 P) ? - public static function isHashSimilar($aHash, $bHash){
9 p: n0 c& h2 X - $aL = strlen($aHash); $bL = strlen($bHash);
8 M7 M! L x3 a: a5 I) F3 \ - if ($aL !== $bL){ return false; }
1 z( H3 z5 l* J4 n8 y2 Q& x -
; O: M4 B' p, D$ [ Z; J$ ?; W - /*计算容许落差的数量*/
2 l! D- Z' X6 M7 [3 G - $allowGap = $aL*(100-self::$similarity)/100;
' X$ I) v7 l) ~% b% \2 g - / C; g& P" C! R0 H' i3 C- {' `: }0 u
- /*计算两个 hash 值的汉明距离*/ 9 h: ^! _+ D% _" ~3 j- R; e0 C
- $distance = 0; : ~6 Q" l8 `' U
- for($i=0; $i<$aL; $i++){ % m! A: T9 R* v7 ?+ _# p. G% v- `
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
+ p+ M6 V9 V1 r& B5 Y2 w' j% w, s - } " ]+ h5 {1 e! ~8 O, ^
-
' O+ |8 S: D8 M& H5 L& h* i - return ($distance<=$allowGap) ? true : false; ) `0 `" N' C. ?' i( V
- } 2 | M1 I( {4 |1 v
- ! {. R/ h, T# B P
-
# g5 U+ K2 ~+ X. S5 [, W: J - /**比较两个图片文件,是不是相似 + N; u) j: Y# D6 _! E
- * @param string $aHash A图片的路径 ) ]# V" s* d |4 T
- * @param string $bHash B图片的路径
" ]* K, J8 j1 e( x" O - * @return bool 当图片相似则传递 true,否则是 false
" ]# T, m8 \ G! R4 c) G- r- j, d - * */
0 @$ c; b6 W5 n w, y2 r - public static function isImageFileSimilar($aPath, $bPath){ 7 C5 ]3 r5 L: f
- $aHash = ImageHash::hashImageFile($aPath);
) w( N Y# e0 B K6 n - $bHash = ImageHash::hashImageFile($bPath);
& d, W% {1 ~. p6 N8 s - return ImageHash::isHashSimilar($aHash, $bHash);
1 M. _3 I. a& A1 h' M - }
: N0 ^2 D# v8 o" @* g1 U* N - % w: O) }( i3 x3 H2 b7 v
- }7 \ R0 S5 J; y# i0 ?- \
复制代码
. a% w6 r- I' {) I; @, C: F% u& \" T6 }6 ?4 k
|
|