管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
& e9 l0 D# y: w3 N( M& J5 o5 c7 u- p2 P" f9 A. M7 n7 m4 z
5 g3 Z. I6 G0 e% ^! M: X2 X
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。7 w7 `; j# u) }' i( \: t
- <?php
# h/ O* p! `5 I7 r( n - /**
/ k4 N6 D- z- B - * 图片相似度比较 & D- ?* t) e [8 D+ b7 i: n
- * 7 i7 V* m) r4 o2 ]8 [
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 8 g; m+ T0 s- E" v4 D$ k0 L; K
- * @author jax.hu 1 S9 E2 ` p! p+ \4 F+ O
- *
! f7 S1 Y5 G- U! Y( C. T - * <code> 2 G) _# Y+ k: I# c& S
- * //Sample_1 ! r* ]3 I! V3 C
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
+ B* { p! T- _6 O - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); , V2 b* |3 t7 ]1 o, z
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); N) H1 [" `0 M1 A! [) F
- * 4 {# e: q& A0 o8 J& [" _' k/ B4 t/ D4 Q
- * //Sample_2 ( j- v7 f2 N. ^: A
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); . J% U E. x* n; A
- * </code>
$ O# t& r) B2 n- ?9 L c - */ * z$ k* D" n7 _4 F; g
- 5 h, x; f$ A4 A/ p# o/ [
- class ImageHash {
# t+ [- N+ Z/ ~+ I1 k+ y - , [2 Z, q2 S) q4 z
- /**取样倍率 1~10 % e4 O+ b, k3 |
- * @access public
# n; |$ x0 Q; ]# m. E1 X+ A - * @staticvar int
6 M n- K3 M: p$ R - * */
$ Q6 F; O$ V, |0 z- \5 L7 b - public static $rate = 2;
4 i3 t' Y! S! F3 J2 ` - ' P3 C+ }, x" b2 ~. \
- /**相似度允许值 0~64
) N0 [* i$ o7 T# a& C- T - * @access public
( F$ \: y: p1 K2 R* f - * @staticvar int 4 `( ]5 r+ z1 p0 S- _2 h) s4 u5 R
- * */ ' ~9 Z9 _: l7 D& ^1 _
- public static $similarity = 80; ; p5 X5 b9 T2 t' X
-
( i) Q9 H1 W, F* S q - /**图片类型对应的开启函数
+ o9 a h2 V+ ^ - * @access private
3 k5 F6 q& B; N$ @& n Q - * @staticvar string / Q' G; S$ S7 M) B7 y' M
- * */ ( |/ k9 f% }2 _: o
- private static $_createFunc = array(
- J U& U9 a, S. ^( v; J! [ - IMAGETYPE_GIF =>'imageCreateFromGIF', 6 j4 D) l D1 o% q3 ?
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
+ ^: ~! U4 f+ p7 @& } - IMAGETYPE_PNG =>'imageCreateFromPNG', . M+ [' d z; |0 }: L
- IMAGETYPE_BMP =>'imageCreateFromBMP',
8 m1 y5 q8 Q T# n* r" z5 A - IMAGETYPE_WBMP =>'imageCreateFromWBMP', ' [ O6 u' n1 w8 p% C1 n% {
- IMAGETYPE_XBM =>'imageCreateFromXBM', " [ Y, ?' T b7 x( v* g8 P- Z! d# |
- ); % V% L/ h, T3 C3 P& l- G* `
- 1 a$ }! ~7 Q" q! V! W; n- w
- . f3 S: _- H0 |! ~
- /**从文件建立图片 9 T/ v/ _! p" G5 q7 n" J% U; T
- * @param string $filePath 文件地址路径
, _" f- x" m- R4 b7 q - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
) V& g* K6 \9 [3 m! V" @ - * */
3 T" K( Y5 c, ~. _2 y - public static function createImage($filePath){
% Z8 x4 f+ \7 p1 y - if(!file_exists($filePath)){ return false; } + L, Y$ ^9 k& N* C2 L
- 1 l& n! C; \) m- g. \
- /*判断文件类型是否可以开启*/ 4 s; m: m! D4 _/ l4 u, K+ j
- $type = exif_imagetype($filePath); 4 _5 |: `9 E- P
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
7 ^, n1 |& C5 z! s9 M1 y -
3 Q3 ^) }& u' c3 q2 } - $func = self::$_createFunc[$type];
: z5 ~! w9 O: m' Z( Z: [0 }2 r - if(!function_exists($func)){ return false; } $ ?0 ?: P" Y) b9 Y
- - V" t, s' u. q4 O) s ~
- return $func($filePath);
! W9 n7 ~6 q& q6 {6 R: H - } ; p6 I1 E7 l( |$ Y- @
-
" U2 a8 j0 l6 x2 B1 X! v -
3 I' s! P: o" w; o b9 I - /**hash 图片 6 `: ?% P3 R5 a+ s" d
- * @param resource $src 图片 resource ID
) \7 {& ?" M% @8 w5 _$ L4 a7 M - * @return string 图片 hash 值,失败则是 false ! [: V% w3 \# u0 _8 [
- * */ % t+ O- B& C$ J6 v% K/ H# h; M
- public static function hashImage($src){
Y$ K$ v: b0 a0 i |, z" O+ N* G - if(!$src){ return false; } 5 r; q! ^2 q% T( D- H
-
! p; Q: K G; _) F/ y0 I - /*缩小图片尺寸*/ , t# v4 y4 j: C q. P( J7 h
- $delta = 8 * self::$rate;
) s% I5 M1 d' F" O' A9 k% y8 R% | - $img = imageCreateTrueColor($delta,$delta);
9 o" U3 Y' ]: i) U# B& u5 M' X - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
5 ?. W6 ^$ V3 `+ h; }9 n+ _ -
/ H) ?4 ]" r) m4 [ - /*计算图片灰阶值*/ . X* ]# ~! t& t( u& ?* @
- $grayArray = array();
$ }+ g3 S3 m! [" Y& ~ e' F - for ($y=0; $y<$delta; $y++){ @4 V( q$ X( H: o& A
- for ($x=0; $x<$delta; $x++){
6 C- x3 w# e& Z1 r4 r - $rgb = imagecolorat($img,$x,$y); , \' f1 w$ Q8 H! U/ T. O5 `
- $col = imagecolorsforindex($img, $rgb);
2 o& M4 H! H; h% I# u$ p/ Y* \ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; - j. ]8 v7 Y" t# t7 \- w# |
- " v) z' p" T& R8 n7 t& X
- $grayArray[] = $gray;
4 H: M8 q( W6 ? d7 F8 E - }
5 ?+ u( D$ Z% I6 g - }
- b& f" E( o0 n# Z, g: Z - imagedestroy($img); ' j3 o: S$ a& v5 D
- 5 R o; E) `+ n6 T a
- /*计算所有像素的灰阶平均值*/
( Y- H1 S: H# o5 ~! ~ - $average = array_sum($grayArray)/count($grayArray);
2 K. o; a/ e9 A& k A9 h( _! ?4 G -
" I! l+ B, k4 W - /*计算 hash 值*/ # y% n# t) N5 I
- $hashStr = ''; 9 l {2 V" y. C( l7 P+ ^; f
- foreach ($grayArray as $gray){
* q$ q# z8 c* _( I' \ B - $hashStr .= ($gray>=$average) ? '1' : '0'; # b* G; R L) M+ J# L' ~
- } 2 ?; `2 {/ ^% S# T7 J
-
; d7 f r) @1 m- i/ X, k- h; ~ - return $hashStr;
$ S. g4 l$ E, u4 E u - } + [) s3 d# f4 p* ^% n
-
" i) w. q J; P( k -
4 G2 A1 A$ v! C! ]+ E - /**hash 图片文件 ! D! x8 ~/ _ c, N% g/ v' } f, ^
- * @param string $filePath 文件地址路径 2 h5 v1 r( }: I! `6 M
- * @return string 图片 hash 值,失败则是 false , l6 j" M( ], f, b' W, u
- * */
1 E" R% I* I0 B - public static function hashImageFile($filePath){
4 {4 A; ]& X8 r! ]: I7 T5 L% s; N( Y3 U - $src = self::createImage($filePath);
) F. w& b) h( C/ f- B, R8 E - $hashStr = self::hashImage($src);
8 \2 o8 X S: p/ C - imagedestroy($src);
, i1 r) j7 y" \; a - ' l: Y/ O6 y9 A
- return $hashStr; - s5 j5 |1 N# v5 ~$ [' e1 [9 q" l8 E8 R
- }
( I& o" {' X% \* l1 }# a, y$ d9 X9 ? -
R7 F4 Z! A+ L: U* \- w& K -
- u! n- m+ z5 Z; g' t0 i' O - /**比较两个 hash 值,是不是相似 O6 a+ [6 q+ `+ l3 Z, J7 Q! ]
- * @param string $aHash A图片的 hash 值 ' h' T; E; E) z! q( `7 c4 _
- * @param string $bHash B图片的 hash 值 : A9 Y: {4 v H: {
- * @return bool 当图片相似则传递 true,否则是 false 1 `" @: L3 c5 j! I% n
- * */ ' s/ ]4 Z2 N) W
- public static function isHashSimilar($aHash, $bHash){
# ?7 n4 d$ T6 D - $aL = strlen($aHash); $bL = strlen($bHash); + v$ V1 x/ f& C& d. U2 N% l
- if ($aL !== $bL){ return false; }
5 R3 a+ V; M! |& c6 H - ) ]* g! Q/ C: L9 o& s9 g
- /*计算容许落差的数量*/
0 @0 s/ h9 @& o$ x& R - $allowGap = $aL*(100-self::$similarity)/100;
# V H& v& Z; B& a$ g [' n - 6 `3 B4 V. }) S1 L7 T! c+ U2 a
- /*计算两个 hash 值的汉明距离*/ ! x- e$ n' j$ M/ y) z$ p0 ~0 k, z
- $distance = 0; ; U" o3 v3 `2 ~+ |" Z; t
- for($i=0; $i<$aL; $i++){
g2 i/ [/ E' u% ` - if ($aHash{$i} !== $bHash{$i}){ $distance++; } - @5 L6 K# D T* {* \& \( K
- }
- E% ^7 a! L Y; ?+ s -
% [; k6 M3 L5 H- y5 o# u - return ($distance<=$allowGap) ? true : false; ! o/ H. f8 G4 g2 U3 z( S) n
- }
% ], i5 K) t! e4 E1 Z - : q5 P9 I& R" P% M+ ?) w
- 0 y k1 o p) i7 f! `) o! s
- /**比较两个图片文件,是不是相似
) ~. O6 u6 \/ W - * @param string $aHash A图片的路径 & e. B1 a4 @1 F( E" M) M' ]
- * @param string $bHash B图片的路径 ) T' v3 n$ F, a8 m) p0 Q; P9 {
- * @return bool 当图片相似则传递 true,否则是 false
, X! S( V3 f) v% N2 b* `4 S - * */
8 I8 D: y# y: j4 K( C9 q( O - public static function isImageFileSimilar($aPath, $bPath){
% ?7 d5 O$ t$ n, W1 ]# B9 N% C - $aHash = ImageHash::hashImageFile($aPath); 5 i$ v5 o9 ^; F" ^1 |
- $bHash = ImageHash::hashImageFile($bPath); # V. e+ Y5 Q+ u: S% { F# j
- return ImageHash::isHashSimilar($aHash, $bHash);
g! z% H' j$ B ` - } ! E V8 P! b0 W+ b7 E3 {+ P/ x
- , Y1 m9 R0 j+ ?7 E7 y
- }
2 s( b! [( y& S1 W2 h
复制代码
# `1 N5 w% U8 G/ w$ V. N% {6 `! [ z5 a9 t3 l0 _: D6 z# y
|
|