管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图$ R. k% \& i* R9 c# F" c8 w# D* a
" y9 W( l6 G. K) ~
: R0 m% \7 ?* I由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
( K3 w2 r O8 d: x- <?php # I' @9 M# N9 @+ v0 J6 P7 D0 [
- /** 7 q0 U; L! F3 r/ W: e3 z
- * 图片相似度比较 ! ^; f. o" X) w, D
- * w# q' ^" _3 N7 U" C# x" d" u
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
( U! l0 H* y4 e" c( ~ - * @author jax.hu $ s! i2 F- s* {+ Y
- *
5 N: f: ~/ b6 h - * <code> 1 y5 S, \) Y0 ?8 P: s: j3 Z
- * //Sample_1
0 E) j: N# K+ e - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); - Z8 ~* j0 y; [* K/ g* e. S
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
( A" u1 Y- L: U - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
n7 {+ R5 k; z. o# P4 _* R5 j - * 2 z8 G9 K0 u6 e% V. ]
- * //Sample_2 / s( o2 x4 I; t( {- _+ h
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
4 q) T0 @. E- Z3 r; d- I - * </code> : s N6 Y+ ` k0 _
- */ - P% b8 i% y8 a0 X
- # J5 t8 F+ n5 [" Z( Q) k J4 [
- class ImageHash {
1 _6 U$ Q' @0 Y% [' ?' z% ] -
. H# W3 p9 o) Y9 s - /**取样倍率 1~10
. j9 I) p! j) ]. _, {0 T- b; f( Z; x - * @access public 6 I) R) K/ ?) U4 f7 R
- * @staticvar int % Y9 n7 o3 B& J& L
- * */ - o7 W6 C0 f. e- F8 s, K5 A+ ?
- public static $rate = 2; ! x5 p7 X c! M3 a5 `; Y
- " ~. ]% f' K! ^
- /**相似度允许值 0~64 ' p0 [5 W* ?9 l9 G8 M" U9 ^
- * @access public
/ l2 X- D( L7 a: E - * @staticvar int ; u7 I- ]. `. X- a* S n
- * */
! n9 w8 W, K. W: ^1 \& m9 Q - public static $similarity = 80;
+ k6 x# p) [2 i- I. Z - ) E, i6 w g9 z2 n N
- /**图片类型对应的开启函数 3 ]# s2 I" M7 @' Z$ D' H$ Y
- * @access private
: l8 f6 t7 K0 { - * @staticvar string
' w& g+ _; f0 f' a - * */
6 D( C9 t$ U; t' Z) I0 s! ~ - private static $_createFunc = array( - q& V: J! L% M. M
- IMAGETYPE_GIF =>'imageCreateFromGIF',
]8 O# K8 ]0 l* ~; h M/ I2 S - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 2 @7 T1 h- n0 I! I
- IMAGETYPE_PNG =>'imageCreateFromPNG',
8 f. j& }& D a9 l - IMAGETYPE_BMP =>'imageCreateFromBMP',
0 u* U5 {4 L2 z1 t7 Y1 B) f# t! { - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
, C$ \- |% s4 p! ^ - IMAGETYPE_XBM =>'imageCreateFromXBM',
! t R) q5 E: n - );
4 i2 s* L$ A7 k - ) _9 @+ P) K$ L; O1 B" O0 Z" z4 z- W
- ; Q& S9 r2 ? E; h1 ?% `
- /**从文件建立图片 + G' S ]: [* t' v
- * @param string $filePath 文件地址路径
+ x2 E( ^' A; a, O/ f4 y- T* A - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false % C- }: H0 _( o' _- V
- * */
! c, G! c2 G( G9 l. [& H' { - public static function createImage($filePath){ 3 {' e( N) S7 S5 Q
- if(!file_exists($filePath)){ return false; } 7 K) d J. C! c2 s* i+ h
- ! s& M: {5 L* ]; O0 B5 }
- /*判断文件类型是否可以开启*/ 3 ?) h: y, E0 y4 B3 L/ R
- $type = exif_imagetype($filePath); 9 S2 y2 Z7 L1 N3 [3 D+ [
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
0 i) z' g6 l4 H, o3 D - . r' ~3 F/ x) f. M5 f. P& |9 M/ Z
- $func = self::$_createFunc[$type]; s/ N& {5 o9 ^
- if(!function_exists($func)){ return false; } 9 J! k/ ? x5 W6 G
-
, h! F' S1 o3 {; h' ]7 }" Q! d. Z- F - return $func($filePath); / o3 w0 f" r! x3 ^& M
- } , M" ?" ~- x0 o5 t3 _- g
- * M6 t& ~1 [5 |% H
-
3 k6 ~) O) K4 q- L* h; V - /**hash 图片 + |& F) e) ?1 L, v3 \
- * @param resource $src 图片 resource ID
- {: R- s" m7 J - * @return string 图片 hash 值,失败则是 false . Q3 ?1 \, c& g8 E8 d
- * */ 8 n: D5 F- ?' h, v6 j
- public static function hashImage($src){ $ Z! a: `+ J( |" ]& s
- if(!$src){ return false; } $ l. S- m8 N9 X' c- @
-
0 R: C$ i4 j* D/ X - /*缩小图片尺寸*/
; c; s4 a: i$ Z$ B$ k - $delta = 8 * self::$rate; - C, g0 Z& T) ^/ ^! m" X. ^: |
- $img = imageCreateTrueColor($delta,$delta); , v) W7 Z, u+ d! I: \, ]2 r' E
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); , K4 b: A: d, r7 m& |4 P) x
-
+ h9 p- {! l# \9 q% s6 n8 b2 \ - /*计算图片灰阶值*/
$ V2 B: {' Q, @* z6 {" N - $grayArray = array();
+ c5 d5 Q- C) z0 b# s - for ($y=0; $y<$delta; $y++){
/ {! O% x, h1 F+ V. D8 c9 Y - for ($x=0; $x<$delta; $x++){
2 d# |% E G; | - $rgb = imagecolorat($img,$x,$y);
" Z8 H+ X ~* C! e. V/ A" B7 k - $col = imagecolorsforindex($img, $rgb); + B" A& o( X. Q* ]' G- h
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 0 g2 T! S8 j' a) u. P
-
0 ]0 C$ |% z4 a! \/ X - $grayArray[] = $gray;
, q% H) ?2 m% C9 C - }
5 ~% r2 u) p9 P - }
1 |' ` r: e0 k. p- Z0 ^ - imagedestroy($img);
' J4 `/ O, w6 J- x$ b( B+ I - 1 c4 `: q) r" k% H1 [, Y: k
- /*计算所有像素的灰阶平均值*/
- E3 t k% m7 l( d - $average = array_sum($grayArray)/count($grayArray);
. x' x4 \& x- J5 L - ; Z) k* a8 Q' r6 u
- /*计算 hash 值*/
" R9 Y* \) v9 J0 ? P" d' D - $hashStr = '';
5 ]. u) ~" r1 K - foreach ($grayArray as $gray){
0 V% ?) }% A: }. o- \1 _- C8 m - $hashStr .= ($gray>=$average) ? '1' : '0'; : F% O# r/ w9 n" z( i5 a
- } 2 k% Q4 v3 V$ T! r+ S- M$ e2 E
- 7 M+ z. v$ o a1 S" Z& i
- return $hashStr;
* w8 p; Q7 ?0 e( i. M( ]0 f6 q' M - }
- u. z/ T; @- k5 j/ u - 5 r$ m5 P! s8 f& Z
-
/ o) t; N( Y3 e* E; I1 J - /**hash 图片文件
1 H0 b A- g+ x( W! f% z. ^ - * @param string $filePath 文件地址路径
4 ~5 K- _4 j9 ~- b - * @return string 图片 hash 值,失败则是 false # C& L& G8 M9 I' i' b
- * */
D3 T/ @, z, y0 u9 X - public static function hashImageFile($filePath){
* e* d; z. B2 d7 e4 O3 A7 |, V - $src = self::createImage($filePath); 4 e- n1 a% M( E9 }3 T6 a. U7 r
- $hashStr = self::hashImage($src);
2 i5 E- K3 _7 i! O/ L1 }2 a# L - imagedestroy($src);
/ o# C! V4 p" B5 F7 }6 x4 S. O - + u2 N+ y2 H v$ c" N9 w0 Y: q2 ~
- return $hashStr; 4 F' K2 a; s/ h7 k5 @6 ^! q5 R
- }
' P7 w6 ^1 H5 h7 r, | - - U+ G- Z4 @: a2 V0 y
-
9 x: W z# D0 O. [* Q) B0 B - /**比较两个 hash 值,是不是相似
# b; y! {& \/ J' t9 I. `) q - * @param string $aHash A图片的 hash 值
) I5 J6 |6 d7 [7 @8 w s0 w* V - * @param string $bHash B图片的 hash 值
0 P, L3 m0 ?/ ~& D" G - * @return bool 当图片相似则传递 true,否则是 false
! `: J0 K3 c0 o0 A9 K - * */
0 f7 P6 }! `) ~5 y- X) A2 G - public static function isHashSimilar($aHash, $bHash){
- a' k" q# M8 [2 H% _. M - $aL = strlen($aHash); $bL = strlen($bHash); 6 \1 L, f# w+ [* u8 N3 D% i, @
- if ($aL !== $bL){ return false; }
% K1 y4 k$ u* X' V! ~ -
, d' U3 B w" I - /*计算容许落差的数量*/
& V w) J! v) A" e2 l; o/ s - $allowGap = $aL*(100-self::$similarity)/100; p* l& U) ~, X+ D) x: L
- 0 d; m4 Z0 v$ U. e, f* t4 q8 c
- /*计算两个 hash 值的汉明距离*/ * \* ^: p4 V9 H7 b9 G
- $distance = 0; : d/ U5 t L, B, D% Z
- for($i=0; $i<$aL; $i++){ 4 k/ C6 @( \, C+ k: a
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
" [- J9 F: V, n* ~ - } p8 J1 o& z' a( A2 f, n# ]
-
- U! J8 E4 U3 [9 y2 Q* [: [: a - return ($distance<=$allowGap) ? true : false;
8 h2 w0 T0 w; F - }
9 Y* o; X% |4 D; H$ ]1 l1 X7 \' o -
3 x/ X1 T; z4 l1 w -
9 [9 P, V. t8 q5 U - /**比较两个图片文件,是不是相似
9 v7 O9 O4 b% E" W - * @param string $aHash A图片的路径 : H8 C( B8 T; w- }1 V+ q# s
- * @param string $bHash B图片的路径
z9 _+ Q0 c8 w - * @return bool 当图片相似则传递 true,否则是 false
: H) o& Q0 z: X# e# C: s. h' Z# X- S - * */ ' T( Y# }; L5 Q* k: Z
- public static function isImageFileSimilar($aPath, $bPath){ $ M. i. o& `: a4 i) e! O7 t- s
- $aHash = ImageHash::hashImageFile($aPath);
% _' [( J6 Z, o+ l - $bHash = ImageHash::hashImageFile($bPath);
# V' `8 z$ T. E! F - return ImageHash::isHashSimilar($aHash, $bHash); 9 z a; O% {! x2 p
- } . X3 U* A2 c2 q, c5 s& ~! k
-
1 _7 Z# c# A# `+ C8 z8 }# k4 h - }
5 t* B# x! R% F6 ~/ R9 y) g; N
复制代码
( U* N" W; A# v/ Y* K3 ^1 T* Q7 {1 N
|
|