管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图0 m1 O. f: D$ u) M( s9 a3 l9 X
' p* s4 ^/ P4 M5 a: X( O6 U; f
1 {; I0 g5 W. U }7 t- o
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。; [% ^' T/ A4 R* p' K
- <?php
# L* C; N- ]7 p8 s/ G+ l - /** , \& p/ s, G9 H- f9 }0 y
- * 图片相似度比较 5 f; K% N* j3 Y. f4 \
- *
1 @; ?$ \: O3 a; Y4 K8 z- P - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; $ A. n0 I! i* E4 R
- * @author jax.hu 1 \: p( s- K# f
- *
6 p, w% {# N6 c7 ~* g6 | - * <code>
- f, ^/ d# K ?$ P( s - * //Sample_1
6 h* Y1 H+ w1 L' R0 X7 c# _ - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 0 p! a% }% e8 X0 Z+ \$ j
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
/ s1 X% E G! { - * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); % ~; U& s# X1 i5 _7 }$ B7 K
- *
* K! L6 [, g# _; S3 Y/ D5 H - * //Sample_2 ( l9 V8 R' d! _1 O3 G
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); & C) L1 r! O0 {" }+ F9 m0 V2 N
- * </code> & T' |) D; v8 y' r( q/ H7 i) o
- */ ' m3 e5 R8 ~) g# f# A: z6 u0 h' G* N
-
; _9 J; F+ D6 h$ }- }4 K. U( w - class ImageHash {
- y. M/ K M* f" B; C - & [7 A! ^: F" ]3 f- S: D
- /**取样倍率 1~10
! a9 s8 t T) `. n# F+ l5 v" r - * @access public + U D/ _9 e) x9 T! U
- * @staticvar int % l" e! t+ H' T" C% y
- * */
# ^" y6 u: v+ Q$ r( a - public static $rate = 2;
! Y* z, }# R* O+ }7 { - & G9 b4 B6 a" n* q. U7 x) M
- /**相似度允许值 0~64
; i- l% D! T# }# @ - * @access public 7 ~/ L* ~+ r2 }" b/ w5 V
- * @staticvar int
8 y' U/ l2 m# f' D1 e - * */ ! F: O) y! E2 e5 _4 f# o9 ~5 m
- public static $similarity = 80; & l, h' [& V' O
-
( b9 e0 ~6 o& Q0 O% `/ t; g - /**图片类型对应的开启函数 ( Q* P( W* _* w, i& e! |4 n# k
- * @access private 0 ~% W) L2 V5 v5 R% \/ {
- * @staticvar string 0 a& l9 E0 _. i# S# `0 c1 E
- * */
8 `; W' D/ r) Q3 x - private static $_createFunc = array( 4 G0 t: { j( p$ P k9 f1 Z
- IMAGETYPE_GIF =>'imageCreateFromGIF', $ F8 @! o9 ~1 V0 y5 }5 `
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
; n1 `( n2 D: R/ U( u: U0 ~- u - IMAGETYPE_PNG =>'imageCreateFromPNG',
( s; p0 i/ G1 ^ - IMAGETYPE_BMP =>'imageCreateFromBMP', & z) J" d6 Y- {0 w) |% W" Q3 ?
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
& m. q' _8 U5 u* t4 b - IMAGETYPE_XBM =>'imageCreateFromXBM', . B' M0 ?7 M$ }; I
- ); 8 o5 ^2 Q% [0 B, a* Z% h
-
& N/ G6 f, A0 @4 B - % d. e+ s# u" ~ {/ E3 [" a$ k- Y
- /**从文件建立图片
5 ]) N1 u$ [2 U2 R7 _ - * @param string $filePath 文件地址路径 9 e W/ p% A; [! o' F; Y3 I4 C
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
+ H# F1 I* R6 Z) { - * */ 0 u2 K X3 Y0 e; k4 A3 m" ^ ?
- public static function createImage($filePath){
) ^/ V2 t- O( p0 V1 L - if(!file_exists($filePath)){ return false; } 6 q7 ^5 b7 J9 x J
- / y" M# |+ I+ N8 ~
- /*判断文件类型是否可以开启*/
8 w1 }0 F) A+ @/ v - $type = exif_imagetype($filePath);
' c" a/ [ S* k4 i! U8 z - if(!array_key_exists($type,self::$_createFunc)){ return false; } + }, M& {! ^3 z6 r% u2 N
- ( X, r0 ]7 P" s+ ]% m3 |. Z o V# t
- $func = self::$_createFunc[$type]; # r8 h }* s% Q6 A+ m; N
- if(!function_exists($func)){ return false; }
) U7 [- K; {8 N" O5 M8 d! q1 L% U; l - ) ?% M. P! H8 k$ ]
- return $func($filePath);
) |$ c: u/ M0 h$ b3 [ - }
. ^) r6 j. z6 @7 l; o" f0 j) Z$ e -
8 Q$ x0 n; _% i# k+ x -
$ p7 d; Y4 N" i8 D, s& | - /**hash 图片 $ ?7 X# J3 v' M7 ?1 m% d- p
- * @param resource $src 图片 resource ID
- e9 W- Q6 U# z0 T' h - * @return string 图片 hash 值,失败则是 false 4 F( u0 v- f2 W" E
- * */ " ]2 h- g/ }( F3 G6 p, N( z5 \/ \) K
- public static function hashImage($src){
$ M1 Y8 F1 n* e, r2 P3 t; Y - if(!$src){ return false; }
: S0 a) L% t6 c0 {: q -
" R3 F6 ~4 `5 o$ W, z7 w - /*缩小图片尺寸*/ * F0 Y/ N' L+ o+ @0 o
- $delta = 8 * self::$rate; 4 G) v& j Q& _+ e8 r. @
- $img = imageCreateTrueColor($delta,$delta); * h& C) X$ L& E
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
T: g& s- s1 U2 P+ Z - w" P" b' g& F7 P' f) t/ f
- /*计算图片灰阶值*/
6 O' P$ K8 y( ] - $grayArray = array(); ' q: U; H& i$ {3 M# d* N
- for ($y=0; $y<$delta; $y++){
4 }4 S, X+ e0 a; w1 _6 b8 D$ H - for ($x=0; $x<$delta; $x++){
) b4 h( k( Z! d# s. Y' A4 j - $rgb = imagecolorat($img,$x,$y);
7 H, m" o- }* k [; c2 f - $col = imagecolorsforindex($img, $rgb); , u# c/ v1 w! b, M. `( J4 x. v4 P
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
5 N: C' D3 H s8 m) e+ T, p8 [: Y - 7 T1 H3 ], [5 Z
- $grayArray[] = $gray;
w( g( W4 X; _4 M$ Q# f - }
% Q$ z+ o, V$ ~- u* \& [. l - }
W- Q3 y$ N1 [- P& ^* P - imagedestroy($img); ' L" Z$ g& }9 I6 G- k
- " n$ P& g- {) ^' H3 [- P+ s
- /*计算所有像素的灰阶平均值*/
: w; e+ b) d7 ?! B* ~3 ~3 g - $average = array_sum($grayArray)/count($grayArray); % `! X, K9 _+ t# d
-
, R0 \% {8 K H% O$ Y u' E - /*计算 hash 值*/
( M& [3 Z% Y1 k2 R+ J - $hashStr = '';
2 ~( m, ~4 w- Z! G4 g/ Z - foreach ($grayArray as $gray){
; }& F, x* e7 k$ @/ X0 r - $hashStr .= ($gray>=$average) ? '1' : '0'; / C+ }9 M% z$ E/ u/ Y
- }
9 v! B9 c3 o6 a: f -
% \% H1 @, g* H* r* b - return $hashStr; 8 s7 ?6 V. |, K' p1 a
- } ( P4 j N+ O1 H) o9 j6 F4 k- |0 |8 t
-
! Z) I- w+ O5 `6 x0 d - & g3 [0 _. L1 S' b1 i
- /**hash 图片文件 ' e# B) A' R$ d; o
- * @param string $filePath 文件地址路径
: e3 e) ~/ v5 L. _ - * @return string 图片 hash 值,失败则是 false & T" P. M- T* v3 _, h- ^
- * */ + O$ E3 v: |, T( l- s+ Q
- public static function hashImageFile($filePath){
' z# F9 A/ u& z4 @* Z$ I - $src = self::createImage($filePath); " l \1 P' ?" S @1 p
- $hashStr = self::hashImage($src); + S; m9 b7 n8 l3 H+ }2 b6 o5 }
- imagedestroy($src);
8 i! s" z' a" L% U -
, }. O" l8 o5 i2 B - return $hashStr; + ], `6 J4 X: t$ I Y% e4 r
- } + ~; {) f: x+ K& T3 D% P+ r
- " O7 q- X9 B5 }2 i0 G6 b+ S! C( n
-
8 y" g/ \3 b- X1 | - /**比较两个 hash 值,是不是相似 ; f% [1 e. d i! ~2 X$ w' o
- * @param string $aHash A图片的 hash 值 4 |$ v- Z! o. F4 E
- * @param string $bHash B图片的 hash 值
3 g0 @- U! u6 E" H - * @return bool 当图片相似则传递 true,否则是 false ; s2 ~8 f! k: I) C
- * */
0 v7 ~, T# v0 J+ ? - public static function isHashSimilar($aHash, $bHash){ ! }/ M) f! c# ~; [+ q
- $aL = strlen($aHash); $bL = strlen($bHash); 3 t Y: \- [ O
- if ($aL !== $bL){ return false; }
' k3 P2 v- u' \$ k: p -
@/ H) ~! g; w0 g - /*计算容许落差的数量*/ 5 f5 O5 i/ z2 P& e) t
- $allowGap = $aL*(100-self::$similarity)/100; 3 f# F3 q. `: r. V" x7 r5 F8 |' m
- 9 r: s7 d9 d" k. W! |
- /*计算两个 hash 值的汉明距离*/ 0 k7 S3 O/ p9 N P0 F/ g* \( R
- $distance = 0;
# g4 G, i# ~# A; D- C - for($i=0; $i<$aL; $i++){ ( w, A* E1 s, w7 i; X- b) w
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
5 U& |+ f- F/ m" X* @, f, v9 z - } % y1 }9 o( u% u: W W. v
-
6 g2 `' p) |) F+ N9 q4 m - return ($distance<=$allowGap) ? true : false;
1 m% ~+ W. C2 ~/ Y% G - }
8 B9 [! O, v- h; q -
0 ~+ X A8 k) ~: Z* S) _$ y. r5 h$ E -
" ^) H" q5 Z0 e9 V, ^2 W - /**比较两个图片文件,是不是相似 + C! q. q1 g" G* ]0 z8 {$ P
- * @param string $aHash A图片的路径
( t. i' ]( Q! {4 b* p - * @param string $bHash B图片的路径
' o$ _4 ?5 E9 G$ S7 X - * @return bool 当图片相似则传递 true,否则是 false 8 }0 g6 w4 V) {
- * */ 1 z J& ]5 @- P5 `' o. u: Y( ^8 ~
- public static function isImageFileSimilar($aPath, $bPath){ ' K) p, e* k0 P( U. c
- $aHash = ImageHash::hashImageFile($aPath);
) s: H I% O+ N% `' Y9 `% b - $bHash = ImageHash::hashImageFile($bPath);
, A+ Q& r7 H) M* p: m - return ImageHash::isHashSimilar($aHash, $bHash);
9 R# s6 f1 t8 x# S* H: B9 [ - }
: t# ?! c# s* s -
, K9 h! z! f% f! w* B' F - }/ u* |1 {5 r$ T* y& Y: M1 c
复制代码
- T& ~! @/ t6 w# j9 }6 G- G" t6 ?9 D4 e) j
|
|