管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图3 ~' r8 ^6 J8 }: [$ {% H* i# j
7 H( `% E) S( h* e
' w3 n! _- R- e4 ] N由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。; B* E1 n' c1 J) H; n
- <?php - N, O. B# T1 ]$ H' {
- /** 0 y5 y8 S7 L5 o
- * 图片相似度比较 . z: C3 [7 n. o
- *
( q0 @% k6 p/ m. B9 \ - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
% ]5 d+ `7 r, G! x% j- c3 a - * @author jax.hu 4 Z% K- F) o6 F$ r+ w
- * 3 _% Y% e, x! @; `. J
- * <code>
! s( p. ?" ?, Z+ E4 a - * //Sample_1 + n1 t4 |/ i8 w
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); n7 D/ Q* E( q/ Y) a2 g
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); . \4 r" g. k5 k3 e/ T* ^+ P
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
, G. U% f, e: O6 }* { - * 3 W6 Z4 m% q& F
- * //Sample_2 & ?+ r$ l) {1 y8 \: r! {: ]
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); T4 i. n H/ W% d. D. P
- * </code>
: }3 ^+ f) H6 `8 x. {: E - */ . B7 Q, N5 v' i5 r" _
-
, u4 f% m" A( p7 j( F8 H! w - class ImageHash {
% q: p& B# M- e, G9 k$ S -
5 ]( F/ y ~; J7 U: m" ]' I6 V - /**取样倍率 1~10 / |* z/ a$ C. a* X; Y7 l' r) }
- * @access public 8 M* s" ?; O! U' w
- * @staticvar int
8 i% k( f( B+ ^/ b: Y7 `% ^/ l1 Q - * */ , G4 i; Y3 C: c, ^% b
- public static $rate = 2; 6 X- n& ?2 z) n9 a; v3 e% f
- 6 h' r) R$ ]1 N6 ]& b
- /**相似度允许值 0~64 1 a) p3 x5 @+ _/ I; ?0 Q/ R2 H3 _
- * @access public
/ T! V5 O2 y9 u0 H. i- ^1 g - * @staticvar int - {1 Y1 l/ Y5 v6 ]
- * */
' q6 I+ y' B4 ^# \5 `* { - public static $similarity = 80;
9 T& ~2 @. K/ b$ _' f -
B9 [0 Q1 y/ |7 j4 P - /**图片类型对应的开启函数
& t6 V5 \ g+ c( c - * @access private
; s$ r6 \0 K; W/ k" i - * @staticvar string
+ h7 J6 v- U0 z* B- v- ~ - * */ - ?, @1 l2 q& d# U8 V
- private static $_createFunc = array( - R) s; o7 M7 |$ ]3 a( O0 v6 I
- IMAGETYPE_GIF =>'imageCreateFromGIF',
! J% g' _* T% V0 G8 k5 {: Y5 k - IMAGETYPE_JPEG =>'imageCreateFromJPEG', : E0 b$ O* {1 J
- IMAGETYPE_PNG =>'imageCreateFromPNG',
: B7 v$ }6 p# F7 p( L- `: Z/ o - IMAGETYPE_BMP =>'imageCreateFromBMP', 5 C& Z0 T4 n1 H
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 8 E, |3 v! \1 Y) S5 C' i
- IMAGETYPE_XBM =>'imageCreateFromXBM', . Q8 |# Z% V) f1 _9 \* U, @( J8 V
- );
& f# ^; x0 @) o# E+ Y( D9 F- _ -
+ [7 @2 Q! d( n - 2 e+ c& u2 q# G3 ^8 r
- /**从文件建立图片 ) s% T( U1 C2 G* M
- * @param string $filePath 文件地址路径 ' l, h& d1 n" _) ]* ] X6 w% W
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 5 ^$ ]* @1 I0 {% V3 f5 V+ [7 c
- * */
7 a: O$ F7 @/ t' H - public static function createImage($filePath){
1 u7 c2 f) Z Z' W( @" n - if(!file_exists($filePath)){ return false; } 0 y+ @% ?" K9 e" y7 y6 W* L! ]
- " e% R+ F; M# S! H
- /*判断文件类型是否可以开启*/ 4 g# E) W# b' B% x6 o
- $type = exif_imagetype($filePath);
! n' y1 A5 v! c - if(!array_key_exists($type,self::$_createFunc)){ return false; }
' F' @: o9 `: C! L- Y -
7 k& O7 X8 N0 m - $func = self::$_createFunc[$type];
9 H! v) e7 C2 d9 w' N6 ~3 o3 | - if(!function_exists($func)){ return false; } ) V# W$ l& h1 V
-
0 f. V7 C- ?+ [: u- {) Z - return $func($filePath);
3 V) A2 q. [" |/ H( y. f% k - }
4 j/ f! a% l) u, V -
' j( x$ r6 f$ L; g9 S+ v -
) j& V1 K9 q9 K' ~ - /**hash 图片 ' r ]$ m" o3 F- p( A' G
- * @param resource $src 图片 resource ID
7 Q# z) P, e3 }& | - * @return string 图片 hash 值,失败则是 false " I1 j3 @0 A+ f2 l1 \
- * */
4 V3 @, F3 G- ~( N - public static function hashImage($src){ : N% I1 h3 ~% O
- if(!$src){ return false; } 9 T; ^' H# H+ o4 ?9 h
- , d# m$ a* V: ]1 s# l6 u
- /*缩小图片尺寸*/
4 H4 h* L2 \) ]9 t/ j: h - $delta = 8 * self::$rate;
0 a; B( P( r l0 `9 A - $img = imageCreateTrueColor($delta,$delta); & [( v7 b- L# q9 |3 S4 _: f( J. t
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); * E2 x6 u0 `* ^* m1 b+ o* i+ ]
- ) x( X8 a* H$ p _
- /*计算图片灰阶值*/
/ q; U0 O/ A/ ?% m0 v0 J - $grayArray = array();
. y# e# s5 Y4 `6 w4 p' K - for ($y=0; $y<$delta; $y++){ ! \0 W0 F+ _% [' e2 p
- for ($x=0; $x<$delta; $x++){
* v( S- I/ p2 T t/ J, t; q/ U - $rgb = imagecolorat($img,$x,$y); 7 Q9 K% A& t) G. ~& t
- $col = imagecolorsforindex($img, $rgb); . i% I1 k0 e1 S! G
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 9 T6 ~" i5 T+ R( u v
-
4 `) t' U. b' U! @ - $grayArray[] = $gray;
2 l1 A( D2 g5 `& z* v( T) y - }
0 C& J R, \2 x- ~9 I - } 6 W" I$ u% A; W0 c5 d( r, K+ n; E
- imagedestroy($img);
% r4 ]% K& y+ E' t/ [ - 3 F. G7 b, \; Z
- /*计算所有像素的灰阶平均值*/
; G- F+ c. L4 J+ U - $average = array_sum($grayArray)/count($grayArray);
0 r( b8 s l/ i7 \ - ' Z) q* C4 y; R1 M, ^
- /*计算 hash 值*/ ) ~3 y8 O5 M' I- g* W8 y2 j* ^
- $hashStr = '';
% c5 \$ K) N) d" x3 g; O" R - foreach ($grayArray as $gray){ ) Q+ u9 H! G* y( U: X
- $hashStr .= ($gray>=$average) ? '1' : '0';
7 _' N W( j( P5 s6 Y: _! H: w5 ` - }
7 n, A2 m' U1 w1 f9 F i* G7 _8 S -
' @5 E1 M% o h* e9 T0 \9 z - return $hashStr;
3 O2 {' D8 \& u7 |4 @ - }
# v5 r1 ^! N! P -
" Q/ }: ?; B" g% p7 t! A; x - 2 I8 p$ u8 S7 c/ v' f \
- /**hash 图片文件
) U5 c' |4 D' {/ U& M - * @param string $filePath 文件地址路径 ; x) y$ ] Y$ {
- * @return string 图片 hash 值,失败则是 false 5 E7 l! w1 x& j, {
- * */ & w) H% I7 E: p6 }
- public static function hashImageFile($filePath){
* I- q. T, L! J L M. P& M( M - $src = self::createImage($filePath); 1 D: f7 ~) N$ l9 h# U6 S! a
- $hashStr = self::hashImage($src);
l+ f* z1 d0 q - imagedestroy($src);
! N) Q* B! f( X) r; l9 } S9 a -
0 }$ D( m. L* S" W# i+ J - return $hashStr;
" O( c; [0 q7 g# y. C. ^ - } l6 K2 S5 X8 m5 i
- 3 x$ [- \! s5 @1 d7 C
- : J: u* B& p& B9 g
- /**比较两个 hash 值,是不是相似 5 F4 |' ^; a0 Y& t7 R" i
- * @param string $aHash A图片的 hash 值 ; X/ a* ^- }* A* a& `+ w ~" q0 i
- * @param string $bHash B图片的 hash 值
( e" A& f/ H. X: O% r - * @return bool 当图片相似则传递 true,否则是 false / ]3 j0 A+ L! K' b# i
- * */ ' e0 w. E" ?/ [8 s. I: j
- public static function isHashSimilar($aHash, $bHash){
$ x4 @ i I1 E. ] - $aL = strlen($aHash); $bL = strlen($bHash);
% }2 l7 [5 f- ?( A* }1 _$ D - if ($aL !== $bL){ return false; }
0 Y- T0 v. ?- \5 i - / M8 x' z: n* p) V
- /*计算容许落差的数量*/
\9 o. G, d* ^ - $allowGap = $aL*(100-self::$similarity)/100;
6 ~: z; u' M: A* b3 A: M -
: z/ X/ m+ A. M* E( x$ i - /*计算两个 hash 值的汉明距离*/ & ]4 J2 L( Q" s$ b. O
- $distance = 0; 2 c5 A$ g4 M; |& F& W. [1 J
- for($i=0; $i<$aL; $i++){ # P7 G* x+ w2 w1 \
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } 1 @8 O) m# m4 G; a& r- N& G! a: F
- } ; m* X. T& v. M$ a
-
/ @( f+ ~) A: v! X! H - return ($distance<=$allowGap) ? true : false;
% T& i0 P7 ]% \3 W; u4 W - }
6 I8 X% d" z4 P1 @/ G/ ]# i- P7 u - - f1 x) R; Z- c+ c
-
- `$ v7 s. w% A9 |$ h - /**比较两个图片文件,是不是相似
$ O9 [; |5 p1 F, F - * @param string $aHash A图片的路径 8 A( Z; z# Q, [/ O& }
- * @param string $bHash B图片的路径 1 _" X2 n5 A7 y' s1 i1 q
- * @return bool 当图片相似则传递 true,否则是 false " `. g$ `; K6 i0 z3 W
- * */
) d3 n3 f/ ` U2 W% i0 D - public static function isImageFileSimilar($aPath, $bPath){
# z, G, Z4 p6 p9 n/ g3 F0 U - $aHash = ImageHash::hashImageFile($aPath);
: @. }. N: t7 L9 y - $bHash = ImageHash::hashImageFile($bPath);
* g0 U6 a/ O9 @+ O: p/ S - return ImageHash::isHashSimilar($aHash, $bHash); 9 `( `0 N v4 i+ ]0 V
- } 6 ~7 c. a! u6 K+ x' c! Y3 x/ r
-
( U0 n; m3 F3 M- j6 }" H - }4 U/ E- R' L2 G5 p
复制代码
7 v# T6 \" }! U1 T/ y" M& L5 j8 D4 T) G7 K9 ~( s
|
|