管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图2 r$ M5 z6 |" ]6 E2 N; F0 A
( s: e! ]3 V* k9 b N' Q& I% i# ? |" _
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
; M) y8 l- S( H% v# N4 K- <?php
9 @& V* |8 r4 M S - /**
8 d9 b* w/ R6 J0 h- o! u - * 图片相似度比较
& m3 ]/ b8 d: H2 z5 J. [3 Q( |4 C - *
- g/ {. {/ o+ u v# }: Y9 q - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
" w4 V/ w8 W( c% B0 a' C! | - * @author jax.hu 7 X+ q% ^6 z) a$ M3 D) r
- * 1 _1 U: ?+ R" G8 C! T6 Z! N8 ~0 F
- * <code>
( ]2 v4 J# h* A8 r - * //Sample_1 . z1 u4 _. ~8 a) c5 n# s
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
- }. e* q- H. m4 c - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); ' a @- y1 V2 o
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
' Z2 q$ L+ i9 e. P- V1 T T) `7 y, u - *
& E7 Z/ b1 f2 y0 ]: a4 X; p/ P# p q - * //Sample_2
% s! A; C3 f) I7 l - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); ! N$ x0 y8 R: t H: A; `
- * </code> , t, L f, `; H' ]* h& o9 W2 t
- */
) a N1 V5 f; b% h2 z% p' T" C/ X - - k% ~% i% z( c+ w& l* A/ r" ]
- class ImageHash {
+ `; ]! m* j$ _0 Q, ~5 T - # e" T( R9 k4 ]9 O6 V4 I
- /**取样倍率 1~10 # z6 u* j: A/ p: ]& n
- * @access public
2 n) f7 T$ G' W1 |" L - * @staticvar int 4 q' g! }1 B9 Q O) a2 U/ n
- * */
! b5 y/ u/ A6 m7 X, N/ D: K - public static $rate = 2; # G( G5 c) G l
-
) J" z6 d( |/ Q5 P) u - /**相似度允许值 0~64
1 X& [. l( _' V+ D* R - * @access public 7 E2 s. j: [8 x) D. c
- * @staticvar int 0 \, T5 x$ s5 O8 N n) Q4 Q
- * */
2 V* v, }- q: u/ _3 C - public static $similarity = 80;
8 M, C f$ G5 m4 v: {. f -
4 h5 \( I+ P7 Q; O - /**图片类型对应的开启函数
' m% o: p9 e' g6 }+ F - * @access private
+ Z& f' `: A. L' a% L3 i# k - * @staticvar string
9 j6 A& C: b1 X - * */
: Z/ I3 n: A9 {# T - private static $_createFunc = array(
3 N/ G" S, |! w+ [8 C1 S - IMAGETYPE_GIF =>'imageCreateFromGIF', * L& Y. Y- j" {( y
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
) ^9 D7 C! R! n) M - IMAGETYPE_PNG =>'imageCreateFromPNG', 5 k2 f8 v! k; `$ t& c! i; w! X# B
- IMAGETYPE_BMP =>'imageCreateFromBMP', , ^, t: v8 W& j. J
- IMAGETYPE_WBMP =>'imageCreateFromWBMP', - S% e/ \1 g6 x7 c) {
- IMAGETYPE_XBM =>'imageCreateFromXBM', ! f/ D/ \3 q% g6 X/ G# a
- ); - F7 c( o2 g! ?0 L/ v( V. G& Z [
-
/ `( U( k2 p4 s' g7 z -
, M$ I; T/ M _5 ?( U* H/ K - /**从文件建立图片 2 V2 a: ^0 G! M+ i8 b
- * @param string $filePath 文件地址路径
}' _; r4 U% z7 y _8 e$ G - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 6 |% _; q" I L# x8 R T
- * */
2 h9 \& K8 x; m) ~" M9 u - public static function createImage($filePath){
6 v* H% e! b6 f' I! |! \0 d9 J2 x - if(!file_exists($filePath)){ return false; } / {' d& {- X5 x% B( ~- F( _
- 0 ^4 a. ^$ j9 D3 x0 T% a1 I: e5 f
- /*判断文件类型是否可以开启*/ ( `) R2 L: F+ A: v/ g
- $type = exif_imagetype($filePath); % b5 r$ a2 Q# K$ t8 `& S" }
- if(!array_key_exists($type,self::$_createFunc)){ return false; } + p. C# R' k( l5 G
-
( [' E( i% a& J& ~ - $func = self::$_createFunc[$type];
( m2 {0 j9 |+ C' j' o- E - if(!function_exists($func)){ return false; }
- B9 N" |& N% S$ f8 C7 t - , R" l( i3 D+ f Y3 c! @2 N
- return $func($filePath);
! M+ b x( v p" d' X/ y# l0 V1 F - }
' E0 v8 ^8 L8 l; O+ T -
2 W8 y& q! w1 n# q: o -
2 v1 Q. L9 w) D- f0 W' { - /**hash 图片
+ @+ e1 Y$ r+ c - * @param resource $src 图片 resource ID
8 O3 ^' h) i" D) o; P3 L - * @return string 图片 hash 值,失败则是 false
. K' p K8 ^! X ^* S1 a0 @) u - * */ / C- i' R0 N/ T
- public static function hashImage($src){
' X- ?# ~+ Y/ E - if(!$src){ return false; } / v3 o p; q* q+ k9 p# z
- & i i F# y) D- a; f- Y: c
- /*缩小图片尺寸*/
8 L, U9 C4 ^! ^% I - $delta = 8 * self::$rate; 6 U) |6 c, J3 l' D2 |# q
- $img = imageCreateTrueColor($delta,$delta);
. u r& s; Z; B+ f3 ? - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
* t& C& |" \. s/ b* T - 3 f' p8 @+ C' Z1 q
- /*计算图片灰阶值*/
3 X- B6 W7 M* d* s" C0 R5 l) G# v - $grayArray = array(); 2 | h& ?4 ~1 @! U
- for ($y=0; $y<$delta; $y++){ ' z8 c! r- r v+ Q9 ]: p
- for ($x=0; $x<$delta; $x++){
. k G1 _( L, c8 ~- b) U - $rgb = imagecolorat($img,$x,$y);
. v# V1 W3 {5 X - $col = imagecolorsforindex($img, $rgb); 7 b4 J; K4 K0 B3 d; i6 H3 P
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; # m5 X5 l$ i4 l- r: [
- , j# n9 |5 ^/ e: @+ Z
- $grayArray[] = $gray;
2 Y7 h, ^. H- P: p/ p0 ?8 r* F - } 3 e- h2 _2 u' P4 X' K' I
- } $ Q& l2 r" U3 ^! U2 I
- imagedestroy($img); 2 |% ]% T2 B3 c
-
/ R8 V/ ^0 Z, Q+ T# s/ Q8 L( ? - /*计算所有像素的灰阶平均值*/ 4 {5 \- A+ ^( N- `( a$ W3 q+ N
- $average = array_sum($grayArray)/count($grayArray); , P6 H! |, Z6 r
-
% U6 D# r; O9 f$ X$ g - /*计算 hash 值*/
1 E# O3 z; b5 p, k - $hashStr = '';
, P* g0 h/ D: _$ ], @ - foreach ($grayArray as $gray){
( u3 d3 u; P. N- B - $hashStr .= ($gray>=$average) ? '1' : '0';
" q' \! l2 T8 R9 F - } ( D9 u# ]% F2 ~
- / [4 k" U. k/ M# R, W: B
- return $hashStr;
1 u Y6 r2 L! W. U5 y9 n - }
0 `2 C) d* P4 Y' |/ m, W6 n - " x c$ C7 O9 y$ n/ Z: Y! p
- 9 E& L8 t7 t" @
- /**hash 图片文件 / z& |/ ~4 w9 N* q! X8 q: Y
- * @param string $filePath 文件地址路径
! g& _4 u/ K' N3 R - * @return string 图片 hash 值,失败则是 false 8 F* G6 H9 [; l" i, b& |+ Y
- * */
- M0 l6 A# q" U! R% q - public static function hashImageFile($filePath){
. N( g% P6 N! h+ j4 [% m - $src = self::createImage($filePath); ( i% ~. H1 P2 a1 P1 ^% f
- $hashStr = self::hashImage($src); % x& P5 @: b: T& H$ n
- imagedestroy($src); 1 J0 ?1 u+ o- X0 z1 w. W
- ' b9 G# ]( ?1 c! z( V
- return $hashStr; 7 |5 f) ^; W6 E& T* @8 y V
- }
& F5 X" ?* ~' G# U - " A' s( }( s4 s7 e, f4 T5 ~
- $ k8 Q' k6 T( |" N
- /**比较两个 hash 值,是不是相似
. ~! ~/ j' { h% P% ^1 {8 f/ C - * @param string $aHash A图片的 hash 值
# q( s5 _/ q2 e' i! l8 i* g& S0 U7 a - * @param string $bHash B图片的 hash 值
+ G5 Z6 V. i: n# q' z0 u - * @return bool 当图片相似则传递 true,否则是 false ( z: X. y; H# d+ w% q
- * */
d4 i( c* ~$ S1 L S: I; M" F - public static function isHashSimilar($aHash, $bHash){ / I3 l" L* z; h2 X
- $aL = strlen($aHash); $bL = strlen($bHash);
1 r7 l' r6 {7 ~8 Q - if ($aL !== $bL){ return false; }
8 {6 @" J u" ~' ~6 c+ `- j9 n4 ^ - 2 E+ C h o+ d
- /*计算容许落差的数量*/
~* d3 e' @ a - $allowGap = $aL*(100-self::$similarity)/100; s/ d) o, \% g2 p
-
$ a$ D& S2 U$ O! `9 \& Q# D - /*计算两个 hash 值的汉明距离*/ : X l2 B g9 E6 f7 j
- $distance = 0;
: W: [. V% T. o: r - for($i=0; $i<$aL; $i++){
7 P# F# T3 v0 j! w# Z/ R - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
) t) e! u" v& N) ^: q# N" U. L" X - }
" d5 t$ P/ ?2 P4 f' r -
, J( K1 d, x. m# I0 k# G - return ($distance<=$allowGap) ? true : false;
1 ?4 m1 }, w! U- F+ n/ A - } & J4 M2 [9 I- P
-
! S- ^+ j- U0 @* N -
- e D4 H( @: g0 L: U! ~# a - /**比较两个图片文件,是不是相似
% Q" x" |8 R0 A: |, A - * @param string $aHash A图片的路径
- ~+ N: h6 ]+ c$ {# u - * @param string $bHash B图片的路径
7 t, [ Z$ ^5 a, ], Q; h - * @return bool 当图片相似则传递 true,否则是 false
* ?! J( [7 E8 Q9 k: N& x1 X - * */
1 J2 _2 e1 r% {+ D7 p - public static function isImageFileSimilar($aPath, $bPath){ ) l4 E# ] q0 n* m
- $aHash = ImageHash::hashImageFile($aPath);
3 K; \6 J9 {6 U% v* Q, V - $bHash = ImageHash::hashImageFile($bPath);
' | D/ ~# o2 @- } - return ImageHash::isHashSimilar($aHash, $bHash);
6 m$ U) o, g! U - } + \/ ~8 {1 m% Y. E
-
8 e4 G4 D) _0 \1 r z+ @. E - }
9 o5 |: D; Z" P9 p
复制代码
; ]3 p$ v$ O3 D8 T' Q
3 l& a$ ?( G( @ |
|