管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图, V% g7 O* ^0 _$ v
% ]8 x9 ?% f) [: S$ t
6 h( ?1 r" B: S) S1 g5 c
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。; @6 C& p. ]; Y7 {# ]/ k% j
- <?php 6 H% n6 A! d0 M
- /** {; R E f+ i6 C7 e- c
- * 图片相似度比较
: A+ P3 ]5 x/ |, C - *
* e4 k7 l! x; J- G' n" u: m - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; L7 T# x9 t+ Y# m- F5 E0 k
- * @author jax.hu
7 F o( j5 \: z# m - * # h/ a. U8 x3 z# ~' F0 C! j6 p4 P0 ?
- * <code>
% `6 G4 S, \/ d& C- w" d - * //Sample_1 & V9 R- D1 h b; P7 u+ p4 d0 K" x
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); # a; x) q6 O6 V9 [/ P- ^9 \
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 8 }/ f3 F e9 E6 p; Y& j0 a& ]
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
5 g8 M% \- b) g# h/ l* ^3 S - * 0 h$ ^# {: S4 R/ ]- H. @8 G
- * //Sample_2
+ Z* V2 L% G3 _5 D- z* Z2 N - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
+ A) ~9 s0 M, V% t' A - * </code>
1 J ]$ n4 o; x" z& z, C& z1 S - */ 4 E3 p: t8 j$ `* }0 R, E; X
-
# ~) E0 J/ \9 D& R - class ImageHash {
3 S7 D+ f. [: k. Z' f -
& Q& v/ T9 l" A, q. D7 h" F* l - /**取样倍率 1~10 4 }7 C3 F1 o# N) I* r& O
- * @access public , C( W' N) p) s5 k* d# a; {8 j
- * @staticvar int
) r; ^0 Q8 l7 N7 A$ O - * */ 1 }+ F, W' t7 V p1 ?* f7 b
- public static $rate = 2; 7 N, d* J- N) m# F, G+ L+ w
-
. Q9 E+ K" X; _; q7 w. g/ O$ s, G - /**相似度允许值 0~64
- m B& l8 T( ?6 v6 E: B; r, K2 A* T+ B - * @access public ( p) F& x4 ]$ x: X- n
- * @staticvar int * Z! `# `, \( S8 i
- * */ 5 ^ y: }' M4 j
- public static $similarity = 80;
0 n! K4 G& p I$ M3 I - 3 E& H2 P( I8 Z' q
- /**图片类型对应的开启函数
# _/ t0 o* k2 |3 p - * @access private - k2 ]2 |4 a4 I3 X
- * @staticvar string
( F' |' _- m: G - * */
: w$ A! R7 q$ W3 U) p - private static $_createFunc = array( [; `" P" i$ l& G* f2 g- i5 D; V, t
- IMAGETYPE_GIF =>'imageCreateFromGIF', " E+ h8 L* P. }& C8 e
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', + b5 X) s0 M! H9 `( j( O, e: k0 V" V
- IMAGETYPE_PNG =>'imageCreateFromPNG',
' k' G# R7 _9 T, O% i - IMAGETYPE_BMP =>'imageCreateFromBMP',
) d1 \7 `" }; B; C9 u - IMAGETYPE_WBMP =>'imageCreateFromWBMP', % y1 m L" e) k! e5 H
- IMAGETYPE_XBM =>'imageCreateFromXBM',
6 \, U, r% e# P8 s ` i - );
g, O$ @) Q* J$ q7 v, d -
9 S9 q! B: a- U -
) K& Q3 w0 M7 U% b% x - /**从文件建立图片
/ N: X( c) u% p; J: a7 w - * @param string $filePath 文件地址路径 7 `6 g( H! r6 q9 H, H+ H
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 5 e: \/ F/ Z' d- x1 ]5 \' N. _
- * */ / y1 k5 t+ l% a) y- z
- public static function createImage($filePath){
: S. M( `* R# g/ H" I - if(!file_exists($filePath)){ return false; } # Y D% }7 l( P4 T( {
-
0 E) ^1 V9 B8 O) I3 u1 J - /*判断文件类型是否可以开启*/ * f5 M8 D4 F4 X8 `) H# d; n
- $type = exif_imagetype($filePath); 2 Z+ W: H' O- Z: Y$ X5 ~/ J
- if(!array_key_exists($type,self::$_createFunc)){ return false; } : ]7 T3 ^) h$ l+ b
- 2 l, S1 g8 |! `/ f! F7 \
- $func = self::$_createFunc[$type]; ( [+ q! A& B0 a& _
- if(!function_exists($func)){ return false; }
, k# w3 F$ j) D# b - ! f: I7 e+ Z9 l. I: r
- return $func($filePath);
5 p) ~$ s( ~. x0 U - }
* g& `( |3 f; u' W/ j" o -
5 X% O/ R/ k- S! L4 X$ a -
+ d; m3 m/ U4 ^ - /**hash 图片
' u& R; f# w$ |2 B - * @param resource $src 图片 resource ID 3 q4 O1 Y7 V V) \
- * @return string 图片 hash 值,失败则是 false
- T, n# }1 P; Z% B1 a1 o - * */
0 [- X) e2 i3 W - public static function hashImage($src){
Y0 T, ^' m( F& L" B% g - if(!$src){ return false; } 9 V/ v1 F0 A" c
- 9 M D" a( T7 z1 R
- /*缩小图片尺寸*/
0 F+ y* U- P5 B - $delta = 8 * self::$rate; # P8 ~- W' B& ]! H
- $img = imageCreateTrueColor($delta,$delta); ( X9 v, j1 Y- V% n
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
7 O: E5 L$ y* F7 ~6 F* G - + p& i" I4 g. V, d9 p# w
- /*计算图片灰阶值*/
8 n, }) F: d: u - $grayArray = array(); & a2 p" O* I# ]' K5 S+ @# m! z+ F {6 S
- for ($y=0; $y<$delta; $y++){ % y1 ?2 \0 V9 D3 H9 O- U; k; }, w; n
- for ($x=0; $x<$delta; $x++){ ! f8 G+ C# O# p' M& ^# C" C0 D; g
- $rgb = imagecolorat($img,$x,$y); , v, X& H' U; @+ v, y
- $col = imagecolorsforindex($img, $rgb);
2 h, U4 G4 C+ B( y7 b2 O/ r( a - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
' h! c8 L- h6 V* v- t - : J5 I& a3 q `( P6 S- }) r5 D
- $grayArray[] = $gray;
9 k+ {. t; |' |- [6 S% P - } & s" H% z7 \. g2 ?6 t
- } 6 O; P" p0 z9 V' P% f% `
- imagedestroy($img); + w8 }9 ^6 |; L$ y% [/ J } d6 V& g: n+ d
- 0 s. u+ t# ^/ ^4 j: W4 k
- /*计算所有像素的灰阶平均值*/
: G' X3 X+ h t9 m; u" z - $average = array_sum($grayArray)/count($grayArray);
2 R& q$ G4 ~8 w: _4 ]* R! X0 S* ] - * ]0 J! n0 P3 U; f
- /*计算 hash 值*/ 5 {, A% F9 q/ d7 r/ x
- $hashStr = '';
9 }- O/ n+ K& [! G1 h' i. \) i - foreach ($grayArray as $gray){
2 V) X9 g9 o5 t# K2 Z) p+ d' \ - $hashStr .= ($gray>=$average) ? '1' : '0';
% R- ?- O3 \2 A/ \1 M" S - }
% ]0 S6 {( W2 M7 {/ [9 \ -
3 \$ ~9 C4 @; [" r5 B- m - return $hashStr;
, f1 e9 s8 s: K0 @( j/ ^# D - }
# P1 X/ i; T# `* A$ p - * Q* a5 u1 @8 |8 D0 A+ T
-
% [5 ?/ l- A- |" @6 E( R* ] - /**hash 图片文件 ) D+ Q" ^3 R2 B
- * @param string $filePath 文件地址路径 9 A- t" F I1 E, P4 s; P
- * @return string 图片 hash 值,失败则是 false 3 c! U; o, @, m( H: n( |, r
- * */ ~2 i' s. j2 k& h
- public static function hashImageFile($filePath){
6 x0 w S9 G7 o4 ]. J! y+ {, f - $src = self::createImage($filePath);
7 Q6 ]1 O) G" x6 k - $hashStr = self::hashImage($src); 7 x. V' e! I9 u: C( c8 @
- imagedestroy($src); * @4 X/ U8 b! W* f0 T- S
-
) p: H+ H! E3 x - return $hashStr;
3 Z; |9 x8 A# n( L2 U" b a$ k - }
$ B3 \3 g; n* ^. O - % Z9 C6 U H% b" T0 }
- ) u/ \. {! u% W8 i/ _
- /**比较两个 hash 值,是不是相似 E4 f1 |3 g/ E
- * @param string $aHash A图片的 hash 值
, E$ a( m0 w" e - * @param string $bHash B图片的 hash 值
5 f. A( c+ w8 i. i, i+ q, r$ e - * @return bool 当图片相似则传递 true,否则是 false # T& X( ]% d+ O, v* j6 Z
- * */ 4 d( h, ?% A- I% j4 t
- public static function isHashSimilar($aHash, $bHash){ , s& F* [- f) ]) l
- $aL = strlen($aHash); $bL = strlen($bHash); & b. r% Q4 X) B" E( n, Y2 M
- if ($aL !== $bL){ return false; }
; w) w8 l% _! c: \1 H( c -
2 X8 p" L: @# r% t7 J, M& F - /*计算容许落差的数量*/ 7 g$ e9 N" b& \' K
- $allowGap = $aL*(100-self::$similarity)/100;
( j3 n' P* j& F; g( c& T - ( ^; m% l4 u$ N$ W) g0 N- v5 M
- /*计算两个 hash 值的汉明距离*/ + z/ c' S1 v& w; ]8 t4 a5 }: H
- $distance = 0; ( Z! q" G8 G3 Z
- for($i=0; $i<$aL; $i++){
9 D/ a$ d6 K! }. D; I3 H - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
' L9 N' d" [4 m1 k6 ~ - }
/ `( M/ F0 M$ e7 P9 V2 O - 5 r. ]+ t4 C/ x0 _
- return ($distance<=$allowGap) ? true : false; ' `$ ?8 l! b4 J
- } / l( Q/ F1 n% G# R9 n2 h6 w
- , B5 P# B! f2 @. ^
- 9 i& P8 s# D% g8 n; a8 q) I& g
- /**比较两个图片文件,是不是相似
; o6 A0 }6 U, m$ `/ _4 t" L - * @param string $aHash A图片的路径 & }: [) q9 w; Q2 {( J2 l' E! a
- * @param string $bHash B图片的路径
; y. l6 m8 i6 Z5 E7 ] - * @return bool 当图片相似则传递 true,否则是 false ( Q X( B( P1 j- ~- s6 |+ q2 F
- * */
& k1 K, N% k( A. I0 e5 R - public static function isImageFileSimilar($aPath, $bPath){
* d( C7 o$ Y% R/ ^% Q6 y) B+ X - $aHash = ImageHash::hashImageFile($aPath);
# | a! y6 z7 F1 r1 L1 s - $bHash = ImageHash::hashImageFile($bPath); 3 r6 ?8 S; W/ K0 [' i
- return ImageHash::isHashSimilar($aHash, $bHash);
* E( F2 U* d+ {3 l - } + p/ y* Z3 Y( o5 d3 z: P5 y
- 4 H* K5 P) U. W. P
- }
- H- Q0 z, P7 y% ^8 G
复制代码 4 y& [6 z( J/ Q8 s$ V* S+ k
- o" L/ _, b) s9 Q |
|