管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图/ N6 H: Y! _' m3 j% Y
( ?8 G8 V1 J2 e3 J& ?, E" g
; R( C. V0 ]0 D; [* i/ E8 ?0 f" T
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 B1 B! g! m0 \) L4 U# M
- <?php - ^% ]) Y) Y% ^' e2 v" h5 Q: g
- /** 3 A# m3 ?* w- ?7 R
- * 图片相似度比较 6 D! B& `2 ^( g
- * , Z! [3 G$ r( Y' K
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
1 o2 k$ i& t) w4 h$ |: L - * @author jax.hu % g* s/ ~; Z3 b9 v9 i$ k
- *
& L9 H" ]' H) j/ Y1 M9 e - * <code>
( d& T( r) v( ^ D - * //Sample_1
+ y$ B$ J$ | ^- h - * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
/ F7 S) t, V/ v( w/ W - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 6 o; k8 S. a% G( r1 p/ q* e) t2 Z: ~
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
5 {3 N5 _7 b' o d7 [4 y - *
' p. o9 o5 A5 r2 ~& b& [ - * //Sample_2
9 O; T7 _9 R" x8 U0 I7 [4 m3 }! {4 x - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
" g' {. o6 Y; E: o2 B* E - * </code>
; ^) [8 P5 z/ t6 z - */
& a; W! C- [2 Q- P2 K - ( P0 ]# w( v s0 i. j
- class ImageHash { X: A, d% I# s4 Y+ }5 i) e
-
/ L, j, `4 {. O - /**取样倍率 1~10
1 \ B* ?6 [) w: I. Y0 `: B - * @access public * M+ v, O5 C+ m: S3 w9 O: x: _
- * @staticvar int
) m' C* s3 f3 q$ o - * */ ) H- s8 }. G/ ?( R4 w, X
- public static $rate = 2; * B9 l- G9 s/ g' l2 s9 N' L5 h" G$ p
- 3 a7 U3 J' |0 m( U# t+ f
- /**相似度允许值 0~64
. R ~' q! I' s" X2 J* O B - * @access public
- q6 }) S; B9 _$ G: P! ^. Y9 q - * @staticvar int % b" o8 j7 [% `& d+ ^
- * */ 8 Z4 R2 P& g h5 l% V
- public static $similarity = 80;
# v( \, n( \9 i8 V7 Y* I -
. I" e8 R8 m( r" F8 B% y: s1 H4 \- ~ - /**图片类型对应的开启函数 0 m2 O4 W# @; l5 {5 L m: @! R. A
- * @access private % b, K( \. K1 H7 C# _5 p
- * @staticvar string ( _! w9 h4 v, w) U; p
- * */ 9 z) q' h! }& S9 b9 d
- private static $_createFunc = array(
$ f6 `2 G% n( x% o% n - IMAGETYPE_GIF =>'imageCreateFromGIF',
1 K! C6 p% t1 T2 v8 P - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 5 x8 ^% J2 q# v' v" N& i
- IMAGETYPE_PNG =>'imageCreateFromPNG', 7 w$ z! `2 r6 r
- IMAGETYPE_BMP =>'imageCreateFromBMP', ; Q2 K, X7 f* A$ } \" G, y7 S7 P L
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
* w; R4 y6 w) L& ], j Q - IMAGETYPE_XBM =>'imageCreateFromXBM', 8 g0 {. x- |9 n
- );
+ _' h6 R" w I- N" O' R ]) U - # q% M; D5 ?5 [3 i
-
7 d5 D! N4 G' b) p5 K& i - /**从文件建立图片
3 p/ c" x3 s/ C7 f) T: T - * @param string $filePath 文件地址路径
6 |5 M; u$ A5 ]- e) d7 J - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false " v9 v* V: H2 |" Q+ J
- * */ 8 `, G; B2 y, a# i) E/ y
- public static function createImage($filePath){
4 I( N; m4 ]& z( K {( i - if(!file_exists($filePath)){ return false; }
4 a/ y. h. [9 N' K* {8 \2 z- O - " C6 `5 `7 n/ [* Q# r
- /*判断文件类型是否可以开启*/
2 ]3 D: _8 l" _& {' f$ F1 z4 a - $type = exif_imagetype($filePath); 5 v. O5 v" D. l5 R' M b! Z" x
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
/ ?' L( y4 w) H# ] -
* p8 G* {) W# A" q9 \ - $func = self::$_createFunc[$type];
& D6 T, c1 A- _: n) f4 l5 G2 i# v, q$ u - if(!function_exists($func)){ return false; } , d, z, \: H7 b6 {( j9 F
- 2 U8 S8 Z1 o* D# K
- return $func($filePath);
; ~1 ~$ K% ]) c- N - }
) p+ S9 R0 Z; D -
2 m$ V# l# ?$ ^; x) s0 i -
8 q: A! T* z' |4 } - /**hash 图片
5 D5 X/ e. B& j - * @param resource $src 图片 resource ID
; S0 k+ G0 n1 W% A7 K, b - * @return string 图片 hash 值,失败则是 false
( ?( V2 N/ e9 C0 V) y - * */
. S+ d. d: V& [7 {( O! Q9 ^ - public static function hashImage($src){ 1 [% m0 t' v$ E/ S! ^
- if(!$src){ return false; }
9 |1 f1 f, J% ]6 p8 z! H -
& v e8 ?; K4 [ - /*缩小图片尺寸*/
6 i* A% V3 Y3 X' F5 \ - $delta = 8 * self::$rate;
& U9 v4 J% ]! R3 ?8 R* u - $img = imageCreateTrueColor($delta,$delta); # g9 H$ x5 b( ~
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); ; k) ^+ W# U7 E) O1 N5 R
- 9 M7 f! K/ P4 ]8 d
- /*计算图片灰阶值*/ X2 n/ s( j5 P
- $grayArray = array();
+ I: }( y1 W. ?% W+ x - for ($y=0; $y<$delta; $y++){ . a5 p8 J0 V! p. f
- for ($x=0; $x<$delta; $x++){ ! D' A6 R8 G4 w
- $rgb = imagecolorat($img,$x,$y);
+ i0 T$ e4 H+ B - $col = imagecolorsforindex($img, $rgb); 9 C0 M5 w' h7 V$ M
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 5 F* h& ]& ?% y% y9 A3 y% m* ^
-
+ U- _- m) J( r3 ?* W0 E! @/ ? - $grayArray[] = $gray; % I5 p$ `5 d% q2 q
- }
& @5 o) ~* w" b; n8 U - } D% D4 v5 R7 e0 [6 Y/ Q
- imagedestroy($img);
7 \3 c( O- ]7 I+ ?% \. F1 ^; o -
8 }7 c, I" f9 K' ?# w - /*计算所有像素的灰阶平均值*/ " a7 c# {( X& K% H
- $average = array_sum($grayArray)/count($grayArray); # n5 c! v4 k% _" P! u: D
- 2 D$ i& D3 D+ A/ D; V* i% ]% f/ z
- /*计算 hash 值*/
$ G" o; n: y2 l; _4 c - $hashStr = '';
" e% E" ] Z. _ - foreach ($grayArray as $gray){ ( T( X! g3 y- U' d0 ~: K4 g
- $hashStr .= ($gray>=$average) ? '1' : '0'; / M6 t; A/ G* Q+ x) c, Z( Q, y
- } 8 V- {! \+ E' a+ b- Q
-
- `' a5 t. j& f D# R - return $hashStr; 2 X% @: d2 \( Q# A, ]
- } 0 N4 I0 q2 }2 R# |
- + z% V8 R: n. d4 J6 Y/ U
- + L+ j+ |6 D5 V; E* J8 H5 ?9 v2 t
- /**hash 图片文件 u: O4 L- R* h, j$ k$ u
- * @param string $filePath 文件地址路径
. Z6 ^. }: _. `: D - * @return string 图片 hash 值,失败则是 false 4 j0 G( {7 d! m% x! ?+ c: V" a
- * */
: B$ m2 i6 b. A9 d# m! [9 x - public static function hashImageFile($filePath){
" x; ]) I( _ E - $src = self::createImage($filePath);
7 m2 J+ Z) |/ l/ B& E$ ?$ Y - $hashStr = self::hashImage($src);
6 c* z: x# e* Y* \9 |: m* ~. J - imagedestroy($src); ' K# {2 C' I$ ?- T* |, q: w- `5 v
- ' P3 B& q* `1 V- G
- return $hashStr; % J, k* R8 O* z8 p: ^" x
- } + Q# T5 _ U3 F4 c* J9 U9 {
-
, I, F$ Z) t E- d# m4 N5 n -
, R: b2 ~) Q2 q1 M, `7 {$ P - /**比较两个 hash 值,是不是相似 ! b" Z G, y: g- h9 \# Y
- * @param string $aHash A图片的 hash 值
9 v6 x" ^' Z$ {# F: B' `; L - * @param string $bHash B图片的 hash 值 . S" R8 V1 ~( A
- * @return bool 当图片相似则传递 true,否则是 false ( o& A8 L: K. G% ]; a
- * */ 0 I4 G/ E. z$ F2 ]( \
- public static function isHashSimilar($aHash, $bHash){
. Z0 L& W% Y: K- S. L5 \; Q: w) c, m - $aL = strlen($aHash); $bL = strlen($bHash); 8 w9 a( `, q) u U4 g# o- H! m
- if ($aL !== $bL){ return false; }
: x4 I! K' c) ] -
# u* h& y, }: z \$ [ - /*计算容许落差的数量*/
( F* x6 ]5 f, b3 i. X* B - $allowGap = $aL*(100-self::$similarity)/100;
5 R S7 L7 F5 Q3 \6 N8 } -
! d& i( i& L$ O( ~6 H/ C. u9 W9 S - /*计算两个 hash 值的汉明距离*/ 2 F9 ~: [) Q' Z# P; {: a
- $distance = 0;
: K& q. N. O! t& J - for($i=0; $i<$aL; $i++){ : j: Z" f( v4 L- T, r# Q7 R
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } % S% V: w4 [: f# q$ E( S/ S
- } ( ?8 G! F* C* x6 R# o7 f" d
-
, i0 Q: x3 P* H( C - return ($distance<=$allowGap) ? true : false; % C+ q% O _% q% H0 V4 ?
- }
, q' O1 w( M0 ?0 ?3 `4 ?0 ^ -
' y p& s: J2 n4 o& @" j0 z -
F$ p! Z& ]' N! r8 v - /**比较两个图片文件,是不是相似 - V; P* d/ _/ A5 G7 z
- * @param string $aHash A图片的路径
) B# B3 j( R: V5 B - * @param string $bHash B图片的路径
V/ t5 p- B7 z: _% s; f- B( a - * @return bool 当图片相似则传递 true,否则是 false
/ h1 h" u" M9 _7 S2 b, m1 r' F- U - * */ 1 u3 r% p# E, N& ~
- public static function isImageFileSimilar($aPath, $bPath){ . ?. C! g1 j2 u
- $aHash = ImageHash::hashImageFile($aPath); / g+ c( D/ d6 A U! V
- $bHash = ImageHash::hashImageFile($bPath);
: f0 j# |$ j2 b$ h2 ~ - return ImageHash::isHashSimilar($aHash, $bHash);
/ J# A1 F. C3 X- J% [, ` - }
J/ t% {2 Y- y" C* W* A - ' C: t0 x' e( @# }, G
- }
! O# V( d1 m$ s3 N8 A3 ?
复制代码 2 a/ x" C+ G) n4 g! z& m# \
5 A$ h- e( n) j1 k; O
|
|