管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
$ b( K C- T6 Z, X( v4 o* {& V: {- ]" O& X. S8 ?. I
$ L# }0 @4 K# C& G9 M( Q# u由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
3 w/ [9 w5 T! H: O6 N9 W! L, ]0 Y8 V- <?php & |& b% s7 X! r' x! x9 ^
- /** , @8 |+ n1 y- p+ m0 g8 q2 T, e' O
- * 图片相似度比较
% U) J. @' w; F3 i+ _ - * ' w" g5 F5 k( @
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
2 V; B+ [" n4 Z4 W0 K - * @author jax.hu % n- w7 A% }: N* a4 L5 H
- * / ~6 H* V {! G% a1 Q1 r
- * <code>
0 o K+ I( _& z |; d% X - * //Sample_1 $ s* m! k7 j0 j8 H- n
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 4 M! z& H, R4 D7 _5 ^' w
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
. d8 L5 e7 K. H7 K. P; z4 P/ J - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
" b/ O4 g! `+ ^% y - *
" }7 a) G/ W6 C! P# c0 l. \% X) l - * //Sample_2 " y) x$ |1 N g1 ~( S3 O+ c/ l
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 0 q. j+ ~$ o8 k* U! `7 _( z- H7 e& r
- * </code> # r7 F. |) x |' x8 n& ~
- */
. V* ~+ m3 x3 ^8 Q" g9 a - : l7 G4 [- \9 g& L
- class ImageHash {
! H- j/ i% N. c- Y- y6 ~ -
0 [ q; n! p( `$ w - /**取样倍率 1~10 - s ]& w. y F5 l
- * @access public
( c( a2 C( { E$ }7 T5 s! {8 C - * @staticvar int * y1 Z' P* Z9 h2 N* H
- * */
# Y2 ]" W0 H4 w4 o - public static $rate = 2; % s# z8 K3 P/ o$ e7 c4 M
-
' Y, ^1 |$ _/ B+ _6 y; D - /**相似度允许值 0~64 8 A# o6 q' A% C v0 Y
- * @access public , @, u5 N$ ~) T: Y
- * @staticvar int
3 S2 v" j h$ J4 j - * */
! N# g0 E4 l3 H4 D" F- Q* S - public static $similarity = 80;
" H1 s0 q- b" h4 G7 N! |: o' ^ - / m; t+ \2 T. _" k; h( l
- /**图片类型对应的开启函数
# y* S' r, j& @! T6 _2 x+ @2 U - * @access private 2 W5 v" H, c; x2 I$ i u8 A) Z
- * @staticvar string
) P% w' ]: l6 Y7 ? - * */
" n3 _5 t0 i. a, s3 y - private static $_createFunc = array( $ ^" ?! d' m3 e% f
- IMAGETYPE_GIF =>'imageCreateFromGIF',
' ^9 B9 l& k9 F: m1 Z - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
% N o# Q" r$ Q- W* S, Y# ? - IMAGETYPE_PNG =>'imageCreateFromPNG', - N. b" ]; d/ n I. s3 {" G
- IMAGETYPE_BMP =>'imageCreateFromBMP', " E7 A5 ~- R5 o) G( `+ X
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
7 R0 N0 A% ~3 R! V' r0 Q0 z - IMAGETYPE_XBM =>'imageCreateFromXBM', 8 j$ L. S. M- s
- ); ) r9 R8 J$ Q7 L: [
-
) r' H* L( z+ i, z9 @% R/ X8 Z -
% ^6 [8 p5 g' x3 k5 p. h* o - /**从文件建立图片 + {/ w( l! Q& M6 I9 ~
- * @param string $filePath 文件地址路径 . L+ C5 j" P C+ x: u
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 5 `6 f( [, _7 j0 R8 r# A
- * */ & D: l8 C3 E8 }3 q, s2 o
- public static function createImage($filePath){
- `2 p! m6 E* n1 k9 k - if(!file_exists($filePath)){ return false; }
+ Y3 \2 v# t+ C& e/ d: u( x -
/ u2 J6 f0 y8 Z L5 t1 g - /*判断文件类型是否可以开启*/
" \ s# F: S& `9 U- f - $type = exif_imagetype($filePath);
# ^7 S% a& E9 Z0 d% `% X; L0 N - if(!array_key_exists($type,self::$_createFunc)){ return false; } + y- R$ @% Y' o2 f, a' S
- ; x+ E1 k: T& }) ~) a! ~
- $func = self::$_createFunc[$type];
, Y! f8 g7 e, b/ ]2 j - if(!function_exists($func)){ return false; }
* E# G5 ~8 |; g9 S! k -
# V. |( s3 W) { w0 w+ g- k' q - return $func($filePath); - O2 h) X0 z& c+ h9 i
- }
2 \" }* k; _# ^( } - ! ?9 ~: [2 b Z- ]- e' q; J
- : t8 ?0 Q0 l8 N$ {) M4 y6 W
- /**hash 图片 * w/ @# |4 ?, r! f3 R' ~
- * @param resource $src 图片 resource ID
4 G) q0 N$ K) z/ L( g - * @return string 图片 hash 值,失败则是 false
: K6 m7 ]+ G& E8 F - * */
; B; R# {) @/ R8 g* l$ B' w - public static function hashImage($src){
1 e b1 e% x2 x+ F - if(!$src){ return false; }
1 z: v4 C) f9 g5 g - * V1 u& J. L* |& R' l8 `
- /*缩小图片尺寸*/ 5 ]" w6 Q$ z' [6 c B! L1 c2 V/ O7 D
- $delta = 8 * self::$rate; 7 ?9 r. z8 A$ t6 I1 E4 v, U$ X: V
- $img = imageCreateTrueColor($delta,$delta);
0 V4 D4 [) [% U2 }6 x Z! B - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); . y: i% q% G2 X6 N- @7 @% Y
-
) q7 }( m! S3 w S- q+ I" ` - /*计算图片灰阶值*/
5 p6 Z+ g$ h2 Z2 E; W3 N0 t - $grayArray = array();
3 w4 w4 s7 G+ H$ C3 p - for ($y=0; $y<$delta; $y++){
. H0 i. ^, I( s: { - for ($x=0; $x<$delta; $x++){
0 [; o. s0 G2 i" y. k- [1 Z# E - $rgb = imagecolorat($img,$x,$y); 3 h5 A' l1 D; I
- $col = imagecolorsforindex($img, $rgb);
0 t! [0 b. Y% F3 ]) T. Y- M% ~ - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
. Y. S( \! V" J' Y2 O0 ?& y - , m9 ~$ G! [. A& R
- $grayArray[] = $gray; / g, _8 L' w& S" {7 h U$ K
- }
$ Z+ ~ A- n3 w/ B" a6 L7 H& F - }
% C; t7 p$ N! }: T: [ - imagedestroy($img);
- s5 L5 }& A3 c1 g: ` -
( s$ T6 O+ ~( m* F - /*计算所有像素的灰阶平均值*/ * |" L1 ~. M5 w$ a/ Z1 ]- x! w
- $average = array_sum($grayArray)/count($grayArray);
' z' H/ X7 C. q8 x( A1 f - 9 R6 C1 ~( _2 k1 H* f+ ?6 g. q
- /*计算 hash 值*/ " L! _. D9 W8 x! r1 b2 B
- $hashStr = ''; / c5 D p3 N5 {7 V8 L6 |
- foreach ($grayArray as $gray){ 1 E2 d5 S) Q( }8 v" K
- $hashStr .= ($gray>=$average) ? '1' : '0'; : Y0 ], ?8 ~, {8 j7 J& n" O
- } % F( v# u% A6 @+ f$ `& R7 r4 {
- # A: K- \. Z( \9 O; A9 B6 d# m
- return $hashStr; ( j! N y% Q9 k. Q( P
- }
4 v7 a; P5 P+ X$ G# E) G, ? - % ~6 [% l6 u0 @; P# l' r
- . t0 y% u; n# f9 @4 {. x0 ? R9 X
- /**hash 图片文件
% O ~" x+ B' F1 b/ [' u- G - * @param string $filePath 文件地址路径 % w5 z9 X( ?* Q9 [/ X( y2 H
- * @return string 图片 hash 值,失败则是 false 2 f2 ] I$ [; E! y. b$ ?* z
- * */
8 S: H0 B, x( w; `. o - public static function hashImageFile($filePath){
; h8 F# e: [" I" G/ s9 M1 x - $src = self::createImage($filePath); 2 B1 ]9 C3 h, }, x% t* X' O
- $hashStr = self::hashImage($src); / g& t# E' e2 r) ^
- imagedestroy($src); ) T& |* W: ?7 P4 {2 \
-
- k; k% j6 j: ?7 A9 V) M& W9 y* e - return $hashStr; / ^' K9 k5 c/ Q+ _+ K2 P
- }
. f5 s) ~# o" t7 b: z - + N$ Z# c, i' j3 R5 i9 T
- + K, \7 ` k n) X |
- /**比较两个 hash 值,是不是相似 0 z7 A9 f% U- l
- * @param string $aHash A图片的 hash 值 4 o) M/ j3 w4 h- ~6 H! r
- * @param string $bHash B图片的 hash 值 & i$ q8 ?# _/ L! `4 z: F+ k p
- * @return bool 当图片相似则传递 true,否则是 false
! Q! J- ]/ s- V% C - * */
8 R, p% u4 c4 ] - public static function isHashSimilar($aHash, $bHash){
! P$ c' E/ h" p* C7 \ - $aL = strlen($aHash); $bL = strlen($bHash);
; Z2 |5 ~" {# P( C1 w: R' P - if ($aL !== $bL){ return false; }
& T, b, K9 i9 ?, i" Q* ~$ U0 ^: Z h" o -
0 Y% a( }# }1 n - /*计算容许落差的数量*/ # O( e. r8 |+ \0 y3 p
- $allowGap = $aL*(100-self::$similarity)/100; 0 k+ s& G) Y/ @. V9 L0 g5 S7 W
- ! q0 C( v) s5 q3 t8 c/ A+ U% ~4 M z
- /*计算两个 hash 值的汉明距离*/ ) Z) w- m7 X* ]
- $distance = 0; " X6 h6 h/ Y3 O# }
- for($i=0; $i<$aL; $i++){
4 [; f8 R; B) u0 ~% z - if ($aHash{$i} !== $bHash{$i}){ $distance++; } / q) U( z& G5 X {, K0 o5 z+ u
- } / R5 z; O+ @0 ]/ M" Y! c4 q
-
& X! _8 Z; h8 o - return ($distance<=$allowGap) ? true : false; $ }4 V* r0 p# ^# l: a
- }
0 V: _2 B' x) b$ f- ^ -
$ X4 N- l; S" Z) E$ j2 V9 l -
- w5 ]: ?6 h3 e P - /**比较两个图片文件,是不是相似
0 j4 W4 l8 E7 b) p0 t - * @param string $aHash A图片的路径 8 Q1 n' r. r( q @* @3 C; ]
- * @param string $bHash B图片的路径
1 I1 h& {; B( |8 e- x6 W# ~ - * @return bool 当图片相似则传递 true,否则是 false , p$ `7 z- U, _; C# x5 M1 {
- * */
: v, |$ B' L. Z! q - public static function isImageFileSimilar($aPath, $bPath){
% m4 a% b A* U" e. \' r) [5 h - $aHash = ImageHash::hashImageFile($aPath);
9 m N( c0 b3 M - $bHash = ImageHash::hashImageFile($bPath);
9 @# N6 h- |1 s3 ]3 `. X - return ImageHash::isHashSimilar($aHash, $bHash);
. |; u9 W8 @. Q1 |4 ~% U$ Q - }
# T U/ ^8 a4 s5 _- M -
: Q- L6 X$ U9 w1 @* a - }
# ?" a1 i; j0 H! {
复制代码
l9 t$ t8 {5 Z) B4 p+ i1 d$ Y& [6 G# L) s, l
|
|