管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图6 G# @$ ~ t( G4 T, V
3 N! u; m5 T, s6 D" F
R. Y3 U1 A( [0 v/ P* W) o由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
$ O/ s9 d( ~9 @. n3 K- <?php
; Q% w* M& i& W9 [6 P# S - /**
k- [# f% v8 }0 S# O8 D - * 图片相似度比较 " e9 }" t0 k$ B' B0 o* J& Y Q
- * ! E+ X. `* ^$ ?
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp;
! W+ D& e# D5 \- E) } - * @author jax.hu
: D7 s; o: [% p+ } - *
5 x7 P# O/ _- K4 H1 R6 |$ { - * <code>
( Y( B+ r c9 V5 i z - * //Sample_1 9 j' z% e9 m2 p
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
' L9 |' |: b: y# Q: k - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); q# k9 j3 z6 l* |9 {
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash)); $ F; O0 K3 n8 m7 G# d# Q* ^* ~, ]" N
- * / q* Z0 [" I K3 n' D
- * //Sample_2 " w, e/ p6 q- B* v
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
7 } a& @, S# o) Q- {: ~# n - * </code>
5 I }1 R1 M, L - */ : o: b! ~; z0 I; J
- 6 ?- Y7 p& x' a5 I: N) m
- class ImageHash {
4 X$ z2 V, Q3 s8 P: C4 y( o5 N -
1 F( A" c1 x2 j - /**取样倍率 1~10 1 S* z" P3 P" l0 X' m0 g; a* N
- * @access public ( d) }5 t0 h# H' y
- * @staticvar int
" a, W3 l, ^6 z7 `/ S - * */
7 P# P5 A1 O' J* Y - public static $rate = 2; $ w d' F( L, z6 S% {; C4 z
- , D8 H. e. c* W5 Y) H
- /**相似度允许值 0~64
* w z* i; w! o7 [ - * @access public
- ~3 _+ |" o' m: M: T8 v0 o' _: w - * @staticvar int $ M. }- R- J9 |0 }9 y8 j$ D
- * */
0 W2 o7 |5 z1 t; R% L! ^ - public static $similarity = 80; ( y- u' x8 g" \6 N+ y& V
- # s5 O2 p3 I7 y! S& m8 f
- /**图片类型对应的开启函数 ( H5 d$ k- f/ o5 o. ^2 m1 S6 ]5 _
- * @access private
+ K7 |( x0 B$ c: t) J - * @staticvar string ) W2 T+ N. }2 _( J6 b) ?
- * */
9 D/ k% ~3 x3 G) w- F8 ? - private static $_createFunc = array(
5 R$ [/ j# H% [/ ]9 [+ ~) Q - IMAGETYPE_GIF =>'imageCreateFromGIF',
! V6 w+ j0 m8 s7 W# R, w - IMAGETYPE_JPEG =>'imageCreateFromJPEG', % H6 w/ t5 f6 ?1 \! P
- IMAGETYPE_PNG =>'imageCreateFromPNG',
- R J% V# D. o8 _: R4 X* I- R - IMAGETYPE_BMP =>'imageCreateFromBMP',
# d6 n; S, B5 n5 v8 ^/ p# ` - IMAGETYPE_WBMP =>'imageCreateFromWBMP', 9 u& z" J/ J6 a& N$ C
- IMAGETYPE_XBM =>'imageCreateFromXBM',
n! h- i0 F- R* d - );
4 F/ E- n# }- S8 q/ N9 D - 4 G2 y# A7 z# i% ^
-
1 i9 n6 ~9 g% A6 B1 L - /**从文件建立图片 , [" r3 @. @& f) a" \* ?
- * @param string $filePath 文件地址路径
! m7 v1 f8 P4 ]: o8 F; Z - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 0 h& I4 W6 h6 j' i! v
- * */
' U9 o% B/ `! ~ b" g z5 y+ t - public static function createImage($filePath){
9 T, C! \5 f- y+ e( n - if(!file_exists($filePath)){ return false; } ) b- T% D$ h f0 E h- i, k
- 2 ]( j. S! G, i! }
- /*判断文件类型是否可以开启*/
6 d( |/ [$ x3 ~5 v t+ d! e0 k - $type = exif_imagetype($filePath); # ^+ `4 X' n0 T8 O/ y
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
% J! R9 }) e0 K0 I4 R- f - " s; t" J- H; [
- $func = self::$_createFunc[$type];
( T# j$ N$ b( n' g - if(!function_exists($func)){ return false; } 0 \0 f/ g+ m& s4 |6 ?3 U7 o
-
+ j, m0 c! o- m; V1 @4 l - return $func($filePath); ) V0 A: ~5 Q. [, d# o
- }
) Z5 H1 {+ f/ V0 v# n7 o - ! h7 b7 ~; ^ G' @1 e( i
-
& G8 e9 ]+ I6 ]8 a - /**hash 图片
. R" t, o/ G/ u8 Q" q - * @param resource $src 图片 resource ID : _8 Y( ^2 ]5 y
- * @return string 图片 hash 值,失败则是 false , {" a3 P; c7 O1 e$ z
- * */
' ` l7 X3 s# @4 R - public static function hashImage($src){ 8 p! H2 b+ f$ A- `
- if(!$src){ return false; } ) N( J' b" J+ e
-
0 U7 ]# Z9 R# T1 d% r8 v3 f - /*缩小图片尺寸*/ . z* t/ G! }9 W% z# s2 \" _
- $delta = 8 * self::$rate; ; ]$ w+ ]/ C1 w$ o8 T( Z( }, t
- $img = imageCreateTrueColor($delta,$delta); ( g, Z* b! c* y) Y9 d. o
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
) ~$ n: x2 n) {4 N2 u - ; d/ P- w8 k2 K
- /*计算图片灰阶值*/
$ R0 E( L: A( Y. k - $grayArray = array();
" Y3 O' Q [" J - for ($y=0; $y<$delta; $y++){
. D5 v \# t' D$ |8 `5 e# N" C - for ($x=0; $x<$delta; $x++){
9 V6 ~' E- W4 ] N. Z+ a% q$ o - $rgb = imagecolorat($img,$x,$y);
: A( U% m% ^1 u& A - $col = imagecolorsforindex($img, $rgb); $ u& h2 Q2 g3 J
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
. X( b3 I' q6 Y B( k -
& x5 W6 X' Q( c4 i* C2 E - $grayArray[] = $gray; / n+ a( H1 i( Q, c1 V2 }
- }
, {9 `& \* X' E7 @% u6 }9 J: N - }
& _; k! G' c. A5 Z+ b( Z - imagedestroy($img);
v' [/ T: A3 B) {3 H) a4 i! W -
e+ W- @' K3 u2 | - /*计算所有像素的灰阶平均值*/ ) h5 h7 \9 r1 r% Z3 ~# v
- $average = array_sum($grayArray)/count($grayArray);
; U2 E' |1 o1 w( }% u - 1 }3 L% Q3 Q1 c, x% s- R5 U
- /*计算 hash 值*/
; W( N2 c K" A3 v - $hashStr = ''; ; F2 s; |! b0 H
- foreach ($grayArray as $gray){ # ?( d: O! i& ]! r1 Q+ S4 Q: T1 ^
- $hashStr .= ($gray>=$average) ? '1' : '0'; p B3 \. O/ I6 E+ V
- }
* s$ J t: U0 r5 W# p: p1 m) E -
3 K L: d3 t7 e0 {% u - return $hashStr; ( M3 r" q* d& p: n) q v/ Y
- }
% Y) y9 _& y4 ~# _/ B2 @9 ?$ W -
' z! W% U- G; ~1 Q% Z - # N& i4 P' j, o
- /**hash 图片文件
' i' r% W9 y% M _2 B$ ?, R - * @param string $filePath 文件地址路径 " y: l) q. R1 j! L6 s% ~
- * @return string 图片 hash 值,失败则是 false ) o& C1 h+ T4 o( i- B$ H6 O b
- * */
) E4 c2 g/ W/ A - public static function hashImageFile($filePath){ ?3 J# r0 e& C% _4 V
- $src = self::createImage($filePath); * y( \0 O2 O0 j
- $hashStr = self::hashImage($src); + ^0 c# r% I ]' _5 j
- imagedestroy($src); * N: u' _! l$ f! D) A! }
- & K5 O- m' q) C2 n
- return $hashStr;
$ w3 e( D2 O- V - } 5 {! O! t) t- G! {; S
-
1 w; p l4 r9 D -
/ p2 X* O- w r" y$ ? - /**比较两个 hash 值,是不是相似 , }% q# f9 t0 v% ]0 L- Y
- * @param string $aHash A图片的 hash 值
0 C5 r! n, L! d Q W - * @param string $bHash B图片的 hash 值 0 I+ M9 u. S" Q: { {& }2 p
- * @return bool 当图片相似则传递 true,否则是 false ! L, ~: ~$ P5 {& q
- * */ 1 \% _1 `( _0 h t8 U# d
- public static function isHashSimilar($aHash, $bHash){ $ Q W& _: f0 w
- $aL = strlen($aHash); $bL = strlen($bHash);
: _# q, v6 ~$ N - if ($aL !== $bL){ return false; } / F6 n0 L; M. k3 x* u7 m
- % P* Y1 m8 K; R* i! |
- /*计算容许落差的数量*/
& d1 { @ V4 F" D - $allowGap = $aL*(100-self::$similarity)/100; V+ f+ U# `3 k8 y$ J+ y
- - x3 x* J; B2 N1 f2 Y& Z
- /*计算两个 hash 值的汉明距离*/ 3 k! Y: k6 c% _9 |# [7 l
- $distance = 0; 7 r, l9 z5 f2 f; n1 }$ `1 w
- for($i=0; $i<$aL; $i++){
2 Y- e! D- {$ R' P+ e) O: L - if ($aHash{$i} !== $bHash{$i}){ $distance++; } ( ^ h/ }! C* i4 y. ~8 ]) ~: s
- }
6 r4 ?: J, k: V! A/ f" k - + I; k0 s8 [9 U( l
- return ($distance<=$allowGap) ? true : false;
; u3 f" T, x2 J& z4 V/ K - } 7 |' I$ g7 B3 @& f" b
- 5 W0 A$ Y. G7 a1 r( M0 U
- 7 R# J- t2 B8 _6 U/ v
- /**比较两个图片文件,是不是相似 . {0 N+ q9 t6 }. I( g% g
- * @param string $aHash A图片的路径
' l$ n% _* H4 u& ]2 w - * @param string $bHash B图片的路径
4 f) ^" N6 s* G3 X | - * @return bool 当图片相似则传递 true,否则是 false # @0 `6 y" o9 J5 n- S. ^, `" {
- * */
/ P9 A2 \' z5 a) e. B# E+ U5 P! R ] - public static function isImageFileSimilar($aPath, $bPath){
4 k( k: O }5 q# \# W! w' n - $aHash = ImageHash::hashImageFile($aPath); e# ~0 u5 R* v( R4 T5 S
- $bHash = ImageHash::hashImageFile($bPath); ' O, v" T# ]+ Q& \/ q1 H7 H( j- D
- return ImageHash::isHashSimilar($aHash, $bHash);
8 A! K# i0 z+ m* }4 a% ] - } " p [* S* D" G* ]# j
-
) l, h) L7 _& v r' H1 _ - }
- ~6 U* D0 S! c5 u0 x; H" K% B7 s
复制代码 P5 u+ I$ `0 I' j7 A
5 ?4 H4 x1 B9 s# a; X+ o/ |* i
|
|