管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图8 f3 ~3 b/ H6 |; ^8 a0 h) T
+ P. Z' O. f1 P" K2 Y2 T
0 j# l$ c& L* Z9 e6 b. K1 H由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
6 I- v. p1 L& I+ W6 X* k- <?php
2 ?# u* v- v- b+ u - /** 5 S# ]9 n. H* U+ \6 O
- * 图片相似度比较 8 o* n5 L- y/ v% U) Q
- *
3 e: ~ N6 L6 W2 t4 R6 @' }; m - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ) S3 T- @* S. F- N2 N% j1 X4 t
- * @author jax.hu
5 J% ^1 F6 x2 p) z% R5 Q - *
V6 C) X `$ X- V9 } - * <code>
/ p2 R7 w& q! j! i - * //Sample_1
( J: W; I' }' L! G Q - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
* p. p0 ?; g5 y5 A0 l I4 q - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
. G$ L( t$ G" k( r! |% p - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
& m" D- Q; q. E0 s% d7 H - * + Y6 R7 ^9 N2 z
- * //Sample_2
% Q* m9 d# h: Q" L - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
8 t* H$ g: v9 J' b# ~( G) r, ~! g - * </code> + h! x: \+ L+ C8 G
- */
! @/ U: w$ b4 ?6 e/ O- s3 w2 y. D - 6 @+ W* s6 {4 u9 p& F
- class ImageHash {
7 ~: o M3 |3 ]) G8 u -
4 ]# |& u* D% @/ i; [4 I; Z1 x - /**取样倍率 1~10 1 e A4 q# J" z
- * @access public
9 S2 a( {" v+ u3 u3 U; A - * @staticvar int * u+ Z0 S" O% |
- * */
- |8 V( Y4 h, a8 H j% G, c - public static $rate = 2; , _4 R. [) H5 }* D5 I( F
-
3 l% D: R$ V' X' g8 A, S - /**相似度允许值 0~64 : {+ F' P$ m Q2 U# n$ \7 g5 E
- * @access public " G6 q* R' e3 ` v h+ K$ C _# Z
- * @staticvar int
5 [9 t, ^' e' Q" \# J2 W+ E - * */
* s9 s4 r4 Z2 k4 N6 l- W - public static $similarity = 80; - N" n% O% Z( O
- 2 T- M3 `# R6 }8 G& Y
- /**图片类型对应的开启函数
f2 C, t' b. m% s) k9 `6 a! p* a - * @access private ; W5 _' U, w( l4 _! |# z
- * @staticvar string
# K% p+ \% Z5 W0 i$ X; r+ M - * */
" `2 I- v" k3 V7 o) O6 X% [/ j - private static $_createFunc = array( 8 \& _; `0 H; r7 h1 F& f! D
- IMAGETYPE_GIF =>'imageCreateFromGIF',
' u* b0 B" s# r; N; a$ ] - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 1 l8 U3 {4 c# h* O6 o) P
- IMAGETYPE_PNG =>'imageCreateFromPNG', ! C! M( A1 ]1 O6 i8 u8 s6 g$ |
- IMAGETYPE_BMP =>'imageCreateFromBMP',
4 j' @5 m. u. C" f4 f3 o - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 2 s& e: k% q3 E/ n0 o0 H+ @
- IMAGETYPE_XBM =>'imageCreateFromXBM',
) Y1 Q, { ^4 s& E" R+ \; ^9 S - ); 0 P. N& m, q" M6 m t9 \/ ?
-
" k, M- t. g. N; [2 |/ ~+ X -
4 ~; z8 q) P5 c/ x: ~ - /**从文件建立图片
" y; r/ i. Z3 S9 c0 Y' m1 M2 o - * @param string $filePath 文件地址路径 ) E, T/ J9 D# L& E9 G$ j" l5 g
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false . Q6 @! q. Y; U
- * */
" r$ X" p7 w/ \) ]6 z7 V - public static function createImage($filePath){
6 } J: Y' \2 `) W* }& a% `6 w0 l - if(!file_exists($filePath)){ return false; } : `# v# H% L- H, B3 g4 Q
- 1 c; x4 D: ?$ _8 o: S
- /*判断文件类型是否可以开启*/ / Z- C% a- E3 ?4 v/ g5 W
- $type = exif_imagetype($filePath);
; p) v/ L) C+ y9 i6 l% i - if(!array_key_exists($type,self::$_createFunc)){ return false; }
" }+ y) W, w1 {$ \ | -
2 a& Q s* M1 `( W$ R \/ R - $func = self::$_createFunc[$type]; . p8 g& }8 f0 c3 \
- if(!function_exists($func)){ return false; } / s; g* Y; p/ `7 M
- ( r1 E4 z: Q# R2 m! F u$ }# ^% S
- return $func($filePath);
' x1 _! R' j; N8 c) h9 J9 f- }. x$ \0 d - } & H5 E3 A, c8 F* [ v' o4 x9 L) P
- 2 S. o% }: K6 r9 ^3 v! `# {
-
4 z0 v/ A$ J, G$ n9 v z - /**hash 图片 2 K3 ?3 Q- ~# Y
- * @param resource $src 图片 resource ID 9 q5 w& x* M! z/ U/ Q; l
- * @return string 图片 hash 值,失败则是 false ; T1 w4 Q$ \& n/ a! r
- * */
, [' n' t. l3 B - public static function hashImage($src){ . z2 h: C' R7 d$ q0 |) {! K
- if(!$src){ return false; }
& C: W$ q0 `4 ^& T2 J0 h$ n" @$ Z - W5 p$ P- l) c( E
- /*缩小图片尺寸*/
1 M1 h( r8 k, H* |* \ - $delta = 8 * self::$rate; $ Z8 |) u0 W8 V, m
- $img = imageCreateTrueColor($delta,$delta);
* @8 g' h. o `6 o( @( |& O - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); " c$ f3 C8 Q% X* M. i( C/ x
-
9 g! e6 p2 _5 k) d" a' j: d - /*计算图片灰阶值*/ 9 l$ F* ~' d) Z8 {
- $grayArray = array();
0 S* O8 m/ F5 `8 R( m4 y - for ($y=0; $y<$delta; $y++){ 4 {- O0 R$ \6 n( P3 H
- for ($x=0; $x<$delta; $x++){
, k! I( D9 }4 |' N1 W% [ - $rgb = imagecolorat($img,$x,$y); ) A' `: M9 s% W, _, H
- $col = imagecolorsforindex($img, $rgb); ; [1 D' k* X4 M2 P
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
. M" H' w$ w0 [8 p, A -
2 b$ U/ }( B2 F( Z - $grayArray[] = $gray; 5 {0 r- E( y* ?6 M# ~6 \
- }
: Q1 i1 ^4 I3 T) Q7 s& B# y" t - }
6 z) f0 g7 t. M/ e5 N" r* z - imagedestroy($img); + s: Y: ]8 S! U E w+ u7 \# M
-
; }, d/ ?4 G2 [0 u7 ^ - /*计算所有像素的灰阶平均值*/
Z& U0 I; J2 { - $average = array_sum($grayArray)/count($grayArray); 8 B7 g, _0 l; Z
-
9 n1 U$ _* `+ X. d - /*计算 hash 值*/ 4 n7 d2 Z" N/ a' u3 K* R
- $hashStr = '';
9 p& d9 n/ Q$ H6 ~. F# B/ [. N - foreach ($grayArray as $gray){
2 k" |, f1 U* f3 P6 n - $hashStr .= ($gray>=$average) ? '1' : '0'; / t; v9 w( H7 z3 [3 {) q
- } * U7 h. e' m9 e" a' e' e8 g
-
0 j. F# h% _2 T Y4 U - return $hashStr; " w: N+ h8 T; D+ A- x- R
- } / N5 Q0 s3 g! _: P5 k! }
- $ C# a. r1 o( w) Q
- * I& R" f$ c/ T& D- g2 _
- /**hash 图片文件
' d: n! s+ q1 g& K' u$ L- P" R# c - * @param string $filePath 文件地址路径 . n( v9 q6 Y! q, J, [
- * @return string 图片 hash 值,失败则是 false
7 R8 U& Y3 X4 _' X - * */
5 Y8 X6 L Z, ?2 j9 ? - public static function hashImageFile($filePath){
; _. b- w w( [ - $src = self::createImage($filePath); 3 m1 a8 w$ h6 e% ~2 {( E G+ j/ B
- $hashStr = self::hashImage($src);
) }' s0 w1 H* g - imagedestroy($src);
. G( D! I7 M# l; f. T -
! r1 e7 @, r% w& x: H% j7 K - return $hashStr;
0 W0 }# C# V2 T! N( j - }
j" I: A* M3 m1 O -
4 {- t+ s' E5 P9 \6 E9 Y9 J, H -
* I6 z0 Z$ b8 T/ _ - /**比较两个 hash 值,是不是相似
1 `9 ]- }9 X6 H, @, v+ k5 @! N - * @param string $aHash A图片的 hash 值 ; F# D9 e+ x9 _2 P9 n
- * @param string $bHash B图片的 hash 值 7 \! P- a* ]; [; C7 r. u) V
- * @return bool 当图片相似则传递 true,否则是 false 5 x! D+ N6 F( d% [
- * */
% ^: b. n% ~" Z) e0 F - public static function isHashSimilar($aHash, $bHash){
# Q9 @4 l* ?8 @9 d$ j6 m - $aL = strlen($aHash); $bL = strlen($bHash); ! e8 k$ ?- q! ?7 N7 H9 W8 A- q# `) E
- if ($aL !== $bL){ return false; }
: \8 y7 `( n6 V! f8 _% K; \; E3 o - - {; X+ x3 J" s4 @1 i
- /*计算容许落差的数量*/ ; g$ a/ @! u" v2 z# t- t, ^3 T0 s7 ^
- $allowGap = $aL*(100-self::$similarity)/100;
9 m8 v d! X; P2 a2 i, R* g -
6 K1 ?, y6 O; x- C6 ] - /*计算两个 hash 值的汉明距离*/ # G" p7 \8 [- W6 H: ]4 F
- $distance = 0;
& J. y3 M+ ^7 f2 k2 `* o5 r7 _ - for($i=0; $i<$aL; $i++){
) Z' \6 Z$ L7 E9 p( J - if ($aHash{$i} !== $bHash{$i}){ $distance++; } 9 a/ C ]7 M: w& K$ F2 \
- }
% F) v: q2 d9 R% d& u - 1 g& i% x/ g# l& R Z: }' q% L0 Y
- return ($distance<=$allowGap) ? true : false;
, e, ?# o6 H; ?# B+ A# B0 E: O - } * @( G8 P& K4 @
- ( w5 v! c, {- z, E: q
-
0 b6 t0 a1 m* G' e6 \ - /**比较两个图片文件,是不是相似 - \+ \/ K) f3 C, H4 ~
- * @param string $aHash A图片的路径
, t5 R* x- K Z/ n* k7 I9 W/ K - * @param string $bHash B图片的路径
8 ~7 Y2 T7 j- V) D - * @return bool 当图片相似则传递 true,否则是 false 1 }! L4 y9 O7 K% \6 u
- * */
! [ J' K7 T/ T8 }$ I- O - public static function isImageFileSimilar($aPath, $bPath){
$ j' y3 o/ Z$ k5 U+ d - $aHash = ImageHash::hashImageFile($aPath); & X+ i. w, u1 a1 f
- $bHash = ImageHash::hashImageFile($bPath);
$ G) [5 B' ~" ~$ L N8 P; M! B - return ImageHash::isHashSimilar($aHash, $bHash); 5 q) k, G3 \3 i2 F: @( Q
- } 4 P9 L' z2 b% J
- 3 }1 H2 p- o/ I$ Z9 C3 n3 g+ m
- }
, U- ^* N$ y/ h, P' V8 C
复制代码 3 W Z% a, V+ F0 ~' L
& X1 h2 ~) f) N, ~. r. z |
|