管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图% G3 a% Z2 A& W$ e8 ^ Y5 [3 B( D
: ]8 E5 V3 S! q0 O j4 B
1 {. g4 l2 S+ G& o9 \由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
, U1 b% k& f8 q( P Z9 P4 C2 W- <?php
# v" Y4 M9 w/ [9 _, G - /** 1 B/ T+ r0 ]( D) X6 q
- * 图片相似度比较 : g$ p- x% k+ e/ ]
- *
6 Z1 w7 a+ C& G* z4 q - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
2 X) C! c& Q" Q9 I - * @author jax.hu ' y- ^; N; ~; y+ ?' c! V- T
- *
9 }9 l- d, c& f3 f5 Q! ` - * <code> 4 u/ A$ A0 b: H% W" P
- * //Sample_1
. ^& @, f* _3 v- [; p - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 8 w# U+ g- G1 e
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
% j! R* B; S/ d+ p8 M& c- @1 A - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
& O# l3 k- c; c7 A - * 1 f& f5 g+ m5 n: N( R: @
- * //Sample_2
- \/ G$ z' W% j% ~: ^" g - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
l+ P! X0 l0 M7 } - * </code>
" a: {4 f" A' u - */
$ I/ i" E9 W' U - 2 P( u }8 q/ }# I. K5 ?6 H
- class ImageHash {
$ O. d @: l' J) e$ M& a4 ] -
. I4 p' {: J! Q! A% T) Q$ o: _& M - /**取样倍率 1~10
; z n/ Y$ A1 r& W( I - * @access public
( _$ Z Z1 o- X7 l: i - * @staticvar int 9 x* u2 ?& Z; Y5 d5 ? ?
- * */
/ p. g P: z# D$ P - public static $rate = 2; # x% B! l1 \) u/ P3 ?' Z* T1 t
- % @* N* i& b: F
- /**相似度允许值 0~64
9 s# K+ C& T- _6 A* S$ ` - * @access public
- x) n$ H9 i: N - * @staticvar int - F2 F* ^4 j) c* E2 m5 c
- * */ 7 l0 H' @* p' m: c Y/ \1 |
- public static $similarity = 80; ( v9 m z; [0 Q7 V$ ^) b
- 3 |- w! |: O& [3 r3 c& A# a$ j
- /**图片类型对应的开启函数 # a; N% P9 r. s8 J& B
- * @access private
& M: U7 ^5 O+ q1 L/ ^4 a3 ^) q, R9 Z - * @staticvar string
p, x* A/ j9 e2 G( j - * */
3 S# S: l" M0 j9 Y - private static $_createFunc = array(
% f# l- J2 |, C! g) f2 i - IMAGETYPE_GIF =>'imageCreateFromGIF', 3 p7 i! G& g, z- W. Y7 s) u) F
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
. l# _% m' ~5 S. T - IMAGETYPE_PNG =>'imageCreateFromPNG',
6 O) [* @" a4 Q4 ^6 m6 i5 \0 T - IMAGETYPE_BMP =>'imageCreateFromBMP',
+ r* b8 L+ H; Q w - IMAGETYPE_WBMP =>'imageCreateFromWBMP', _8 J' w9 Z% ?* P
- IMAGETYPE_XBM =>'imageCreateFromXBM', 2 B! T$ ^! o+ T3 J; h# u
- ); 6 m" E& c/ p4 Y% D! y) R
-
2 ~1 B) |/ ^0 W$ \( Q - ! P' k ^3 C/ I5 {' G0 v* R
- /**从文件建立图片
/ H* b. j7 z+ h6 e - * @param string $filePath 文件地址路径
1 ^0 _; a8 S4 x - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ' f R. r& P/ e1 K- e- m
- * */
# d/ ~" `) k2 I$ v - public static function createImage($filePath){
' \0 e: G6 `; R, d - if(!file_exists($filePath)){ return false; } . t! W) b" g( G( |- ^
- 0 R/ C, S0 @! H4 Y! E( U
- /*判断文件类型是否可以开启*/ ! t! W) T3 o7 t* p
- $type = exif_imagetype($filePath);
8 q: z1 D# r4 A v - if(!array_key_exists($type,self::$_createFunc)){ return false; } , ~& t7 Q H9 ?7 _1 {1 F
- 8 S/ o" I3 i$ U0 a8 Q8 L2 ~
- $func = self::$_createFunc[$type];
' G4 \5 V) h3 P: w% r& C' ? z6 A - if(!function_exists($func)){ return false; }
9 O, [- G( h2 } -
* _5 U, O) N0 ^. d5 F! X - return $func($filePath); # F' h3 o R9 l) h
- } 3 W/ f- E Y! w; u
- 5 E: _! w( r* g+ c
-
) Z; x$ m! v& ~& s+ R$ c - /**hash 图片 , _2 b# f+ f; C% X1 y& L* B0 T5 `
- * @param resource $src 图片 resource ID
( k# j7 P7 b8 q6 J# A5 F0 c - * @return string 图片 hash 值,失败则是 false
6 I& g% t4 C5 I) A) N - * */ % M& {% n- D) G" j) X7 T# ]* r0 y
- public static function hashImage($src){
# S& x8 D4 `' ? - if(!$src){ return false; }
* C% {: b+ B) F) g -
2 A3 l; ~6 J6 } ]$ l - /*缩小图片尺寸*/
' Y& w" u! e$ X b% l! S/ d) U0 R - $delta = 8 * self::$rate;
- `2 U+ M! J: v. @% [+ Y - $img = imageCreateTrueColor($delta,$delta);
5 b6 u9 R2 D7 H$ O - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); # g6 v5 G4 M: y
- , a, R. o9 B: F8 b4 g9 v0 o) X
- /*计算图片灰阶值*/ ; ~ U2 ?; ~: T: X1 K( z
- $grayArray = array();
. s+ ^! C; F- z4 q0 W1 k - for ($y=0; $y<$delta; $y++){
" G! p5 j, \' C- q$ A - for ($x=0; $x<$delta; $x++){
( A9 q4 g. x* v8 J2 K* j - $rgb = imagecolorat($img,$x,$y);
9 W3 O, t) G# V& \ - $col = imagecolorsforindex($img, $rgb); : s8 O) [+ p# R Y! ^8 c
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
2 l- y/ V' F) \/ X1 t -
' o# p; n% O- W0 x k. C. w - $grayArray[] = $gray;
5 r9 L2 w) U' W/ v) b8 v* D - }
, u7 j& q6 R: o2 G/ a, O - } - n. U9 W0 [2 e! X5 T/ k1 V
- imagedestroy($img);
4 D2 v# L3 E2 O# v" K - 8 r3 Y1 D6 ^0 s& x
- /*计算所有像素的灰阶平均值*/
1 |8 l6 s. @) e - $average = array_sum($grayArray)/count($grayArray); ) V# j8 {* C* G* E# y
- ) B( f/ p5 V2 q
- /*计算 hash 值*/ # j' F; m/ E4 f' o* u
- $hashStr = ''; , W- K* g( W2 C( L
- foreach ($grayArray as $gray){ 0 ?" L8 [! q2 E% a- w
- $hashStr .= ($gray>=$average) ? '1' : '0'; 0 o0 r, _; e" V z0 Q, _* n
- }
6 [1 U' `: z/ a- L0 R) Z- v; V - 7 z5 M& b% a, i- z; ~& b
- return $hashStr; / i. s; q, H+ K& E" H
- } 8 Z; G7 m# M z9 e+ U
-
7 L. [& e) N8 ~8 M - ' y$ n! e% }& e
- /**hash 图片文件
4 M6 I: j: F# D5 x9 I8 w - * @param string $filePath 文件地址路径 : g* W+ J# m0 s9 g
- * @return string 图片 hash 值,失败则是 false
* [" h; ]7 b8 X9 Z7 D) p - * */
; J5 x* ^6 j8 ^3 j, t - public static function hashImageFile($filePath){
2 k; c( d( g2 k - $src = self::createImage($filePath); 9 Z- g4 E0 s6 g1 `, W
- $hashStr = self::hashImage($src); 9 P+ G Y$ c. ]- I: Y9 V
- imagedestroy($src); 7 ^0 x& c# t4 \
- 6 H8 `; o5 v4 Q g5 s. W) J
- return $hashStr; 5 V6 @, w8 b: _
- } ( Z3 B% l; L0 [3 o' V( ]5 K
-
9 v$ r- d6 ]9 x6 D) f/ Q - - F6 J3 j: r( q4 i
- /**比较两个 hash 值,是不是相似 - q0 Z6 n# `3 x$ e9 Y& J
- * @param string $aHash A图片的 hash 值
4 B# U7 Z5 Z5 V, T: ]$ U - * @param string $bHash B图片的 hash 值
7 \" o. Z$ I |' S% ?9 c$ o - * @return bool 当图片相似则传递 true,否则是 false # }$ P i9 S* [, ~# s9 O: [$ W
- * */ # H9 {( g( ^0 Z
- public static function isHashSimilar($aHash, $bHash){
7 S1 z2 w: S4 l - $aL = strlen($aHash); $bL = strlen($bHash);
0 Z/ T" k. Z* D% g - if ($aL !== $bL){ return false; } $ D% J1 V; n+ A) u" M' R9 x6 e
-
- @% H" p& |/ }% q8 w3 c - /*计算容许落差的数量*/ 1 `7 I8 R5 g0 r8 }# M0 a
- $allowGap = $aL*(100-self::$similarity)/100;
4 ?3 x6 O0 r& X' W: p* [ - " g% R) \. o i5 S! \: |2 G
- /*计算两个 hash 值的汉明距离*/
5 e, A# `+ Y4 f3 W& p - $distance = 0;
- C+ t$ x& [. [( Q - for($i=0; $i<$aL; $i++){ # ~( O1 b% C( C' [/ l1 G. w9 w
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } 3 j4 K( v: y- @# I2 |) T
- } ) S& K- i$ X$ f8 m
-
" F- \, b, g5 K - return ($distance<=$allowGap) ? true : false;
, [* h4 g& i! s$ F3 S - } " R4 [* p& k1 z5 A" Q6 [" D
- : I+ T7 Q& L5 v7 I
-
5 T" J; |. j2 S - /**比较两个图片文件,是不是相似
4 }! ]. H; W1 m& e5 [ - * @param string $aHash A图片的路径
# l5 v) }0 D6 W - * @param string $bHash B图片的路径
' _" f4 Y; s: _1 T - * @return bool 当图片相似则传递 true,否则是 false 1 u" M0 A/ G& {4 I9 D S. M) Q
- * */ , e ^& A" D4 ]/ _# M
- public static function isImageFileSimilar($aPath, $bPath){
9 p, f" M! H6 w5 C+ y, N3 Z% R. W9 ~ - $aHash = ImageHash::hashImageFile($aPath); . p7 e/ O1 a9 J- Q) \/ d5 R& U6 N
- $bHash = ImageHash::hashImageFile($bPath);
! Q; H# m0 t+ e0 S; l - return ImageHash::isHashSimilar($aHash, $bHash); + u( v) I. V5 `
- }
4 \. w* _2 ~8 M$ }' e7 u- I -
( ~: A. l6 h4 l/ K; N( z, H, ]6 W: V - }3 r1 n0 O( j5 T, m. f
复制代码
# ~9 J+ L! s% l$ r; L
" k) q. I: I F- @) s |
|