管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
0 J( u% f; _& U: r. E
( R2 V$ O' V, \. R/ D: ~
6 X. D: ~( Y, J7 w; X, ~8 i7 Y由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。9 B6 B6 c2 ]5 F) s& q7 P
- <?php
8 }$ P9 \7 o% p: y - /**
5 w5 Y6 x0 }5 E; P4 b/ M" W7 K - * 图片相似度比较 4 y1 k \' K: i( e1 i
- * * I$ J. i/ Q' ?4 f% |8 u
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 0 V" G, K3 _$ R+ z/ c
- * @author jax.hu 4 p9 O9 E0 @. W* a& a
- *
8 T( y% [% U! a - * <code> 3 w7 N* f; ]6 i' J( z: x0 n g
- * //Sample_1
@ s$ s( }- \2 Y S - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); % `* e* r" E$ I
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ' z- r% _( Q! L2 l+ k# ~
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
/ c9 {, E l3 n, e5 C9 a - * 3 X' l% a" Q0 ~
- * //Sample_2 7 X4 b- C6 E# v! Q5 R3 D h/ c
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
9 [+ d4 H! J& P) ~7 s! Z - * </code>
: F3 v/ @! l, o7 m2 h - */ ( V0 I8 u/ w0 A/ y! X2 h: Z! w d% f
- 1 t# |1 Q. a) n, n8 F, F
- class ImageHash {
7 J! @& G" P" y - 8 y7 k4 a. K$ d7 S% F% X& X" l6 @
- /**取样倍率 1~10 4 q2 T1 l- m& g, p* U7 B
- * @access public
" M. k5 H$ k( a" P B! X; h - * @staticvar int ! ?, N+ @- F5 B6 k
- * */
( c9 n' }" D5 M6 F6 T - public static $rate = 2; ) A6 M; d: B% j4 V
- 9 z8 e( _ @1 s& f+ K Q
- /**相似度允许值 0~64 . ]# T: |: t' q6 e& ?5 T
- * @access public * t/ p- c1 `/ r7 I. p* Z9 x* \
- * @staticvar int ! p. e E$ D9 L8 v/ T+ g# f' n* h
- * */
$ r, M4 u$ C) E7 P. c: {: O - public static $similarity = 80;
" u% Q( O/ ` d3 |* }7 S) C -
' k2 ?2 |$ u0 y. f0 x, r Y - /**图片类型对应的开启函数
2 E' ?0 F& y, e& e0 X* {0 J9 z h% c - * @access private
. u4 u- N, p' o& ]- Q; H/ y - * @staticvar string * {% a/ u& c# `' v/ H
- * */
5 w$ @1 ]5 \; F' d$ x2 _ - private static $_createFunc = array( 3 L% P4 N. t4 {) C' j* S' J
- IMAGETYPE_GIF =>'imageCreateFromGIF',
! k3 `8 E% Q8 e+ C- @6 b- R8 ` - IMAGETYPE_JPEG =>'imageCreateFromJPEG', ( C/ g7 t1 Q( Y- M+ @; b9 S
- IMAGETYPE_PNG =>'imageCreateFromPNG',
* M; I, W) `" Q& j& }. X - IMAGETYPE_BMP =>'imageCreateFromBMP',
. w7 x6 j* P; p* W6 ]- t" Q; p - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
, B/ W/ h) L6 e# ^+ N - IMAGETYPE_XBM =>'imageCreateFromXBM',
P/ A$ H7 Z/ l' v' c, U - );
' \3 m% r/ }, g5 l! R: u& x( {* G1 n -
, h% ?( p& ^' R - 9 j$ ^& ]. o( b/ @0 w3 I
- /**从文件建立图片 ( X- h" l: }( H" {4 X' o; o/ d0 J
- * @param string $filePath 文件地址路径
' B4 t+ i3 z x$ |) d - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false - Y3 H; _9 w* p S
- * */ . b. @- t; ^, r' C
- public static function createImage($filePath){
) ^5 S' O; M7 {/ Z - if(!file_exists($filePath)){ return false; }
1 o9 i) j" E, i* S* X! X. ? - ( f" L' H1 e% G. ^0 j6 b
- /*判断文件类型是否可以开启*/ . t6 l9 i! Y; b. q4 N$ q7 E
- $type = exif_imagetype($filePath);
_2 j* |5 c' l/ f4 {' Y - if(!array_key_exists($type,self::$_createFunc)){ return false; }
. N; o- W( s7 L8 B, \: Z3 a* P -
1 u+ o$ Z6 k8 n" w7 H* X - $func = self::$_createFunc[$type]; , g6 H$ V9 V1 p4 D3 [; W) a. o3 u# x
- if(!function_exists($func)){ return false; }
, _/ ?, c4 @# T: R) { -
3 ]$ j7 y3 G+ A1 w3 | - return $func($filePath); & r9 p3 A6 T: V f: b- J3 i
- } 6 y, z: ?. e- v; u; m
- . \/ u, q2 U4 L l1 L. M
- 9 p' ?" x8 l1 d9 U( K- p4 V+ P0 q
- /**hash 图片
6 t2 Q6 O7 U/ S6 {3 C - * @param resource $src 图片 resource ID
- ^2 `: y7 U! m7 I8 M$ A# Q! n3 j - * @return string 图片 hash 值,失败则是 false
( p1 g m! N- l1 r4 j6 _# T; X& ^ - * */ 1 t( ~. _/ \) c, k# I% p9 O
- public static function hashImage($src){ ; E7 b8 s/ u; v- Y* U+ @
- if(!$src){ return false; }
! z) s! L" [) T% v1 I" M- A2 C - $ B7 H9 D; T& p1 [( S. J
- /*缩小图片尺寸*/ 7 q3 ^ j2 b' U6 Z
- $delta = 8 * self::$rate;
5 r+ J4 p# F+ U9 Y g - $img = imageCreateTrueColor($delta,$delta);
; P3 C9 U2 I/ h5 i - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
3 {' I4 ?& n& _# }1 a" r2 n8 g -
, U/ G5 i) ^# i) T1 ] - /*计算图片灰阶值*/ . L- U2 T& A- n {3 F6 a
- $grayArray = array();
, p; l# @: G3 L( [& x. g7 ~, ~5 U - for ($y=0; $y<$delta; $y++){
9 V; U# W$ d( X) ~7 d. w, e - for ($x=0; $x<$delta; $x++){ ' F6 Y7 ^2 w- V
- $rgb = imagecolorat($img,$x,$y);
+ E. g- |# C) x' y- z! U. U0 ~& [: f - $col = imagecolorsforindex($img, $rgb);
% _; z5 X. C+ ?0 y( _ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; $ D y: m% k: V3 i; z. a3 }
- 3 Y( I( d R* }+ J( y8 h
- $grayArray[] = $gray; . i0 d$ \( o6 [( @/ ?
- } , |' A5 V: M! ^
- }
& T. w" Z- r" Q' X8 A/ Y - imagedestroy($img);
: R5 M4 {- I+ v- r8 ] -
0 |8 m6 P$ v" @5 s8 g5 N/ M' \ - /*计算所有像素的灰阶平均值*/
% C' A | b- t$ G: | - $average = array_sum($grayArray)/count($grayArray);
- M0 }: M9 R7 p5 R' Q6 Q: f d) P: A - " x) }# m$ F: C4 e0 e5 q% }7 C. R
- /*计算 hash 值*/
! q! `! z* A7 G, z7 y$ | - $hashStr = ''; : c4 K6 [& h. m4 Y
- foreach ($grayArray as $gray){ 0 X5 F9 r2 |- {" F: B1 u+ C6 C
- $hashStr .= ($gray>=$average) ? '1' : '0';
) {' q: n9 A" J$ m7 }4 X4 R0 v - } * f/ l' q% x2 _2 z+ Z2 b
- 1 E0 z1 ]0 N, g! T( T# ~
- return $hashStr; ' G# ^: t* X+ L. {0 u
- } ( S+ N' z# A C% P$ m
- " X3 f* i* q! d; }0 r0 L/ K
- 4 m- K) N; Z& j* B; | c
- /**hash 图片文件
1 L. x6 e* E1 {( s - * @param string $filePath 文件地址路径 0 m. I; D! M* A- E
- * @return string 图片 hash 值,失败则是 false
3 d. G& B; V7 E4 I S% s( i - * */ 8 Z1 U: n( ^( J. k2 l4 }
- public static function hashImageFile($filePath){ " c2 p4 n# M: y" s" E0 H5 b
- $src = self::createImage($filePath);
6 q" n) E @0 S - $hashStr = self::hashImage($src); # W" e" G' o0 v7 h4 m. S; K
- imagedestroy($src);
`3 A8 n" W: e: z1 h -
3 B3 q" l: D+ f8 j% d4 T - return $hashStr;
7 p2 N% V- k: }/ B1 `2 \/ M2 o - } 8 a+ W5 W k0 ^; K
-
% G$ {8 d E! W; {* F -
: E2 f. l4 C3 M( L7 ~ b! i# U7 p - /**比较两个 hash 值,是不是相似 , N- b- d$ N9 t9 v1 s2 g$ Q1 V
- * @param string $aHash A图片的 hash 值
/ `% R- L% m- ~. j/ V( } - * @param string $bHash B图片的 hash 值
) s3 T: k, K( k - * @return bool 当图片相似则传递 true,否则是 false . V7 [+ X+ O: N0 Z
- * */
; S* Z$ j1 \, }- t! S - public static function isHashSimilar($aHash, $bHash){ " I0 L7 B6 x# ]
- $aL = strlen($aHash); $bL = strlen($bHash);
6 f0 s; ]1 V- D: Q - if ($aL !== $bL){ return false; } . k( a( z+ |- W' a' {
- $ G2 l* H7 a& k
- /*计算容许落差的数量*/
- O& m3 w. e+ M - $allowGap = $aL*(100-self::$similarity)/100;
! J3 ?/ Q. y) ^ -
) Q, f/ s$ N3 ]5 B' L, x0 a - /*计算两个 hash 值的汉明距离*/ 8 w2 u% G' v2 e: I
- $distance = 0;
3 y6 j& y. J, M9 A3 N - for($i=0; $i<$aL; $i++){ 1 l- p4 `+ ^6 G. h) Q0 W& @7 C
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } % f; m5 _1 T( t% n7 j) h- _7 N$ d
- } . O! w9 B5 |1 A: H$ e: v& N4 P
- : V% x7 k# B0 ]# w. R3 u, a* N
- return ($distance<=$allowGap) ? true : false;
8 J$ w( K' T' Y1 i m - }
( Q# N- O1 C6 d( a& h6 x - 8 N0 U( ?" m$ V7 N, O; `1 {
- 8 R* `3 V+ |7 o8 f/ H# w
- /**比较两个图片文件,是不是相似
1 i/ K2 _9 E4 ?" p4 _ - * @param string $aHash A图片的路径
* a" x1 R* t4 K8 Z* F9 z - * @param string $bHash B图片的路径 h. z Z5 Z+ A1 [8 [0 X. i
- * @return bool 当图片相似则传递 true,否则是 false
0 H; }, _9 ~$ Y' R3 { - * */ ; s4 m5 c: ~4 r' N
- public static function isImageFileSimilar($aPath, $bPath){ : _: Z4 `4 ?" C$ p9 U9 z$ P
- $aHash = ImageHash::hashImageFile($aPath);
; B! ~. A+ ~ l4 B1 E& k - $bHash = ImageHash::hashImageFile($bPath);
7 _! b. Q; z% u9 _/ p0 F8 j - return ImageHash::isHashSimilar($aHash, $bHash); 6 h3 E' W) l# @
- } 5 q" f: }. @1 o- H
- " R4 Z' q8 _" ~
- }( r6 D8 ^) z6 I$ G6 @& Z* J! `( f$ m
复制代码 # i/ K4 Q% C9 t* \+ `: d
; Y! i, h' Q' o5 n4 ]
|
|