管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
) [+ t" S/ `. n8 ]" V5 E: j* |$ E k1 {9 U' `& D* V: {3 H( v. L
" O$ o' c) o8 {2 |4 r由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
( d, |8 k2 p5 c9 }3 Q- <?php
3 f1 F3 t9 W ~0 }2 k& t% ] - /**
1 S+ a, N1 U( ?% o - * 图片相似度比较
8 A0 H' B$ ]& n4 f* B - * & b% p2 H- S V6 O" o; m
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 1 E& W( [* f% P5 f9 `. X
- * @author jax.hu
4 k2 B b5 u' \" R; }$ I - *
4 G" r8 C2 B1 D+ Y2 D; ?8 J - * <code>
6 T5 @, C- M8 @8 ? - * //Sample_1 ) R' f4 A1 C7 j+ h, X Y5 O
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); ! F8 t+ n8 E) y4 t$ c8 h
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 5 K! M7 @0 j; R7 V1 Q6 N. x2 H
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 6 [6 D8 y+ g7 u1 ?/ [
- * ) r0 B' @/ t( h$ y+ G+ A, s" p+ X
- * //Sample_2 ; `$ V" [# B$ U2 {
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
! ?9 @# ?1 L6 k+ G - * </code>
1 ~' v; n7 {( y" n - */
; v- N3 Y* m% Q+ N, ^% k - ( F k# R* y' u5 G, z3 |
- class ImageHash { C4 n4 K! Y( X+ g. y$ u. _; }
- 2 k1 S7 o% y5 `5 x- y9 T
- /**取样倍率 1~10
* M# A( o9 u) K3 _2 Q* T - * @access public
5 ]* C1 M* u8 W: @3 p) i - * @staticvar int " P7 y8 L! g# H
- * */ 8 A% m1 \; N, ]+ i. T
- public static $rate = 2; ' s, f/ s1 R2 s, {
-
% A0 p. x9 [% F4 z9 O5 W - /**相似度允许值 0~64
; O: r" d8 h7 ~* i - * @access public
0 S; W. t4 W7 \5 z2 H% _& d9 }: f - * @staticvar int
0 J! Q) Y5 a% ~% n9 p9 \ - * */
$ b8 Y) s! I6 B' q( |; X - public static $similarity = 80;
" E- C3 n3 A3 V8 _" p$ S - }6 q( x5 l8 }* @$ h; O
- /**图片类型对应的开启函数 # {1 H5 i3 @* a: M4 v
- * @access private
7 U% W" |2 l- f" B6 W$ U, a - * @staticvar string ) J7 u* T. S4 e
- * */ & f* Y8 d7 o1 j# L+ _ q3 u( M6 K$ U
- private static $_createFunc = array( 6 y+ t3 c3 `5 d
- IMAGETYPE_GIF =>'imageCreateFromGIF', ' j" q# `6 K6 u1 J
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', 9 @: k. f* ~. H
- IMAGETYPE_PNG =>'imageCreateFromPNG', 3 ^2 z$ T3 w% x' [
- IMAGETYPE_BMP =>'imageCreateFromBMP', * L2 {% }3 z$ S- w
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
: ^* t. V! f) Y |1 O4 | - IMAGETYPE_XBM =>'imageCreateFromXBM',
' _" H' i) ~4 G+ _- B1 ?1 z - ); 5 t+ o& f& q! o5 l
-
4 I; T2 z7 W$ _& K @7 K - 8 `4 M+ s6 M5 E; Q& D, K6 W" v
- /**从文件建立图片 1 ? D' j2 q( {' w: W# f1 o3 {
- * @param string $filePath 文件地址路径 . G8 b' v1 `; |7 Z" Z
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false ) a* w/ l. b; c9 ^0 n6 v4 a1 Y2 p2 u
- * */ ( y0 J1 ?! O7 x
- public static function createImage($filePath){ 7 S" y! _* ^/ u5 {- B+ F' K
- if(!file_exists($filePath)){ return false; } 2 C9 N. G* P: o/ @
- ! {' D+ }8 }, ]( ~( R# m* v
- /*判断文件类型是否可以开启*/
; R8 ]; g7 r' g/ ~ - $type = exif_imagetype($filePath); : F# A. ~% }* d
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
% U% B. N. M2 w( l1 X -
# r$ t3 j) r0 _/ D* k3 O - $func = self::$_createFunc[$type]; & q; K6 E8 |; @- C* m
- if(!function_exists($func)){ return false; }
J* R4 K9 q, Z% r5 |3 u -
9 t; ? e% s1 i" o% U - return $func($filePath);
3 h2 p0 b% y3 c: ~1 z) D6 h: U - }
+ l! \9 ^" s$ m, s2 e - ( l% ~0 D+ p, X+ ^8 u, D( p
-
4 X+ |0 h) l2 @5 [4 z; ~" \! d - /**hash 图片
: i& i8 V2 F2 Y* _! c - * @param resource $src 图片 resource ID / @1 n: C# {& p( ?3 ]2 T: C
- * @return string 图片 hash 值,失败则是 false 3 w% a3 |& d* n' n/ C
- * */ 1 h& \* D: y* }9 {3 N
- public static function hashImage($src){ 5 _. c- O0 [# ~$ e
- if(!$src){ return false; } % K0 H- t5 W9 t6 l2 T
-
% o1 { g, K* i( d2 M, n2 o - /*缩小图片尺寸*/
; j2 a5 `% o6 s; f3 J, F- x ^- h - $delta = 8 * self::$rate; 7 H7 @: G0 t. p
- $img = imageCreateTrueColor($delta,$delta); + S7 @& S7 L2 F+ w) s/ r" U
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
e5 z N K' w9 o. ? -
& j( k: W' _% |( v1 b - /*计算图片灰阶值*/ 4 U% \/ ]; Q% x" k, m$ `
- $grayArray = array();
@9 X4 |4 ^ Y6 p8 Y2 V - for ($y=0; $y<$delta; $y++){
L/ |# y" b8 S. K- |8 e - for ($x=0; $x<$delta; $x++){
2 Q+ g0 E2 {/ T8 e6 u - $rgb = imagecolorat($img,$x,$y);
8 q$ h1 x# Z2 k ~' K4 _: Q - $col = imagecolorsforindex($img, $rgb);
g ?" }6 f9 g3 S' T. F; z - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ! b3 g* l: M2 p* p; `( G
- . v* {$ ~. g# Y$ _+ k
- $grayArray[] = $gray;
5 U% l' ?/ p6 V; z. u+ _5 g - } / P5 X5 G, B# ^! o7 P3 A: x
- }
1 U$ I6 h) `( U! e, V - imagedestroy($img);
1 N+ M5 ?2 f: Z# n% D1 t -
5 |2 W: R1 j/ `3 O - /*计算所有像素的灰阶平均值*/ 0 E5 z1 r9 x8 m! D$ J' Y6 k% @
- $average = array_sum($grayArray)/count($grayArray);
! x; ?) B+ G, } - , C5 p' r s* i
- /*计算 hash 值*/ - ?8 _' D ]9 a" L/ T( g( N: ?6 A
- $hashStr = '';
6 J9 g& g, l8 D4 N0 ]( P0 y. ], D9 T - foreach ($grayArray as $gray){
' ]/ g/ q4 C3 I - $hashStr .= ($gray>=$average) ? '1' : '0'; 2 R7 W" ?+ V) c! P5 } e
- } $ @. G9 d7 L' `8 e9 z6 q8 A
-
! g' ?3 i, E% a/ Z: T - return $hashStr;
9 i, t+ v. j7 a/ v5 p - } & m! ^( _+ L3 x( C1 |/ X' c) ?
- 0 z5 F+ t, Y+ a2 w5 N C
- - M: ~, [+ ~4 v! W% y. G
- /**hash 图片文件
; Z8 d% ?" e$ T0 d# |- l - * @param string $filePath 文件地址路径
" h3 T# O7 W* f: {4 g - * @return string 图片 hash 值,失败则是 false
; B' y+ E6 e: ?3 l+ |/ F - * */ + o3 G% A) p" z% n% h$ b5 g2 p
- public static function hashImageFile($filePath){ ' ~5 e! q$ j- b( t+ l4 {# ~. q
- $src = self::createImage($filePath);
5 {- q5 v1 D5 @ - $hashStr = self::hashImage($src); : f8 p* J# E) `1 I+ X. x/ o* V
- imagedestroy($src);
p$ u6 U0 I8 @ -
3 c$ G7 q6 X$ P - return $hashStr;
. X8 U/ X7 T+ r$ p0 I" g" P4 u" [+ { - } X4 l$ y% r/ G" R/ j3 ? |8 [, ~. }
- ' d/ z; \" O9 ^! D
- 0 z) z" }7 C3 S8 U. q0 @- r
- /**比较两个 hash 值,是不是相似
! [) ~& y, l3 e5 T3 n - * @param string $aHash A图片的 hash 值 6 j3 n4 @- \; _" A
- * @param string $bHash B图片的 hash 值 - x5 |1 B7 i# p7 a
- * @return bool 当图片相似则传递 true,否则是 false 6 _" s/ g# D, a% q+ ~
- * */ 0 _( |7 q* g$ q
- public static function isHashSimilar($aHash, $bHash){
4 {2 J% t, w1 S - $aL = strlen($aHash); $bL = strlen($bHash);
( |3 l. {8 T4 R - if ($aL !== $bL){ return false; } - s5 T! Y B# x/ ?6 h+ v
-
3 V/ u( z. c7 o8 D - /*计算容许落差的数量*/
9 w) X# k9 n4 L0 N7 B5 Z( s8 { - $allowGap = $aL*(100-self::$similarity)/100; ) E, o2 A6 O1 y6 h* i# s
- 3 ]) {. I, c9 x |
- /*计算两个 hash 值的汉明距离*/ + E# ?- ~9 I( x1 P3 p3 N
- $distance = 0;
% N: N, L" g; T1 } - for($i=0; $i<$aL; $i++){
, q3 | C) Z! X2 l6 K1 h. b! J - if ($aHash{$i} !== $bHash{$i}){ $distance++; }
$ Y& u3 |5 O' y+ Z8 O - }
% A; }% b- o. H9 b -
$ ?' v) f8 \' ]$ u8 W+ I! M* X - return ($distance<=$allowGap) ? true : false;
) p& }- N2 l4 A# t - } 7 D# S; S4 o; T' e4 s
- ' C% \; a- c8 o! \- A8 |$ Y8 O" P
- , T0 [7 v+ \, Q
- /**比较两个图片文件,是不是相似
6 r* I/ ?# R8 _! z - * @param string $aHash A图片的路径
8 r# f; |/ L: b/ e3 F9 Z - * @param string $bHash B图片的路径 3 z0 i" M5 g5 W$ t+ l4 W
- * @return bool 当图片相似则传递 true,否则是 false
' L* t: W- p. u' h; Q' k9 f - * */
# B8 `( b( i. p6 c - public static function isImageFileSimilar($aPath, $bPath){
& e) O- t5 f8 k: |. c3 A - $aHash = ImageHash::hashImageFile($aPath);
5 o' M1 I: i* W- [3 Z7 z& o# ]$ F - $bHash = ImageHash::hashImageFile($bPath); * K5 x! |: ?2 \+ Y3 p
- return ImageHash::isHashSimilar($aHash, $bHash); ' A! K* z7 }2 n" v- F4 O
- }
% V2 _1 _9 x9 U! ~' Y -
9 `( Z: I' ^- a& J% D/ A6 a4 l - }0 o9 ?, \$ q* ]+ L
复制代码 $ K# C$ D) d" t/ {# E& W
) O8 e Y4 b6 t. [
|
|