管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
5 r! }6 _& x( ~/ G y$ Q! E4 u- d* H' ^% ^$ }0 \" I
, l' A% |; g9 o1 d. W
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
& R. V9 F9 a6 X. K3 x, L- <?php 2 x) A8 `. r6 A
- /** & ^# o( C- z' m9 @8 e7 I( A' E4 f
- * 图片相似度比较
# J8 w* Z8 b% K+ M& b - *
' |, L/ P2 J* D( k8 i: Y" J2 j - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
* V y' g, n: ^ - * @author jax.hu - S4 W0 o3 b+ ?! M$ }
- * " { k. M( Q* T- W1 J7 @
- * <code> 8 o: N: e7 Q0 \
- * //Sample_1 ' t% i3 @, |9 O4 ]! }8 {
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
: e M, r+ A& s) k) | - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ) x! j/ C; p( G: a9 ?- {. r
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 4 M+ l7 |4 }6 ~4 n* W
- * / {3 ~. {1 {7 J/ W
- * //Sample_2 % `- s$ P. E( c8 \
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); : _ S* E. I; x% c7 N$ H
- * </code> : P5 L! I; G' o& j# P3 e. U8 @( T6 R
- */ ! g; b X) l, W
-
4 t6 L) ]' o5 l/ D/ j6 [3 b - class ImageHash {
% ]/ K' T& x4 \! m" V -
. N0 Q& _& e, M( ^$ |( L - /**取样倍率 1~10
" n$ @3 z5 x9 F" {$ I. a* f) J - * @access public
' F8 A6 o/ ~% i3 i% p" x8 { - * @staticvar int 4 j$ b" _+ d- \3 Q) L5 Z8 R2 ?
- * */
# I% z, ]% h9 ~1 Q; I - public static $rate = 2;
- r1 y& y4 p2 @+ ` -
" d5 h% Z7 @7 c& _' m5 `( X - /**相似度允许值 0~64 4 U' B0 u/ C, A& F0 ^
- * @access public , k4 V. O7 V, V7 @
- * @staticvar int
% i* P7 V2 d+ N; V. p - * */
) ~9 r7 W8 W6 u - public static $similarity = 80; & z7 l% E8 ^& B1 c9 u j4 D+ k
- / e5 k! B2 W0 J" @; [6 J
- /**图片类型对应的开启函数 3 U! E8 w9 F- l M0 [
- * @access private # S: z+ R, C8 Q* R3 O: [# Z- x, d
- * @staticvar string 4 |* ^# P" w. f. n+ B
- * */
: I/ `5 W3 s! @# c+ S; y - private static $_createFunc = array(
4 O6 K8 `$ B, c* d- B2 }$ f - IMAGETYPE_GIF =>'imageCreateFromGIF',
/ k! U$ Q' v9 W# o" I- J, ]; U - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 4 o, u0 A& {; V% D
- IMAGETYPE_PNG =>'imageCreateFromPNG', - g) U8 I; ]" ]
- IMAGETYPE_BMP =>'imageCreateFromBMP', . `6 E) v7 g: D; O
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', 0 `# e, U) @) f2 D1 D0 W" A
- IMAGETYPE_XBM =>'imageCreateFromXBM',
0 H+ ^; s3 X' @$ l$ t3 ~. V9 ^ - ); 3 M$ B. [& h3 j# T
- * D+ H, b6 q6 W
- * p; c) Q# ?1 B, e }
- /**从文件建立图片 3 @# |6 N. |% G- `/ o) p# u
- * @param string $filePath 文件地址路径 & a7 `: i6 _3 R; m4 C' X
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
- Q+ @$ R( ]6 s. Z# _ - * */ P. \5 V2 e. ~! f
- public static function createImage($filePath){ / U+ p& S0 H6 Z2 i% G
- if(!file_exists($filePath)){ return false; } # ~, `4 N! k6 T1 e& U
- ; C. H( G8 H. M* o
- /*判断文件类型是否可以开启*/ : H. r- H* V1 \/ P- z! B- T
- $type = exif_imagetype($filePath);
$ t( B: `: ^1 C2 H9 v - if(!array_key_exists($type,self::$_createFunc)){ return false; }
; W$ _4 v" J3 x6 _ - 9 Q1 U5 M% m* O) h
- $func = self::$_createFunc[$type]; b8 I$ Q; _" ]0 w# Z
- if(!function_exists($func)){ return false; }
5 m- b$ F6 L2 c# I. [) ? -
* \" K- {7 h9 R - return $func($filePath); 4 {. T5 p* w% W7 j ~
- }
4 g1 S$ C' A4 n* F# Y -
% w; t* c+ r- Q" _ - & A3 G7 ^0 N. u; B/ T
- /**hash 图片
! p- e% o$ r W+ w - * @param resource $src 图片 resource ID 3 g! T0 t1 p5 Q/ ], ~; e! _
- * @return string 图片 hash 值,失败则是 false
1 o+ @. c- G* E: J - * */ ; d9 y! M' Q9 r
- public static function hashImage($src){
8 P6 G5 o' r ?+ K, G* d5 d, w - if(!$src){ return false; }
; }6 @, W* i& K0 W; o! P+ N8 C - # n. Y+ D- j/ @" { W# M
- /*缩小图片尺寸*/ 4 L7 V+ t# ^6 Q$ H8 J% i
- $delta = 8 * self::$rate; ) y, p9 u3 ^% R& \2 S* v" C# {( ?
- $img = imageCreateTrueColor($delta,$delta);
! ^$ ?/ G4 U, s* K0 l( A0 s - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
3 F: ?4 h; ?- I% X. Y. |. s - 3 \1 H. B/ v2 K
- /*计算图片灰阶值*/
9 _' P3 ?* V, a - $grayArray = array(); & C! k1 l8 c. |" z/ x& v
- for ($y=0; $y<$delta; $y++){
9 d7 x; c/ _# q: `& p# I - for ($x=0; $x<$delta; $x++){ % Q, {/ t- }& l6 X
- $rgb = imagecolorat($img,$x,$y); ( B# [, M; K1 X4 S
- $col = imagecolorsforindex($img, $rgb); : _+ {3 J) K- N; b) i2 n$ w
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 6 `, W& K" ]2 Z% {, J, \
- 5 r) b+ I% } d5 {1 \) v ?$ u4 D
- $grayArray[] = $gray;
7 n# L$ \$ S4 r' b - } . @% k+ T w. c" [, ]8 P# d3 |
- } * G1 M. z8 E- K
- imagedestroy($img);
n! w+ y" I- f! ~ - 8 d4 T5 [' n" B
- /*计算所有像素的灰阶平均值*/
0 i# L$ W1 [2 k- o; a - $average = array_sum($grayArray)/count($grayArray); ; y- A8 V4 J, e
-
# U3 U r, r# q0 c - /*计算 hash 值*/
8 A, V2 ~& ^) o; ~( ?& q4 e: i - $hashStr = ''; 3 C; P4 L, t# ?" d9 _8 L
- foreach ($grayArray as $gray){
5 |6 i' A+ n9 U8 z - $hashStr .= ($gray>=$average) ? '1' : '0';
1 k0 M2 s9 o/ t5 N [ - } 2 b, x5 }5 \/ {- | Q9 u- ~# ~
- ' @0 r) n3 G2 n) N3 G: c
- return $hashStr; ( k2 q/ w. H% `$ k7 K4 d: f" t
- } 2 I6 x3 X, t0 h: G) Z7 t
- 2 q. ^: [1 Z" M+ J" h& V: V! j
- ) y6 u5 }6 ^3 a4 \( B" _) d
- /**hash 图片文件 & q2 o0 w% ~# |: ~2 O( J! K( J
- * @param string $filePath 文件地址路径 : {) A8 [ u5 _* ~) r0 R* v$ E+ Z
- * @return string 图片 hash 值,失败则是 false
0 D+ }3 N4 } ~$ } - * */
5 o/ i# C2 g, G5 Z: E8 ` - public static function hashImageFile($filePath){ 4 h1 y+ T: j/ `: O' z/ r
- $src = self::createImage($filePath);
5 M# ^8 I8 t; M4 o& P$ H1 x$ _ - $hashStr = self::hashImage($src);
( y% D- X* V8 M - imagedestroy($src);
[. u+ h! ]" k" s+ s2 ` - 0 L0 U9 V: T& [
- return $hashStr; & R9 b% Z$ i. Q
- }
- E- D. }" X' I8 l/ J" m: {+ g -
( V2 e3 i0 D. N ~3 G -
2 [9 [' I7 Q1 e) o - /**比较两个 hash 值,是不是相似 2 b3 w2 Z& e; z, X8 E% }8 I
- * @param string $aHash A图片的 hash 值 ) i' s2 x8 w7 x, u1 _0 [
- * @param string $bHash B图片的 hash 值
7 e g N6 m9 ^5 q( v1 ~ - * @return bool 当图片相似则传递 true,否则是 false
% k5 N/ [$ z/ d/ `! Z5 b - * */ 3 w* z. W" n y: W/ H
- public static function isHashSimilar($aHash, $bHash){
. ]; d. w8 c' H: x1 [ a - $aL = strlen($aHash); $bL = strlen($bHash); : q& O$ C( O% W) Q
- if ($aL !== $bL){ return false; } 2 d, U# k1 f/ L' l
-
5 D+ V. Y$ a4 n. [7 G, _( y - /*计算容许落差的数量*/
6 E9 x6 Z3 ]$ Q' b/ S - $allowGap = $aL*(100-self::$similarity)/100; 4 a5 ^7 o4 u4 R0 B, }$ h4 | J
-
/ k6 W* R0 F8 p- b; k" ?& T7 [ - /*计算两个 hash 值的汉明距离*/ 8 U% N. I% {/ Y, p( D
- $distance = 0;
7 y0 C+ r1 [6 P* Y' S& }3 G t - for($i=0; $i<$aL; $i++){ " x' I0 J' N$ h, o# q
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
' w: }& [4 h1 u: q ] - }
2 R/ ?& y) U$ k: q; h) e0 b -
: N7 Z; C- [$ }2 Q - return ($distance<=$allowGap) ? true : false;
+ d7 C- [/ p0 A; p: `: ?9 ~- _ - } 0 x, _& Z7 r$ g+ T9 t5 c
-
+ r/ p$ v9 B7 P0 M0 s7 s4 w -
; B m, e. m2 V2 v; b) A$ m - /**比较两个图片文件,是不是相似
8 B! {' @4 ~% [/ O: V - * @param string $aHash A图片的路径
3 g) U0 Y3 f- [+ L - * @param string $bHash B图片的路径
2 b- J" l# @6 @8 O: \; z( W - * @return bool 当图片相似则传递 true,否则是 false ! b. v4 b. C; `7 {+ D
- * */ + C2 P+ r' G, |5 n7 ~! o
- public static function isImageFileSimilar($aPath, $bPath){
4 J4 \. j' {" p5 F/ K - $aHash = ImageHash::hashImageFile($aPath); ) P+ b' f. l8 G" r: I
- $bHash = ImageHash::hashImageFile($bPath); % [$ L; ?3 v4 f: f. ~) K
- return ImageHash::isHashSimilar($aHash, $bHash);
- F5 a& `* z* r! e- j7 k - } 0 @2 j' W9 r" u
-
+ a \& S$ d8 A& Z; O - }
* F, H! M! o: J; _. a
复制代码 , M# q6 }3 r6 h* g
+ n O) I. `) l" C+ i I; [+ a
|
|