管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图5 d2 a2 y# e4 B' f3 T. O: k$ }
: }5 t& H3 n0 N0 J2 ~* X
- {$ |$ }- O& ` G4 o1 ?' r: G由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
! d0 {$ |# a+ x7 X3 F- <?php
' O2 z y% x9 w8 ~- b% y, ^" H7 @, r; i - /** y6 o! @5 ^: e, W3 S4 y& G, L- j4 H
- * 图片相似度比较
Z W9 m' Q. e - * : b" z0 k& c+ T- z, j4 v
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ' r2 t8 o+ a* l& L
- * @author jax.hu
/ {4 v% D. z, A; N3 _ - * : L$ G9 ~9 i# K* T$ ]& i1 \( Q
- * <code>
" k' V& @* @4 c - * //Sample_1 0 T6 M- D& v* \- ~) p7 V/ @% G
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); + m! w# N+ h- |# Q; `! g
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
; l C* o; I3 x2 q0 }* j - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 2 P' O6 ^% [ ^" |/ p9 p! [! T
- *
; u' l/ u4 T9 o0 f+ m - * //Sample_2
* I( R/ j( I: P4 a4 Q y - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); " o" r# m3 B; t8 c& O! z# A
- * </code> : b0 A" j1 K' [- v& O9 Q
- */ b* a' ~2 c( U, C" [, N0 n
- " I8 r( Y3 o) Y r6 H$ p2 o' T) j
- class ImageHash {
( Z1 W9 `2 r/ N) i$ p$ _ - : w, C6 _: y, y8 z/ T7 s
- /**取样倍率 1~10
0 p# O) \! W% q8 M2 ~ - * @access public 9 q# S8 X6 r: }/ }
- * @staticvar int
- m5 ]& e% o# h K/ ?, m! C - * */ G, a% j# G( I7 L! P! K8 K! c7 }
- public static $rate = 2; 5 H2 J- h" g8 x" | ?' G6 W# v% w1 J
- 9 b+ r. _% h0 Z$ i, l T
- /**相似度允许值 0~64 % q9 I* }2 r+ N; j! t$ ?( ]
- * @access public ( A9 E* s2 k* e, ?+ n- j9 @
- * @staticvar int
# S* T+ v/ @2 s5 ?% N - * */ / L4 ] t2 W# K6 ?. r9 f& U' S6 O
- public static $similarity = 80;
# J% k, h: n3 ^8 Z) X9 W - ! j& F$ q$ h& a3 [- a+ n m, J
- /**图片类型对应的开启函数
1 C7 t5 @. Z: h1 @ - * @access private
1 U" a: Y9 B, N9 }& S5 N, F4 J - * @staticvar string - d8 |1 |* B% P5 p4 [* {; N
- * */
. i& l* b: z! q' G9 w9 [9 A" @( i - private static $_createFunc = array(
0 ]" ?( T `8 u# O - IMAGETYPE_GIF =>'imageCreateFromGIF',
( @! w. q y; L! g" W' G% U/ G - IMAGETYPE_JPEG =>'imageCreateFromJPEG', % z$ O$ U$ H- s! Z
- IMAGETYPE_PNG =>'imageCreateFromPNG',
/ f" t$ Y: {% Z5 t - IMAGETYPE_BMP =>'imageCreateFromBMP', 1 g! p+ z% l6 H6 R: V$ [+ @/ @2 X) G
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
( }5 Y; ~) K, Y& { - IMAGETYPE_XBM =>'imageCreateFromXBM', 2 q( I: C* | e6 q2 V" F( P' \
- );
( O0 O' I5 n v* d# K, B - 5 i/ ?+ M0 \3 w: d
- 8 |- O9 u7 n, H
- /**从文件建立图片
( s& G6 X7 T7 A - * @param string $filePath 文件地址路径 7 v& R7 a9 Y9 \7 x) n
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
' |; v7 i3 C: I# p2 w; Q3 D) y4 l; L - * */
' C4 m2 T3 P9 X) R1 v/ C5 v - public static function createImage($filePath){
- G8 Y& Z: l- H" u' _ - if(!file_exists($filePath)){ return false; }
$ K2 ~* D) o, w -
. K7 I) Z0 x2 p1 W+ O - /*判断文件类型是否可以开启*/ $ M+ A) ^! N' O. W8 ]
- $type = exif_imagetype($filePath);
w- X' v; q0 N* ? - if(!array_key_exists($type,self::$_createFunc)){ return false; }
) L7 q. Q* c! g7 o' d. O -
4 T+ C, J$ [6 B1 W - $func = self::$_createFunc[$type];
6 ~# l; a! I& d6 X# _, a. G' F/ T - if(!function_exists($func)){ return false; }
* h. V; f) {: u -
, i: _7 i1 K( Y - return $func($filePath); 3 u- l: r/ D b! M6 J; P
- } 9 }) d/ k- q, A) X
-
+ a& m: E+ ?' N' o2 q! v- _( |' _ - 4 j, t/ i' Z& E5 [, Y( [
- /**hash 图片
! g; n4 N3 r j z2 M - * @param resource $src 图片 resource ID 1 T+ B0 {4 D4 }3 U/ M
- * @return string 图片 hash 值,失败则是 false + r5 j8 J3 U7 D9 [/ D6 D" J
- * */ ) W8 G0 l* h8 g
- public static function hashImage($src){ % q' i! O' D" E
- if(!$src){ return false; }
3 ^6 B) p6 L$ ~$ g# A - % B( W9 A+ z# o; v
- /*缩小图片尺寸*/
% [$ u2 U& n/ @4 e p2 x - $delta = 8 * self::$rate; ( x# C# d5 T9 t/ x4 @
- $img = imageCreateTrueColor($delta,$delta);
. M2 b9 k6 z6 s9 o& ` - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
u. J& }) D' ~; I' `; i -
! S! k6 t" J1 I3 {. R - /*计算图片灰阶值*/
t5 g" k9 c: g9 j" p - $grayArray = array();
0 h* a! ?- o _1 \ G, j$ A* p - for ($y=0; $y<$delta; $y++){ 7 ?0 L% S9 d; Y! L* Z9 _1 [! Y
- for ($x=0; $x<$delta; $x++){ , l; M, C5 ^1 k' y K
- $rgb = imagecolorat($img,$x,$y); # \8 a* l" A) @9 ? D) b" E. f
- $col = imagecolorsforindex($img, $rgb); " z: v2 v* \. Z" C3 N
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
- o, \/ w2 c& e" Q/ Z -
2 ~" S* d: C$ B7 x" N$ v - $grayArray[] = $gray; ' h e+ h3 i8 i5 R
- } - D/ Y" Q s4 `
- }
# l. V3 r+ e% z3 f* P. }& Q - imagedestroy($img); 9 l0 @5 Q8 Q: x4 m! v6 Q
- ' o) C7 F( s) J& b2 z G1 ]
- /*计算所有像素的灰阶平均值*/
c- M& s, M! e' @- Z - $average = array_sum($grayArray)/count($grayArray); $ X7 r( h1 t- ^- }7 ~9 G
-
; X* y( n0 j0 ]7 t - /*计算 hash 值*/
. Q4 Z! |5 W2 P0 w e o" r$ S8 Q - $hashStr = ''; / J9 D1 |9 e, f9 r
- foreach ($grayArray as $gray){ ; D j* ~# p% J
- $hashStr .= ($gray>=$average) ? '1' : '0'; 2 u- I* n* D7 ?( V& _
- } 2 V% L; W9 W/ B- A" Q
- r. G4 g' R% z$ k
- return $hashStr;
/ P2 B4 ]0 p% I6 D8 V. e( H' a6 e - }
% p3 H1 Z0 o2 A4 e/ \ - 8 Y5 \* b& V) k* G' }6 e" H
-
/ ~* W. X2 n/ ? - /**hash 图片文件
; L4 b$ N& P$ l$ u* I; p W& Z - * @param string $filePath 文件地址路径
7 n6 y+ j9 J7 O! C& b2 P - * @return string 图片 hash 值,失败则是 false
9 ^ w8 ]! F2 z" K- X - * */
. O+ W9 T+ K; l( S" C - public static function hashImageFile($filePath){ % n% E0 J& o$ z& O S
- $src = self::createImage($filePath); 6 L* _" w" S: G! o" |
- $hashStr = self::hashImage($src);
. a* O9 L- G! Q3 e0 J4 P - imagedestroy($src); $ X" u4 B( \( C" t4 b4 {7 [
- 4 M% O. E8 |4 j
- return $hashStr;
1 {% O. J9 y2 u' @5 b8 r2 n1 R - } . L/ X+ F k& N4 E3 V" k. s" w$ u
- + J4 ]+ z, ~/ R2 R6 a
-
) h. x. R5 G% x8 C. f' v( b1 ]+ w- ] - /**比较两个 hash 值,是不是相似 2 [. c! y+ l6 ^- z( D$ V U j( _
- * @param string $aHash A图片的 hash 值
8 D9 o% ]$ q+ n \0 A1 u( D8 {$ s - * @param string $bHash B图片的 hash 值
1 u9 z. D. f5 Y - * @return bool 当图片相似则传递 true,否则是 false $ N! U& q {" j: P
- * */
, r% o- M" p4 g) w: C* N J" v - public static function isHashSimilar($aHash, $bHash){
, O) c8 v: H+ r3 M4 a; j; w; _3 U - $aL = strlen($aHash); $bL = strlen($bHash);
9 s: M3 o1 s7 c) U - if ($aL !== $bL){ return false; } $ z0 v3 M8 L) C& @5 L* C" J- q: S
- # m+ C" K! v2 N# ]+ I) z
- /*计算容许落差的数量*/ - N9 C! [+ {4 \
- $allowGap = $aL*(100-self::$similarity)/100;
2 w6 u7 q! X0 m - 4 g. c' ~2 M8 Z9 R
- /*计算两个 hash 值的汉明距离*/
% |( j( Z7 z1 E5 s) |& I - $distance = 0;
$ Y7 q9 ]- r" z( `+ @ - for($i=0; $i<$aL; $i++){ ' [2 W3 c$ I; ]. d
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
; r5 q0 a5 q9 A U9 b7 T1 X; G - }
* u: x4 T, J) O - ( F! M& `' V- _( f
- return ($distance<=$allowGap) ? true : false; $ v' I2 w, h( x. I
- } ( E1 f9 { j4 I& r. L
-
4 v# C, f$ L1 f7 E# A3 R4 ^" X" b -
- U1 ~0 c/ K6 r8 O0 Z - /**比较两个图片文件,是不是相似
1 ?1 {# T* R* ]) J3 |* m - * @param string $aHash A图片的路径 7 F- O2 o) U( t7 P
- * @param string $bHash B图片的路径
- i+ J7 V' S% B; K% J, a# n8 B1 V - * @return bool 当图片相似则传递 true,否则是 false
/ E9 z: J9 m; ]9 t W2 K& H - * */ r, |& A" N- ]& j. g
- public static function isImageFileSimilar($aPath, $bPath){
9 y# `9 s- t2 s# f+ g( L3 Q - $aHash = ImageHash::hashImageFile($aPath); 5 ~) p5 r% B, {, A
- $bHash = ImageHash::hashImageFile($bPath);
' s% v; g8 p" q - return ImageHash::isHashSimilar($aHash, $bHash);
) _" s2 _5 `1 q& s5 ~ - }
' J% Y8 {/ u+ K: a5 a -
& ?( m& A& `: T9 a- P7 s3 c1 H$ ^ - }; q0 J l6 |( E; a( ^
复制代码 ; V5 J& v* P* S9 \
' m3 {# k! ]+ z9 Z( ]) C
|
|