管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图0 k) Y# T2 v, Q; R
9 X+ `4 a3 z+ H$ L$ U6 j$ w, P9 P5 H
# n$ s& e1 |, p v" l+ _5 |由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。! P0 k+ q! ^: m) a4 I- n8 Z
- <?php
& E( k% U% J4 U6 E- O/ g2 }3 X - /**
/ [' Q5 A+ i' ]8 k9 B1 s - * 图片相似度比较 " m, o7 A2 J7 ^8 z4 m
- * & c( G! h1 L- t+ J+ H. e7 E" t0 j
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; v7 L) ]- ]8 X2 @& u' k
- * @author jax.hu
. P1 n8 C% v) L1 Z3 w1 G - *
- [4 X4 ?, I# B4 W2 |7 W7 L - * <code>
. O9 i& r* z% e* r - * //Sample_1 1 f; `& P. ]; o5 A
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
1 ~* O1 ]. t/ \8 @! t - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
1 G9 y# o d8 c( s7 D - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 2 x" E4 V1 o" u z* S3 F
- *
5 D1 y# S* I& G - * //Sample_2
' |. w. K- Y: ^( p& t- M - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 4 \2 @- o- w O
- * </code>
# x- V1 ]) j7 [# E - */ + k- ]; J! `: e: _, _7 j4 O3 ]# A
- ' U' C$ M' v! q1 j9 A
- class ImageHash { & |* O0 F* l) l9 `
- 8 r' j( K& `0 y2 `( \% c) s8 \
- /**取样倍率 1~10 : r% Y" [! `5 p
- * @access public * P3 C( w' c! p0 B& V
- * @staticvar int
( D) a3 \1 t" [ o# O- G) o. {4 K - * */ 4 ^4 H, p, O n4 n. T
- public static $rate = 2;
& i w1 Z9 ]: z: r- d -
6 P! N9 C1 ~3 ~- L7 C5 f# B/ u - /**相似度允许值 0~64
4 N0 Z3 n7 Q1 Y5 X0 v1 R+ Q3 K - * @access public
" z- E" o$ w7 C - * @staticvar int
# t6 K) @/ }! i) Z: z; r2 k. ?$ r - * */ * k- d& r) {! ?" l
- public static $similarity = 80; & ~" B, t* ~% y8 U$ A
- " V0 G4 v/ O/ R' z5 R7 K% A
- /**图片类型对应的开启函数 1 s0 R+ C+ O6 v& H* P
- * @access private
. V) z) R; S7 R) P5 b! w3 V - * @staticvar string 5 Y# S! \7 L/ \* ]* a( v# p
- * */
. X7 _5 H% }4 Y2 V& Z - private static $_createFunc = array( 6 R p s$ d' ?) T' Y. E$ c
- IMAGETYPE_GIF =>'imageCreateFromGIF',
; @6 p7 h: V3 Y* Z - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
2 N3 Y4 {8 C8 P. S, B - IMAGETYPE_PNG =>'imageCreateFromPNG',
4 q4 V _6 y2 A( z9 l3 t - IMAGETYPE_BMP =>'imageCreateFromBMP',
9 \* t Y k3 J8 D$ w, _" I - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
& f6 h" K8 b! E6 m$ V/ \/ L - IMAGETYPE_XBM =>'imageCreateFromXBM',
7 l; U! R" c2 `* G5 }# f9 x - );
; _9 g3 c8 ^4 K. C; B- i -
" u5 I& t: w* q+ z! n& b2 K - 4 ]/ y. A4 [2 O6 X2 {/ w
- /**从文件建立图片 ) E$ e$ W6 P9 X4 ~) f; Y Q# y& X
- * @param string $filePath 文件地址路径
1 y d* {+ a5 N' ^9 } - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ! }/ [5 n4 b. M) D# Q' x
- * */ ! a/ w8 y6 \$ V6 s% U' U
- public static function createImage($filePath){
* w5 L) T" B" t% V P) d5 _+ N - if(!file_exists($filePath)){ return false; }
7 h a+ [% L8 x9 p0 B5 a/ M - % P3 p& u& j% K8 Y( W2 ~! q
- /*判断文件类型是否可以开启*/ ; k4 c; L+ s/ O
- $type = exif_imagetype($filePath); 3 G: p: Q9 b8 U
- if(!array_key_exists($type,self::$_createFunc)){ return false; } . y8 w7 n0 E5 E4 }
-
- D: ]2 f; G0 k - $func = self::$_createFunc[$type]; * o2 z) H( |1 Z9 D
- if(!function_exists($func)){ return false; } & i0 ^; S" r. D, ^4 i& ]' d
-
4 g! @5 f* t! S/ k* a& G - return $func($filePath); 4 d2 @! j, _5 g: |' g& P: r- W1 y
- } 7 m8 m* } ~( J; f$ ^* Q
- . Y7 C% p I4 V
- + a, X- X0 c% Y4 e
- /**hash 图片 * @* ]* S4 U5 j9 h/ Q& |6 G
- * @param resource $src 图片 resource ID
0 W" |! ^% E& ?4 w8 h - * @return string 图片 hash 值,失败则是 false / S' ?+ l0 j$ F: g
- * */ ' k, {. V ~; R3 B. G" t8 w _
- public static function hashImage($src){ . v' Q2 D& x8 P2 v% r: M
- if(!$src){ return false; }
2 ?4 c f& h7 }8 M4 l# Z" Y' Z - : W; x1 w4 I$ K. f! a. m* p- d
- /*缩小图片尺寸*/
% _( ?9 C5 q) b2 V* B3 V1 n( x; O - $delta = 8 * self::$rate; - g9 D0 L" A* ~3 F5 |1 g; p7 r
- $img = imageCreateTrueColor($delta,$delta); 0 _4 A$ ]+ n" P9 M2 K0 Y
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ( a2 o$ @' `: Q
-
$ H; K& e0 Z$ \" w8 R: | - /*计算图片灰阶值*/
* d+ f- z+ C5 d$ w+ `& @ - $grayArray = array(); * h2 \9 h8 X( i0 \. C) S" f$ R
- for ($y=0; $y<$delta; $y++){
v. O E* j+ y. n- W4 X - for ($x=0; $x<$delta; $x++){
: P; J; V; s- D- _0 m* `8 O - $rgb = imagecolorat($img,$x,$y); - Z3 t0 L% d' `/ J5 I: e& C
- $col = imagecolorsforindex($img, $rgb); $ R, y$ D; k( u8 q2 c* l5 k
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 0 n$ G5 ?$ A, n
-
6 L' E! h+ f$ \9 b0 U' E2 k& d( Z - $grayArray[] = $gray;
6 @% j& q4 F( w4 a N - } & U% P; B+ U6 \. H3 h
- } 8 B! e+ c( f D$ ^; e) U
- imagedestroy($img); 4 E# A9 b/ E y1 f
- 4 {- D- f3 y5 ^2 f2 q
- /*计算所有像素的灰阶平均值*/
8 j- y3 c0 z, C1 J/ e - $average = array_sum($grayArray)/count($grayArray); " X) e5 _, J2 g* J1 T
-
0 n& ?. x* V. P - /*计算 hash 值*/
! [/ c* e' i6 q1 D$ R4 W0 N - $hashStr = ''; & ^! V: F! v: J& V9 {
- foreach ($grayArray as $gray){
+ c1 E, e4 R/ L# o+ b! l( u+ R - $hashStr .= ($gray>=$average) ? '1' : '0';
* Y N o6 H% f) j9 x" s - }
- y4 c" e3 ~: d1 C( h - 5 c! |3 G% r: @' N' J8 V
- return $hashStr;
4 z |- e9 O9 a0 d - }
' `; H _" X* q* n* \" A! t -
9 S$ H( y K7 O& I$ R7 C -
2 Z0 }- K7 ]9 U. a; n. o - /**hash 图片文件
4 f" K7 c0 B. J' b4 L% y) X - * @param string $filePath 文件地址路径 % a( F9 {: T( ]+ {- q
- * @return string 图片 hash 值,失败则是 false
! e9 e2 T: |2 [ - * */ ) f( R3 ^# t( [7 z" d
- public static function hashImageFile($filePath){ : c7 s8 [+ D0 [ [0 x& n: P
- $src = self::createImage($filePath);
- S/ Q+ w3 x- f+ K - $hashStr = self::hashImage($src);
* E! Z: z r* S* Z& V* }' x) V - imagedestroy($src);
/ c' i/ g1 h& L O1 o; f2 u - ( O- N4 W4 Y; p/ Z' R+ f
- return $hashStr; & W* d) M b0 Q5 A6 g) g
- } 4 R6 }- a: o t Z' n
-
' T I# U( `' K$ k* i8 A$ S8 f - ) _* S: b3 o( w) z
- /**比较两个 hash 值,是不是相似
" w T+ z. C. \/ _: R4 J5 r - * @param string $aHash A图片的 hash 值
6 D. V; t& c8 B - * @param string $bHash B图片的 hash 值
s) l2 M X% s. h( s- a& S# o - * @return bool 当图片相似则传递 true,否则是 false
7 t" J9 g8 a3 O( C% P: ^5 |& T! D$ j, G - * */ ) Z) Z5 f8 p% N8 U, }8 i
- public static function isHashSimilar($aHash, $bHash){ 7 Y' Y% ] I2 y. g
- $aL = strlen($aHash); $bL = strlen($bHash); 6 X1 N* n. l. r* x4 @+ o
- if ($aL !== $bL){ return false; }
7 S. \. I4 Y1 P8 @5 ?6 j# B$ A -
! D$ _8 \$ D8 ^0 R. R% f1 h5 W - /*计算容许落差的数量*/
, z2 h5 I! A" l" _3 ^; V" r: C% w - $allowGap = $aL*(100-self::$similarity)/100; + r8 y% _5 |9 l. `# X5 r
- , I \! [0 h% W6 J" k
- /*计算两个 hash 值的汉明距离*/
3 u5 F- g) Y( o3 p: l - $distance = 0; " @/ @5 C5 p! I- d: M
- for($i=0; $i<$aL; $i++){
5 l% {+ ?! L( [0 Y; O! ]) f4 v8 o - if ($aHash{$i} !== $bHash{$i}){ $distance++; } 1 Q" _6 \, d+ [& e$ d$ y4 o. W
- }
9 V4 |& r, S8 b5 Q, e - 9 F. Z. A! C7 @" o1 [* s* i
- return ($distance<=$allowGap) ? true : false; ) M- Z. x6 J: b6 j1 l
- }
+ o, P% |. a: q5 \ - " |, Z/ K0 V* q: s
-
7 y0 Z5 a" r7 `) [; x - /**比较两个图片文件,是不是相似 0 ~7 q; e& P l% U4 h; i
- * @param string $aHash A图片的路径
: j" U/ V3 I$ @4 y: d) X$ R9 x - * @param string $bHash B图片的路径
5 T( S& `2 u$ H, |! w6 z - * @return bool 当图片相似则传递 true,否则是 false
9 F7 t& I* \7 T# ?4 c z2 | - * */ - g8 V, L3 F$ L5 O3 O q* `
- public static function isImageFileSimilar($aPath, $bPath){
6 [3 p' }/ Z Q( {; q7 W - $aHash = ImageHash::hashImageFile($aPath); 4 R6 [) V$ O. }( t
- $bHash = ImageHash::hashImageFile($bPath); $ Z8 N" U: Z" r& A, h
- return ImageHash::isHashSimilar($aHash, $bHash);
/ S% y9 }. Q1 n" y2 c7 j/ G7 }7 w - }
! @: A1 S% v# w3 x B* Z8 y. x -
* D/ h. G6 ~8 E* J - }* }' H# y v! S; T0 m. a
复制代码
1 r# Z, L; A& e) B; P. S+ z# z# S+ r( F$ n
|
|