管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
' q3 v/ { Q+ t4 a! v
: ^# p% t: i3 i
, |7 D' o: V$ w' e* f; q( M由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
7 ~: W( F! E$ |9 M& `9 U# v- <?php & _. C; X. ]* C% A0 K- t
- /** 3 I+ {& v% _7 m$ ?$ g
- * 图片相似度比较 % t: x" p+ \6 p# ?8 u$ [) H- V
- * % p6 v6 V9 U. T! a& s+ K
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
5 ]4 {/ o: k5 t V8 w4 X - * @author jax.hu - Y2 F6 z2 n @5 S( u
- * 9 _- {& l& Y" r4 f
- * <code>
0 I: Q; e, t$ S ~6 ~1 j - * //Sample_1
. h2 I5 g9 M) ] - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 9 g8 R3 Z# a l6 u- h
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
& |+ o1 b: E- s - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); ' r8 f0 ~1 e9 @: c& s8 ?
- *
! c8 k- m+ N5 J1 \ - * //Sample_2
) k, |- s& E+ C% l7 z$ Q4 W. w' S - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
! H# J- m4 |8 W; h, t - * </code>
; u) u) H F$ q - */ , Q* {' W0 N, \' m
-
0 U" M. n& u; z/ X - class ImageHash {
; @; ?' j9 x$ \# D5 q2 q - # N% @+ p; z/ E
- /**取样倍率 1~10
: i! U' K$ F5 ^3 B7 X! i8 e - * @access public
, U* L' N0 }' i* ~ - * @staticvar int 5 H8 w& ^# W, v4 Z i5 J
- * */ 7 k5 i7 h$ w% r* w4 s8 L
- public static $rate = 2;
5 D- }* ]2 M* u* c" h# G - & O0 G! ~ `& g \1 H
- /**相似度允许值 0~64 ! @6 E! v% s7 H6 y6 l& ?5 ~) r) L+ I
- * @access public / O$ B. @7 E1 @6 u1 ~6 M3 H
- * @staticvar int 1 l$ X. S0 K+ c9 S
- * */
! e5 @; X9 E( w' F0 K J - public static $similarity = 80;
1 S: T. l2 R1 G m# M6 R- M2 s -
+ z% I. c, {$ k9 ?' J8 } - /**图片类型对应的开启函数 & H; Q+ ` {% h: v% [ W
- * @access private 0 h/ ]+ ]9 }* |3 G& j8 \: q" m
- * @staticvar string
# e/ |% U4 |9 T, ^6 w6 l6 R$ I - * */ 0 F' T+ o- b) [7 V7 x* N
- private static $_createFunc = array(
/ @: c2 r. r. d" n: ~ - IMAGETYPE_GIF =>'imageCreateFromGIF', 7 K; `" \3 y0 e+ U$ A
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', ' c$ B$ g3 ~% u$ t& Q6 m
- IMAGETYPE_PNG =>'imageCreateFromPNG', - ]# P9 O- r& J7 w( _# Z
- IMAGETYPE_BMP =>'imageCreateFromBMP',
+ `7 [, ]9 W/ Z( L - IMAGETYPE_WBMP =>'imageCreateFromWBMP', ' E7 p! Q; h/ ~( F3 S- G
- IMAGETYPE_XBM =>'imageCreateFromXBM',
4 t% o4 m8 ^' Z$ p8 X - );
9 A& o2 o4 w, X+ Z; g - 7 L, ]$ ]5 }4 n6 B5 U
- ' V( d2 R, Z& i! Q S
- /**从文件建立图片 : `( T, w+ H2 B8 x5 ]/ n
- * @param string $filePath 文件地址路径
& l* G, w) o" x+ R2 ^ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false # B n+ l8 a: Z
- * */ & b" @0 s% I* |/ V8 S1 s
- public static function createImage($filePath){
& ^8 x6 E& u' b5 u$ m* S - if(!file_exists($filePath)){ return false; } # x9 h u9 W p0 ~; [
-
6 @( }: N: G0 Z5 _5 e - /*判断文件类型是否可以开启*/ 0 ?- E+ b8 T& Y9 L
- $type = exif_imagetype($filePath); - G7 S- o, n8 e3 E. l
- if(!array_key_exists($type,self::$_createFunc)){ return false; } / r5 j# n* E7 X# m2 k& }
-
5 z# e* {' E) z - $func = self::$_createFunc[$type]; - U3 w6 t4 a1 | I% }% C( R
- if(!function_exists($func)){ return false; } - z% m; J1 n. j: m9 ?
- ) z8 |6 F! C7 h3 ]0 H/ s9 K
- return $func($filePath);
7 B1 M8 T& B) F+ c2 z. \ - }
# i- F' d m7 b6 v0 F) u% b w; Z - 7 {5 s' i1 `" Z% i7 d. `& P- y
- $ G2 }, L& ]- Q& X2 Y
- /**hash 图片
: n: ^5 U) \& O i3 ] - * @param resource $src 图片 resource ID
4 A0 O F/ ?7 f8 {7 S9 ?$ u* o - * @return string 图片 hash 值,失败则是 false
$ H/ I0 e: ?, W+ G8 m6 P - * */ 0 K0 l- @4 F& y: ]- I7 t# E8 b- `
- public static function hashImage($src){
* z) o1 m' R1 \' G0 L7 n8 D - if(!$src){ return false; }
+ s5 D" P1 X1 Q - 5 R6 e0 U7 ^2 ?* L+ u8 b
- /*缩小图片尺寸*/
+ ~; {9 P1 v( m# A% k- b" w6 p - $delta = 8 * self::$rate;
7 c+ C5 s& H* c+ V. m - $img = imageCreateTrueColor($delta,$delta); 1 M- b& u5 y5 P6 K$ Y8 E
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ) P, K! F" q3 H4 s B+ ^
-
' Z* {$ D9 @; r! W) }/ B& X$ m( X - /*计算图片灰阶值*/
0 s$ p8 B9 p' U2 {# e# F; T - $grayArray = array();
' i- r1 n2 I: A( y9 c - for ($y=0; $y<$delta; $y++){ 3 q0 c, q( r) D/ p9 E& Y8 Y
- for ($x=0; $x<$delta; $x++){
6 J5 W+ L7 j/ w4 F - $rgb = imagecolorat($img,$x,$y);
+ `( G5 |$ n- ]- Y- B - $col = imagecolorsforindex($img, $rgb);
7 g. S8 \6 y: j% k6 v7 a - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; + p* I2 W& [% Q" N$ l9 s! `
- & Y1 M7 D. J: e, g9 L$ _# J
- $grayArray[] = $gray; : W4 d9 u0 V ?, b( D3 t
- }
- c- |. M& u6 `" k - }
. j5 ^# l( u) e8 F" c5 Z - imagedestroy($img);
* ~" W4 C% z& F/ k" s3 d) p -
' d7 i8 I0 i8 F3 I6 z {- R - /*计算所有像素的灰阶平均值*/ % G9 S% G3 B/ |/ L' F
- $average = array_sum($grayArray)/count($grayArray); 2 `- j. @2 \1 d' q
-
( A' ]$ Z, o& _8 E% x* l/ v - /*计算 hash 值*/
0 m7 o- j% r8 I( @$ Z( b# v! T - $hashStr = '';
; |# h7 R( f4 O - foreach ($grayArray as $gray){
! F# R! O0 z! I2 Q _! Y/ _% r$ B8 I7 _ - $hashStr .= ($gray>=$average) ? '1' : '0'; 7 e6 C5 {8 t: \; _2 ?
- } 7 S* R6 L6 e5 l2 F, k" y7 H
-
5 A0 q" m- X2 l# v) Y - return $hashStr;
$ l3 i$ f- r/ A) ?: L8 F - }
, N' j _0 V w1 y -
0 |- _7 j) B: S -
! `$ m+ g; R4 ~. P6 H( N) d - /**hash 图片文件 ( k& h- e: _& x6 V
- * @param string $filePath 文件地址路径
8 n6 {# b' G' ]; G# ^7 y2 b# G - * @return string 图片 hash 值,失败则是 false 6 k; p. {- N( r/ m5 Q
- * */
, U! F6 O* F2 J4 ~1 b4 Q3 H; S - public static function hashImageFile($filePath){
! l& H7 Q' H5 j - $src = self::createImage($filePath);
9 E/ P: V2 V0 @1 z - $hashStr = self::hashImage($src);
# g l# U' z7 v D \( I5 | - imagedestroy($src); , _' W, B# s2 b8 _# Y
-
, w+ m! H1 e* e. C( n$ ~2 d - return $hashStr; 1 n9 V. R |5 B4 L
- }
8 x% X. I! a* z! |+ D - 0 i# u% C( c3 r! Q3 p e
- * o! G8 J5 e5 B F9 ~* ?9 k7 V4 c
- /**比较两个 hash 值,是不是相似 : p, _2 [3 f3 e% V2 Q
- * @param string $aHash A图片的 hash 值
. C/ Z) _4 S9 @1 v* r- { - * @param string $bHash B图片的 hash 值 ( Q+ B. \- m9 N% h5 r
- * @return bool 当图片相似则传递 true,否则是 false
7 k' e' Z& [$ h0 S8 K7 }$ r - * */
7 S6 E% `5 P: y3 M - public static function isHashSimilar($aHash, $bHash){ & n$ E+ N, a0 _5 z/ N5 Q& [5 y' o
- $aL = strlen($aHash); $bL = strlen($bHash); 2 w3 |: ^3 }7 r9 j
- if ($aL !== $bL){ return false; } ( [ R9 ~5 ]9 N6 Q) q
-
0 p% ]6 B4 |. k - /*计算容许落差的数量*/ , u) k, [: L0 k5 _! u- t
- $allowGap = $aL*(100-self::$similarity)/100;
+ d$ {' {# c' M -
( _+ I. |& H0 B# C; z' k - /*计算两个 hash 值的汉明距离*/ " }0 F5 G$ q5 ^
- $distance = 0; 3 h5 }% S9 }) L' M* A2 n; t- |7 {+ p2 c
- for($i=0; $i<$aL; $i++){ % m: W! C$ v0 ?6 G
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
+ U6 T/ }) {/ | - }
* ?/ j! o) F5 \4 O+ J# H -
- {3 ]- u7 L" W - return ($distance<=$allowGap) ? true : false;
& @' { e) s3 B r6 b+ c* P g - } : N9 u% T P& ^- U$ U( a
- - P# U; M. |* n4 S
-
& F+ G# L$ S& f. \) ~ - /**比较两个图片文件,是不是相似
& e5 g3 y' F6 w- e6 ^ - * @param string $aHash A图片的路径 ( H2 J0 e `8 M' l0 _4 W# a
- * @param string $bHash B图片的路径 $ W# s, I9 u, o" b& M
- * @return bool 当图片相似则传递 true,否则是 false : [3 l% D8 j3 [! K
- * */
1 U& b7 x$ T' `; d- S - public static function isImageFileSimilar($aPath, $bPath){ + X9 ?2 c* }* }2 w5 l/ B9 X1 c# n/ H
- $aHash = ImageHash::hashImageFile($aPath); # M; N, [. \7 z2 N* r) o& y
- $bHash = ImageHash::hashImageFile($bPath); ) ^5 _ a) t; p9 [! w, S9 [
- return ImageHash::isHashSimilar($aHash, $bHash); 9 Z' r5 t/ C! {; I, i
- } c, a- i% v) p- F% q# j' U
- / \7 f- e8 U+ x" t/ u
- }
8 n/ V& d5 o6 ^
复制代码
, }4 v9 [6 w. Q8 c; R. x% \8 P/ @: o5 V! K4 L& Q& }
|
|