管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
$ l+ a- \: w/ M2 v3 W- F+ _( | R& u* F$ E1 m9 _
4 ?5 n, B% S. L+ H9 m
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
r3 L0 E* |5 p- <?php 4 K! X5 ?) a" H$ Q* N* p
- /** $ a7 U- Q# N. S3 N" x& c
- * 图片相似度比较
" p$ W7 p. _$ i' s - *
* c1 }! F- l) F6 y - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 1 W% y* v3 `* ~3 r% c6 C- I* V
- * @author jax.hu
0 ^$ N# a6 l/ `/ M% D% c - *
% d; W! p# {4 i" y- U# t* b - * <code> % n; y$ q2 q* d0 E7 k7 Q* F. {0 P
- * //Sample_1 - s! o; @) Y- p7 Q9 w( I) U8 \
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 4 ^: g' d0 u' F2 R7 B
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); " r7 N) w3 y; O
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
; G( x) M R' x. z' g7 u8 _$ b3 ? - *
( o- a. G7 P& ]) H$ f; ~( T - * //Sample_2
G& s0 N0 V, F0 I1 U( N+ v - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); |% c H% d: G$ u
- * </code> ! R; l9 v5 ?2 ?) q- `; g N
- */ % D, h T( ?, T4 e8 ^+ t
- 6 ~, |6 T% R. |+ ~$ x
- class ImageHash {
" h6 C- B1 {$ F M/ O V7 ] x# K; M - * |$ A0 C5 S; h/ q- X& P
- /**取样倍率 1~10 . x6 q* t4 l9 v' e) O# j
- * @access public ; b7 A* Y/ u8 D. t+ ]" v1 p
- * @staticvar int
$ B1 c8 s! p4 Q \$ C% u; m - * */ 8 _2 C8 [8 Q5 N; S: J
- public static $rate = 2; e8 p1 S+ T( X1 n9 u
-
8 W. M+ e( s/ J1 c. n - /**相似度允许值 0~64 ( z8 b6 _2 e3 n4 Y( k( X
- * @access public + t$ L6 C$ z9 \. q# l0 J
- * @staticvar int 6 F; g: m8 v' D) w& }: U% m
- * */ @& }: i* a+ _* M. T
- public static $similarity = 80; 3 I; _) [: [$ j3 I( A
-
4 v5 L5 ?& d! I - /**图片类型对应的开启函数
; v4 J5 ]6 o& j, O$ O - * @access private ; c% f! O2 A' d7 M
- * @staticvar string 7 Q# J4 Z# a% l0 f2 H/ N
- * */
* P! `. X" K9 }# t" c- i, U9 Q - private static $_createFunc = array(
- o5 _# V) Y2 L3 J) B& ]/ ] - IMAGETYPE_GIF =>'imageCreateFromGIF', - }. F7 b/ O) F- `: o
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', % t) n: B B% E" c- I) G
- IMAGETYPE_PNG =>'imageCreateFromPNG', 4 Q) G$ h& V. F
- IMAGETYPE_BMP =>'imageCreateFromBMP', . e# ^9 e1 t8 _5 O0 g- t9 d
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
# ^- |. o& d! \" n - IMAGETYPE_XBM =>'imageCreateFromXBM', 8 L7 m: f. l5 T6 b
- );
- k. r9 B6 `' l( e -
% G) m* d+ s! x3 F3 | - ) N3 n. G. U# Z* ^2 @& Q
- /**从文件建立图片 , _3 f2 V1 p, Q0 f
- * @param string $filePath 文件地址路径 , z5 T6 n+ T. ?2 C3 t3 @0 A
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ( U9 r! e: _: Q6 Y* V x( y) u
- * */ " x/ R0 {6 H2 [: I0 x1 [( ?/ L
- public static function createImage($filePath){
! Z& [% i3 @# Z5 a - if(!file_exists($filePath)){ return false; }
5 o) F* c) w, G -
. o7 X7 d, r7 ^ - /*判断文件类型是否可以开启*/ $ r6 M. A% V( @1 w! U% x
- $type = exif_imagetype($filePath);
! D t* m) Z$ [% J - if(!array_key_exists($type,self::$_createFunc)){ return false; }
; z% ]& ^* S: O6 [/ p5 d6 f - " C0 [' G9 E2 y7 Z' E
- $func = self::$_createFunc[$type];
7 s7 @* ?2 w: ]# W8 ` - if(!function_exists($func)){ return false; }
4 F' a! [1 w s; U: k( l" u -
) h3 F' L" k: a7 _ - return $func($filePath); 2 Y' \/ J. M* S5 d; D
- } $ m# X& i3 p! z
-
1 p+ U# {5 C: \7 _ -
Y( _& w7 o: H- F, u k# N7 b - /**hash 图片 i6 H! U' B% T) F5 }' B5 }9 _+ S4 \
- * @param resource $src 图片 resource ID X' e+ G" {! I7 i
- * @return string 图片 hash 值,失败则是 false
" o- m X7 ?. V9 Y6 c% a6 [0 D, q - * */ ; [; R' @7 C% f/ v) t1 f
- public static function hashImage($src){
& n2 G" o) A; R" S; f! S- m - if(!$src){ return false; } 6 r! t" k$ G! R5 s2 l
-
7 h. B3 z4 p j- b - /*缩小图片尺寸*/ e% Z' H3 M+ D7 z/ o% R
- $delta = 8 * self::$rate; * M; I- ?( O7 H' O# f- i
- $img = imageCreateTrueColor($delta,$delta);
" u" r7 x# V) S - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
1 S3 n. S/ E0 {" U% L! N; Z( Q+ I -
- r5 l# X) j/ o7 r' m8 ]0 O. H - /*计算图片灰阶值*/
4 d3 i( O" l& O3 k# z8 h0 D2 { - $grayArray = array(); 9 [8 p& Z& c( a9 c& H- j# H
- for ($y=0; $y<$delta; $y++){ , K! H9 n4 g7 \6 ^8 \. C% G
- for ($x=0; $x<$delta; $x++){
; O& `# m1 a& i - $rgb = imagecolorat($img,$x,$y); n8 V% S3 c6 ]6 W, J
- $col = imagecolorsforindex($img, $rgb); + e# F4 k! ^( a- V- i
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; . q0 [* w9 M8 M# A5 G9 \
- 2 F2 q1 D: s( Y' O: `) G# R
- $grayArray[] = $gray;
5 o8 F$ A, f0 B2 {1 L0 A. [, R2 |( I' d - }
! [1 j+ _# u# D+ ^. U* j - } $ e: _' |, S) a# i6 |% ^& ~
- imagedestroy($img); $ r& s4 y; ^3 F# x1 M5 V
-
9 [! M, K) @- a/ r% l5 m3 S - /*计算所有像素的灰阶平均值*/ 4 Q% C: Q7 d! I4 a O8 t2 E
- $average = array_sum($grayArray)/count($grayArray);
) b, ~6 Q0 i6 m0 X) P2 s0 { - 0 F" R! u/ i' E
- /*计算 hash 值*/
c2 c& i8 L' M6 O8 C! p, S& a - $hashStr = '';
& R8 Q: I, _% E* x+ D - foreach ($grayArray as $gray){
1 P- }- D- g2 _& _: o5 I; k3 l9 X% f - $hashStr .= ($gray>=$average) ? '1' : '0'; ' b6 t- c/ M" }
- }
' Y1 o: k2 D9 `, n, O - - z0 ]* b( z8 t
- return $hashStr; / {1 g% @4 D2 d
- }
2 n- v% i P( X! u/ i( `& z) k -
) {3 h' b# i" Y M9 R9 n: W ` - 3 w, p) L7 P! { O. |* B
- /**hash 图片文件 - _ j# g0 v$ N* N7 \& _
- * @param string $filePath 文件地址路径 @4 v7 c+ ~' a1 u2 J0 A, J
- * @return string 图片 hash 值,失败则是 false
4 R. [, p. k- Z, g - * */
! W% ~5 u( M# S' |! p$ w) i2 l( s - public static function hashImageFile($filePath){
5 L/ T& l2 k7 }# v t - $src = self::createImage($filePath); , }: \" S; v5 z6 Y! p2 n
- $hashStr = self::hashImage($src); ; N% K1 J y. a3 p
- imagedestroy($src);
9 l$ B1 N: x; D4 a1 Q" S1 m) @$ W - / I7 a. n1 p9 V& v1 O
- return $hashStr; 3 {, J/ z% D" G- [
- } 4 ^ x+ |, |& H
-
" m. r+ h: E8 I! s( p1 \ - @3 U1 @; h. I' O8 [; {9 Y# P- s
- /**比较两个 hash 值,是不是相似 # N7 M4 }$ |% h4 B' R. H
- * @param string $aHash A图片的 hash 值
3 c6 {, z$ x4 X2 K6 a$ V3 A) F - * @param string $bHash B图片的 hash 值 : p1 n0 K. v! I' w# o
- * @return bool 当图片相似则传递 true,否则是 false + \5 W! z. E0 V2 S
- * */ 5 ^3 U0 ^! O8 u; ]! h: }" X
- public static function isHashSimilar($aHash, $bHash){ ) g+ I: E2 Y4 f
- $aL = strlen($aHash); $bL = strlen($bHash);
6 f# x% l0 _9 [! Q - if ($aL !== $bL){ return false; }
$ |$ I" B8 y# |- k: m/ u5 j9 @ - : g" Q5 J8 k. ~
- /*计算容许落差的数量*/ 1 d1 n5 \! h E% z' ^# Q* m, H
- $allowGap = $aL*(100-self::$similarity)/100; $ e+ _+ C- N) h% o8 W' W+ C1 R
- 3 a! u) N% t1 o ~% e
- /*计算两个 hash 值的汉明距离*/ - D8 N# c1 c1 _; e+ ~8 @
- $distance = 0;
% n* B/ B* g* G7 `4 t5 W0 s% B - for($i=0; $i<$aL; $i++){ : y, W4 F1 u9 o% |# F
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } + {( F. x* c5 e4 y5 L. j
- } 8 \+ L% L$ s4 {& G5 k2 R) t
-
; w. ?( R- S$ h" F d# j: e+ \+ ` - return ($distance<=$allowGap) ? true : false; ; F) v/ t+ }2 p
- }
! |8 |( k) y- G: B; o/ c - ! ~ V0 }5 q7 C1 f
-
2 z2 q o$ P- y6 w$ c" X2 b - /**比较两个图片文件,是不是相似
7 ~1 k& e6 o [+ d - * @param string $aHash A图片的路径 4 w2 z- p( n, _0 e
- * @param string $bHash B图片的路径 ) Q/ c9 q" \. N& k: X3 u
- * @return bool 当图片相似则传递 true,否则是 false
/ k! U/ s( x$ i. l. \& w/ U' ^" C; P - * */ - S: {6 j% e+ s. X: J
- public static function isImageFileSimilar($aPath, $bPath){ 4 l$ C7 U" u. ^) _2 e
- $aHash = ImageHash::hashImageFile($aPath); 1 z$ M9 }' ^2 n0 U1 J
- $bHash = ImageHash::hashImageFile($bPath); * p$ [( W( `2 K- M: J( J: b( ~2 h+ u
- return ImageHash::isHashSimilar($aHash, $bHash);
. R7 U: O( E& r - }
, p2 y$ g! i7 ^: G - 9 a% V# g3 H Z; L6 h% h
- }
, O$ G4 i; s' z& v$ e
复制代码 ; I- {1 r0 @5 J+ v; f5 i! `0 t
/ t. v* [0 i+ D3 t( x" g6 J; Z/ c |
|