管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图$ k2 k% H8 w1 |: {
2 D G# f0 R' W$ h6 h$ D0 ^
# u' q: C' `% {2 x& }8 P L0 b: D6 |( G由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。4 ^& J- f' m0 o/ @. m
- <?php
! l* M: o# n) G# g& C/ T/ m: `6 F - /**
9 D8 n2 `% B; s# \9 [( X - * 图片相似度比较 & A: T8 Q5 l; C/ T4 h* A/ @3 Y
- *
" L# t6 }, m0 H! D! ^% n& T P - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
% A. g: m3 n2 ]5 h4 V' Q - * @author jax.hu
. F, V, w0 j% O% }: C5 q/ O - *
: s0 ~7 l, R, J7 b - * <code> " O, W2 W% |5 G& q' T
- * //Sample_1 & O# A# X& |8 q% a. o( n
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
% N- e3 |* B4 i5 U8 p! ?: i" ]& x - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 7 d3 s% ^! j: C' ^0 h F! H
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 6 {$ Y z4 \/ y8 q! n9 V# |4 s
- * 4 V: y5 l# a4 r& v0 Q9 X5 x* j
- * //Sample_2 8 d4 x+ r$ Y& u7 _% C
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
$ i p4 Y. C# t, _. Q - * </code> 4 J3 ~0 _5 y2 ?5 l+ I" V
- */ 2 t8 Z; Z _' A" z' K! A9 O
-
# Z; {- b: q1 B2 ^& e" Z - class ImageHash {
n: ^, {/ Q, {+ n& r - * e' _. B! W, f* I) I( J$ a
- /**取样倍率 1~10
6 i) L; F0 w/ b- [/ M4 i - * @access public 7 R, [$ w3 H0 A# G/ X3 ]
- * @staticvar int 4 l4 T" ?1 J) k5 \1 _
- * */
a2 @2 U6 v; J - public static $rate = 2;
% A- S ^/ F5 j; v# i -
8 d* P n, o3 X# C: f2 v; z - /**相似度允许值 0~64
! u# o b A+ M# n+ `/ s1 { - * @access public 0 ^9 l u+ P ?+ h+ s. G8 U0 Y
- * @staticvar int / I+ P* D( b6 Q( p d
- * */ ( S) v- C* d, x2 g7 o
- public static $similarity = 80;
1 p. C2 u5 B' |* h - 6 i; a, y0 G% U3 |
- /**图片类型对应的开启函数 ) X: v; W7 o* P% y! D) i, D" j9 c
- * @access private
+ F9 w4 t1 z% L/ n* a( @, [ - * @staticvar string 4 y' Y, p( B4 q$ |5 d
- * */
' k3 S( ~3 T- b% d6 o; S% M - private static $_createFunc = array(
/ Z3 X* S5 G1 o: W3 f+ x. f - IMAGETYPE_GIF =>'imageCreateFromGIF',
: O0 `9 ]) ^# M+ P& q7 I. g - IMAGETYPE_JPEG =>'imageCreateFromJPEG', 8 J7 p# `, M; k E9 U+ U+ O4 G
- IMAGETYPE_PNG =>'imageCreateFromPNG',
' }$ D) q6 w$ O% L% |* K1 U - IMAGETYPE_BMP =>'imageCreateFromBMP',
; O1 a6 N+ A0 Y2 W9 C' m - IMAGETYPE_WBMP =>'imageCreateFromWBMP', K3 k( X! n0 p$ b% @
- IMAGETYPE_XBM =>'imageCreateFromXBM', ' G: y2 B; o2 T0 f8 T
- );
8 Z# r% S; y8 A* k [" Q( M% ] -
q: X3 B+ U7 G, q) W* ] ` -
3 ~+ v. U& _. E/ e3 T) W - /**从文件建立图片 " x' ]! f1 s# \* {; u
- * @param string $filePath 文件地址路径 9 G% u" d6 W: [: S1 `
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false , ^& K4 q2 N4 S H
- * */
% G: Y# E5 v. x3 x5 \ Y - public static function createImage($filePath){
4 P' H/ E v- n$ C7 U - if(!file_exists($filePath)){ return false; } " M) w, l+ h1 w, E5 h! i5 g
- % g+ i! i" b- I0 O) E" T7 x
- /*判断文件类型是否可以开启*/
2 l0 I' S+ A g; ]7 \ - $type = exif_imagetype($filePath);
' o+ ]8 k# ~: q! E4 r - if(!array_key_exists($type,self::$_createFunc)){ return false; } : s; r1 {6 C P2 m
-
+ \9 ]! ^* Z* H7 P; t! T/ R; B/ A - $func = self::$_createFunc[$type]; % i4 Y6 D( I) l$ G
- if(!function_exists($func)){ return false; } % G" m$ \: g' W/ Y# `3 `8 t% q
-
; v! ^% M2 @1 E) ]- T - return $func($filePath); 3 n9 }, L0 t; w3 G( U# z6 j) {8 \
- }
+ R+ h+ _4 n( y W+ {0 W: e/ K- L -
9 ], e$ ~0 H4 E7 {! X - 1 f! h) }3 Z$ d9 v9 p; _8 N
- /**hash 图片
$ n) W% k! a# U" R0 K. B$ v' j% F, w - * @param resource $src 图片 resource ID
& @# L5 X! ^2 o - * @return string 图片 hash 值,失败则是 false
' }! t( \2 _2 P3 \5 A/ E - * */ ! J; P U& l/ y! F5 ?9 m2 m
- public static function hashImage($src){
; i. b. M$ {0 ` - if(!$src){ return false; }
- G3 A+ m3 p( W' M( ~ - ( N7 U1 o. S9 p% R' I- ?, R
- /*缩小图片尺寸*/
" A; f. r% ^) U7 I3 P - $delta = 8 * self::$rate;
7 X9 S4 i" k( h3 | - $img = imageCreateTrueColor($delta,$delta); 4 t# g+ N+ a+ ~2 p
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 4 V' `3 X5 h u
-
' D" ~9 ?' h2 V6 Q9 ^) N7 T - /*计算图片灰阶值*/
$ [ f4 c) Y0 K7 D: | |4 ~ - $grayArray = array();
) J5 q+ s" b# P0 m+ r; A - for ($y=0; $y<$delta; $y++){ 3 L9 e1 U3 q' h8 c9 h
- for ($x=0; $x<$delta; $x++){ 0 F0 k# [9 J/ D% p8 o1 L' d
- $rgb = imagecolorat($img,$x,$y);
" O1 E( u( D E, ]0 w - $col = imagecolorsforindex($img, $rgb); 8 y* b5 }+ U+ k( @' M3 M; n5 z
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
1 g* _! u# v0 E+ x) T - 3 u- t* y1 j* }0 l% G7 V+ o
- $grayArray[] = $gray; 5 N2 L- c, K2 E1 {2 C
- } : v W1 B5 W% I. ]4 w
- } ( I3 c- n9 T4 X2 h( @- N! z! B& w& c" E
- imagedestroy($img); + {1 y* b- m: [, |
- 9 d/ l2 j5 b4 `1 C
- /*计算所有像素的灰阶平均值*/
* U0 p1 F# t) W/ Q: e - $average = array_sum($grayArray)/count($grayArray);
8 o7 N) I( j0 `8 O -
& {( C" t6 J3 r/ u; p - /*计算 hash 值*/
4 L$ T) P9 K& p - $hashStr = ''; 4 R% s% l5 G7 I: Z; ~
- foreach ($grayArray as $gray){ 2 a7 \8 d, I! T1 v C: B
- $hashStr .= ($gray>=$average) ? '1' : '0'; ! i9 t% e. q% X! b# R5 Z, f) J
- } - v/ j+ o, n& R' b
-
8 ?+ ^# e& f/ V" c1 R9 k7 \# G - return $hashStr;
4 h" \7 z* S5 x - }
0 n0 A, _) O7 ?( x7 {( T7 L; r - 7 L: i4 u( B; L
-
2 u+ y2 P# t3 T( ` - /**hash 图片文件
' }" ?" {2 `1 N4 c" \ - * @param string $filePath 文件地址路径 2 Y8 Q7 \; w5 D C' i
- * @return string 图片 hash 值,失败则是 false % G4 V, ^ r( y8 c7 W
- * */
5 [9 S7 T8 N/ k; | - public static function hashImageFile($filePath){ 7 P: Y z# ]) R+ A1 q- F0 y
- $src = self::createImage($filePath); . M! y- Y' U2 }. g7 ]6 p- l
- $hashStr = self::hashImage($src);
( w9 G5 o$ `, s1 d - imagedestroy($src); * ]- U) t3 O% K
-
' _1 }. ~' l" @' a - return $hashStr; ' u& R- M; L/ m, `' w" `% L: _! ]
- }
( w" ~% Q& [: U p - 3 K) x1 m3 u5 S( P) N) G- |' e
-
, {9 t. |( y o5 i9 ` - /**比较两个 hash 值,是不是相似
. i* g$ [! b- u - * @param string $aHash A图片的 hash 值
/ t. Q2 d5 H- G9 D/ S* ?: n - * @param string $bHash B图片的 hash 值 % u/ t3 X( F% u2 R9 m2 j' U
- * @return bool 当图片相似则传递 true,否则是 false ' x Q$ p. y4 W
- * */ 7 L; y* u* n8 r' ~
- public static function isHashSimilar($aHash, $bHash){ / D) E1 P# \6 n3 M' \" }% o
- $aL = strlen($aHash); $bL = strlen($bHash); $ l% c) x& y: S$ K4 r
- if ($aL !== $bL){ return false; }
. w. O0 E3 A1 U5 S* a -
/ A+ M L7 l: K( }9 B' Z/ y. W6 \ - /*计算容许落差的数量*/ + ^. n' l ]- g3 y/ T
- $allowGap = $aL*(100-self::$similarity)/100; 3 Q3 Y! L9 i8 P+ h9 a. d( D; }2 x
- 2 I9 }: u! w0 [/ g
- /*计算两个 hash 值的汉明距离*/
1 N( m1 N0 s4 e; y- H - $distance = 0;
0 m7 K) m; J' u" t, n( V - for($i=0; $i<$aL; $i++){
" E* s! ?7 C' D4 _& V! F - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
& [+ A7 z% s3 D$ D P8 d$ D% q - }
0 c7 D* f/ P6 `: _, K `- e - + n4 G* U- S2 x5 e) M
- return ($distance<=$allowGap) ? true : false;
- u6 i( |, `4 r* I - }
* [+ o/ N, x( q+ W8 t! F' M' {( J - 5 C( C! |* g" ?3 ]) p
-
5 [* {( t- C: S6 g& j - /**比较两个图片文件,是不是相似 * R; T2 v+ _$ m- ^1 Y
- * @param string $aHash A图片的路径
. `7 z. S5 }: \9 A W! w" l - * @param string $bHash B图片的路径 - D0 T5 h$ y- y
- * @return bool 当图片相似则传递 true,否则是 false
1 d/ f$ b) b3 G/ l, P h; s6 J - * */
* q, v' l/ O% b2 d' C! g2 m% n - public static function isImageFileSimilar($aPath, $bPath){
. z/ ]' W3 g" A1 E2 d9 o - $aHash = ImageHash::hashImageFile($aPath);
1 u5 d3 n" F: F( E3 |2 }) n) Y - $bHash = ImageHash::hashImageFile($bPath); 9 \7 @3 P; u8 p$ \+ S0 G
- return ImageHash::isHashSimilar($aHash, $bHash); i) I- _1 C& Q" L
- }
' O% o7 @0 Y/ \; H s% c+ w7 e. D7 C -
7 E) X! c% s3 @ - }1 r* P$ }( G, e/ p# h
复制代码
/ [6 L: D6 X( T) f+ v( a: d
' w# y( z2 }3 @4 X |
|