管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
D& k* ?& f3 X0 m
5 ~# a. b' q {$ _, h
& t' u" _3 J( c- \/ }* ^由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。: M5 P& O5 m2 u
- <?php ' g* P% Z7 x1 O, `, g/ S; U7 T
- /** @, o/ d6 F8 x9 `- n
- * 图片相似度比较
C9 l, ~- ^* b" I, [- x! D% I0 ~ r - * 3 L; J8 {4 a+ Z; u
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 1 e7 i( Y: @) d! [( q
- * @author jax.hu
# C: B& H' i& k1 H) {5 i5 t - *
/ F1 f9 p. T6 S* b - * <code>
3 P: Z# V N- J" O3 i* A$ c - * //Sample_1 ( H0 t' d. ]# Q. O$ f( D: j
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
$ F8 q* H; c; v4 S3 S - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ! e6 K- m: b5 V& N2 \
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 6 t% b. t1 d. c5 `; \+ |
- * ! c( ^0 x' U3 y& r9 i% a
- * //Sample_2
$ i- r3 h/ G- g1 E5 P8 V - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ' @5 c4 Z0 P/ B* H: n
- * </code>
) j1 c) c0 Y; O2 b5 W# s4 g - */ 2 ^, {! Z; t. d) }- v8 D- P/ ?; v
- 2 s7 @+ J+ j( A/ W4 j9 n
- class ImageHash { % \7 P/ K, b! _% ^0 W2 ^( l
- & X& F1 f2 {$ L7 Z( W; o* B
- /**取样倍率 1~10 ) i; f: E7 p7 n; m
- * @access public ; b! o; X( H8 }' z1 n, I8 y
- * @staticvar int
2 ?# E3 k' R' `" F5 ?, U - * */
9 ?: x5 H+ g1 F6 v) ~6 E/ \ - public static $rate = 2;
2 B; P" }- k1 Z - 6 V3 e& {" _' A7 F2 X& x) ~% d
- /**相似度允许值 0~64 1 z- d' |* t: `# T' G+ r# ^1 b
- * @access public
. h _* I6 k' ^) e& ]3 M - * @staticvar int 2 }9 T' ?$ x: z9 p/ R& W
- * */ 0 Y: \: f+ k2 T0 x. \: I7 J
- public static $similarity = 80;
* Y$ A; Y- _* p: f3 ^ - 9 s0 l" Q8 l; f9 U
- /**图片类型对应的开启函数 ' }7 @/ O8 P# ~( c$ Z
- * @access private
$ d7 d n7 E: K, T3 m/ r - * @staticvar string
5 {+ ?; x) L( I6 G/ m( f - * */ 6 q- h, x }0 n/ p5 S6 A I
- private static $_createFunc = array(
9 E$ G$ Q3 G* m3 ^ - IMAGETYPE_GIF =>'imageCreateFromGIF', 6 X2 I. h0 u. _! n; B
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
# V9 C4 h" F4 D5 l( t0 `& v5 W - IMAGETYPE_PNG =>'imageCreateFromPNG', ! G. w2 y: j4 }/ x' t$ Y
- IMAGETYPE_BMP =>'imageCreateFromBMP', 2 V7 N0 W" a1 O8 S
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
( K& y! L0 h N$ j \, l. y* V - IMAGETYPE_XBM =>'imageCreateFromXBM',
9 N* v0 N2 M" a# m - );
: V8 _* J! @; x - 2 V0 p% Q5 `0 t, h) l
-
7 _( |% z: D$ G$ @) Z - /**从文件建立图片
$ l+ \# U# ]% s$ Z. x' b* j4 J! K - * @param string $filePath 文件地址路径
J8 w! Q. W& R1 G2 i - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ( \6 O/ u- s' C, b. g' t0 K
- * */
I9 M: W0 c8 b6 }& S& H& ` - public static function createImage($filePath){ 4 i- r5 d* o3 h5 N, y# t
- if(!file_exists($filePath)){ return false; }
; m5 e4 Y% F* V& U3 f - $ |! o4 G- O( [ H# l% l/ t1 Q3 K" h" w' k
- /*判断文件类型是否可以开启*/
/ s4 k8 A. ?5 [: ~ - $type = exif_imagetype($filePath);
& r. w* T+ ]3 l% q, O' } - if(!array_key_exists($type,self::$_createFunc)){ return false; }
1 J, _( S& m! [( O6 n d -
7 \ f1 v/ J' c0 x' a; e - $func = self::$_createFunc[$type];
6 _8 n8 n# K/ u' \1 V - if(!function_exists($func)){ return false; } 6 t O, p) m. {/ y. G( ]; c% D
- % L% }- n& D) u
- return $func($filePath); 8 ~$ s1 A! ~- P- _$ K& l3 \$ d
- }
( e/ J- x( c( V. J- J8 i& C! }2 K -
; N, e+ \& [3 j -
3 o5 Z0 H0 T; r) j. T - /**hash 图片
& V/ E9 j% h' k& F- V i - * @param resource $src 图片 resource ID
- |9 P( J. z* ?* I% q# O - * @return string 图片 hash 值,失败则是 false
6 V/ m7 X& q/ I- h/ Y0 B - * */ 3 [& d5 l) E, R- Y" A4 y, a
- public static function hashImage($src){ 1 Z9 Z" w7 M& Y
- if(!$src){ return false; } / N8 y6 L% U1 E( ~2 k: P
- 3 \' z3 E4 H; W9 Q- k! q
- /*缩小图片尺寸*/
& @9 D: ^# w* D3 R8 @ - $delta = 8 * self::$rate;
1 w7 R; ]) n, L1 j/ x - $img = imageCreateTrueColor($delta,$delta); . ` d- M/ a$ a+ Q/ ?
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 8 E6 n/ S+ s8 e6 R+ Q' L X9 l, F
- . o; b9 c7 J% d
- /*计算图片灰阶值*/ 6 ]: `4 _9 y0 R3 \- e3 i$ B
- $grayArray = array();
: E* V. n) q; F* k# d4 u - for ($y=0; $y<$delta; $y++){
5 Q+ }) C' Q, `! a - for ($x=0; $x<$delta; $x++){
1 I0 d5 E( L+ x- ]5 |/ p! | - $rgb = imagecolorat($img,$x,$y);
+ I. q4 o* I' z - $col = imagecolorsforindex($img, $rgb);
, N& b9 j0 d% s8 t! H8 _ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
2 ?, E8 u9 n7 _8 S -
- H) ~ ~3 P5 J7 }6 z - $grayArray[] = $gray;
' h, l* f% P/ @ - } ) R1 r) ~' Q( M
- }
4 D/ a b0 B1 K - imagedestroy($img);
' c' e1 E/ G, N: A -
2 [) u, s m& a6 M/ y# \ - /*计算所有像素的灰阶平均值*/ 3 c$ a }7 f% V7 X9 z
- $average = array_sum($grayArray)/count($grayArray); 6 T/ B/ {$ U; K! H9 W$ t/ ~
-
. x* l2 R4 C3 S& E - /*计算 hash 值*/ ( B; x0 S& T |% T8 t
- $hashStr = ''; 1 ^. ~) X) D0 }- e, V
- foreach ($grayArray as $gray){
& _1 |5 R5 h* d4 h - $hashStr .= ($gray>=$average) ? '1' : '0';
) ^& b% c- V9 }, s# { - }
+ c* g4 e7 r/ h( T7 O -
! O$ L& A! E& p# o( T- q - return $hashStr;
$ }" [; P4 h! w2 g - } 1 w+ h4 @8 |2 C% |
- + x0 Y1 m% U7 v- Y% K
-
" d$ J" u5 Q; g- {$ a - /**hash 图片文件
2 i$ I5 H, q5 h6 z4 Y3 w/ ] - * @param string $filePath 文件地址路径
0 v: w& E3 v' B. E - * @return string 图片 hash 值,失败则是 false 4 O2 _# a2 v `9 {9 x/ U! v
- * */ & o1 b, J, G, @3 @
- public static function hashImageFile($filePath){ 7 ?( y2 b# h- V
- $src = self::createImage($filePath); 1 C- D) k5 j3 \/ o3 d4 @- Y
- $hashStr = self::hashImage($src);
! x( V# I; v) o+ \8 | - imagedestroy($src); 9 }3 W# t. O( u: {, d
- 7 ?$ K1 \& S$ s" J1 [
- return $hashStr; 9 t! W: Y/ F/ q2 N
- } c, D2 j" |; A
-
( G1 R1 w) D& n( s7 K5 r - 7 f& M0 b) v# {
- /**比较两个 hash 值,是不是相似 . E% V9 R2 g6 n- h9 E. I) u
- * @param string $aHash A图片的 hash 值 5 N. A1 Z1 v7 d- L
- * @param string $bHash B图片的 hash 值
: w! Y. k$ I/ D9 d1 E! i, h - * @return bool 当图片相似则传递 true,否则是 false * n6 K6 Q7 T! e& _/ _; I# u
- * */
% T o8 i& i; Y2 C( ~- H& m! M- g - public static function isHashSimilar($aHash, $bHash){ : K3 K8 @6 V5 \0 W
- $aL = strlen($aHash); $bL = strlen($bHash);
4 m+ l9 L6 m* s( T$ |' }0 d3 w9 | - if ($aL !== $bL){ return false; }
' l. s. I: w; h) h6 m/ Y -
: l7 H: ~% b; ~# H2 j - /*计算容许落差的数量*/
) G$ a) r2 d* q* y' J4 I: p - $allowGap = $aL*(100-self::$similarity)/100; . Z4 R; l+ \) B; d0 ~. n! @8 V
- 9 r. B' L, h& x7 s! ^+ z5 Z; h6 P
- /*计算两个 hash 值的汉明距离*/ ; x: \) Z9 c& {8 |2 v
- $distance = 0;
. l, D0 x: g7 T% }# I, p) B3 h - for($i=0; $i<$aL; $i++){
% @6 i, n' u8 W( G6 X: q3 D - if ($aHash{$i} !== $bHash{$i}){ $distance++; } : \8 O2 x2 U* Q: V
- } / ?7 P! u) {8 F" c3 {
-
8 p. i$ M/ D8 {. @! | - return ($distance<=$allowGap) ? true : false; - l* O( B/ y3 \
- } / M! _5 [+ u* g! p9 x; U
- 7 W) d( a- V. L' w3 ^# u0 c
-
8 K- V0 |; j' S6 E% [2 `# H2 X" a - /**比较两个图片文件,是不是相似 , D& k4 d0 {/ W- a8 b2 u
- * @param string $aHash A图片的路径
3 r( S5 N( C( Y& Q( T: W! A/ q. M - * @param string $bHash B图片的路径
% ^' }1 G, g4 I& R) d - * @return bool 当图片相似则传递 true,否则是 false 0 s6 H1 t Z m1 `
- * */
5 E @1 E. z/ t+ O' l' J( o" E) \ - public static function isImageFileSimilar($aPath, $bPath){
: f4 _- l9 o( \8 i - $aHash = ImageHash::hashImageFile($aPath);
" e! ^% Y f% E6 U7 C& Y - $bHash = ImageHash::hashImageFile($bPath); - f. K# P( T9 H- z' c- c
- return ImageHash::isHashSimilar($aHash, $bHash); / _. o$ ?, o; `7 O7 T! B9 F
- }
# H3 _$ @. j3 I& { - ' @7 A9 |" s3 {' n' }0 A
- }
3 w' }/ k; U, w5 S
复制代码 0 i; Q) t* H7 S% W% M
# x( [* i- o, ^. ~4 p! I* _
|
|