管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
- h' L+ C2 A, U! ?1 K. y" z# ^0 N" `
v% F% e0 o& }4 q由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。+ c$ }6 t/ c) n1 g
- <?php
% f5 J2 K, {& G8 J, w" H' g5 D - /**
& v/ n: u U9 m9 s - * 图片相似度比较 0 \: U2 D9 e1 L/ s9 S, y
- * & g5 X$ I, U0 C) O
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; % }2 Y# ]2 B& x7 h4 B: W
- * @author jax.hu 2 L$ r w: ` Y6 d5 s1 k$ p* X& R
- * # j; A' P8 C% T) ?5 ^
- * <code>
% i& i$ J) g3 k5 Q0 D; w- _+ ? - * //Sample_1 . {+ {5 P$ m# W2 ^* P( ^
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
+ H0 C2 i M$ e/ r9 s - * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
& w5 Z& }3 _; j+ G8 \" u" f- G3 ] - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
! H1 G: x$ l& \6 |) @5 M - * 1 S/ R" a! C8 p9 E$ z- h: L3 t" n
- * //Sample_2
/ ~; f$ R7 c2 J# A. L) v! y - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
8 q; m& S" f! O! w4 D# p - * </code>
3 ~. M5 L6 S* V - */
! \9 Q, G! k5 z - . t" h6 P: j b) v- M, D4 \$ m9 g
- class ImageHash { ' c( K2 U$ w2 r6 d* j
-
/ ~9 F. G& R: V- y - /**取样倍率 1~10
q% b/ W: \2 C9 r: h - * @access public
" A" P( U" V- O$ G6 m8 l - * @staticvar int
1 N4 i' B( e5 K; W! x' Q( y4 k - * */
0 T% ~/ ^: E! F0 T1 P& G; h( } - public static $rate = 2; 6 R: @9 }, e U, k7 _ N% t
- & l! e& r; E; ]" B2 @0 u
- /**相似度允许值 0~64
0 d6 e+ V! E6 f& e+ p - * @access public ' Q* Y7 D, D+ s0 X9 a; M) n3 g
- * @staticvar int
) \" x- e% A g+ k# J7 p9 v9 ~2 l) z - * */
/ I* w' @8 `0 ]. O- y. C2 f0 c - public static $similarity = 80; - n7 S& A7 A- o' N$ U, [
- # R3 \8 L% _3 n) j
- /**图片类型对应的开启函数
1 {# O" b3 W7 l" Z" @# J - * @access private , [% b# w$ r1 c; ^& ]
- * @staticvar string
" V X f+ M! s6 B; r5 H* ^ - * */ 5 L' l3 J" U& C* q1 |) o( T+ z% D
- private static $_createFunc = array(
4 [9 J; X1 r# B P: p7 k& h/ z - IMAGETYPE_GIF =>'imageCreateFromGIF', ! m$ e! b7 y: q0 M4 T7 d
- IMAGETYPE_JPEG =>'imageCreateFromJPEG', % P. | Y7 {. `$ z2 F6 T, f1 l
- IMAGETYPE_PNG =>'imageCreateFromPNG', ! J5 t1 V/ r, @" i; @ R/ A
- IMAGETYPE_BMP =>'imageCreateFromBMP',
% U }7 n/ F7 M/ x1 I - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
/ r& v: C+ ^+ ~* a" V& n: d+ d3 Y% ? - IMAGETYPE_XBM =>'imageCreateFromXBM', " v, Y+ k# V% U& K1 |0 d
- ); $ P9 T0 \( J, K6 m9 j- C1 e6 B% d
-
: c" q1 O7 T; T+ @* L5 H -
7 M( `# P# ~- M# W, ]4 x - /**从文件建立图片
6 [' i! T$ ]) a s- C - * @param string $filePath 文件地址路径 2 C) k) ^$ s2 R# C1 {
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false # f1 u5 ?5 I; {
- * */ . u7 I1 s: {# V
- public static function createImage($filePath){ 9 Q, F$ }9 w* W% {+ c. \
- if(!file_exists($filePath)){ return false; }
* y4 d1 L" H# }/ m - 9 m' W2 E+ I1 R7 N( w
- /*判断文件类型是否可以开启*/ . _3 ?7 J+ t/ w5 e; {" `
- $type = exif_imagetype($filePath);
9 n4 h$ ?% J5 g9 q; J - if(!array_key_exists($type,self::$_createFunc)){ return false; }
) u$ o/ v0 e. ^4 S0 ~ -
1 s9 w6 s% s7 T8 N. v - $func = self::$_createFunc[$type]; - S: h# a* v% [' z; H
- if(!function_exists($func)){ return false; } B' B" Z$ e$ Y4 b" r7 B' D
- $ m K" l1 O9 D0 I+ ]# |# x0 y
- return $func($filePath); * Q8 E- w+ a3 d$ }
- } 4 H! V9 l" {6 K" @: s! l
-
. A) d: @# a8 y2 t- C( g3 B - ; m( ?8 z9 Z+ @- w4 o* ~
- /**hash 图片
$ J* p7 ^1 f0 ]" o - * @param resource $src 图片 resource ID
- U Q) |' F4 j: c - * @return string 图片 hash 值,失败则是 false
4 I/ A( s# {& J( @& c - * */ 8 I% w6 [1 h9 Y* }3 G
- public static function hashImage($src){
( e- X& b) a7 W& j9 N) A- P - if(!$src){ return false; }
2 q1 h7 C! A; q; j, [+ N% ~ -
' n0 R& B$ X, ~$ p$ K - /*缩小图片尺寸*/ , d7 X, i) F: X! C- o% u9 y+ w
- $delta = 8 * self::$rate; , v( h p6 ^: ^& `% B, P1 ]
- $img = imageCreateTrueColor($delta,$delta);
7 w9 \) p& u/ J( }- b6 x - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
% t- m- k& {& d( w/ x+ p -
( r( O% C+ s, z; Y3 x; S) J0 g - /*计算图片灰阶值*/ 8 ~) h# T) u) }5 r7 k6 G, f
- $grayArray = array(); - q! ?& P- Y/ C( s% e6 _
- for ($y=0; $y<$delta; $y++){
( U8 `& C2 H' ]. Y9 M3 ?5 Y1 d9 H - for ($x=0; $x<$delta; $x++){
; t. y) z' `" E# T( V' z9 t. S - $rgb = imagecolorat($img,$x,$y);
- l4 G6 D0 R2 f$ z - $col = imagecolorsforindex($img, $rgb);
, p1 T4 S v+ i2 j# e - $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
: ]' g+ Y0 z5 L) U f S -
, L' b6 x! K6 H8 a$ j4 I e& n - $grayArray[] = $gray; ; H+ G) A2 ]4 h/ k- T
- }
/ D% O) G6 j* C: M - } * W2 R# [2 |5 B' k+ g" m" m$ x
- imagedestroy($img); ' o, D& c* A! f9 [
-
/ K5 j. R T3 L8 m - /*计算所有像素的灰阶平均值*/
4 n# M& I6 O+ k0 ]+ g+ U, B - $average = array_sum($grayArray)/count($grayArray);
1 v( }; s: T5 G -
- t* P. w/ K5 ?0 [2 o! ~* K - /*计算 hash 值*/ ) G2 k% L4 d; y- Q/ @
- $hashStr = '';
& {9 y/ b% S) m- L9 `: s8 t - foreach ($grayArray as $gray){ 9 O4 b! Z5 F4 q
- $hashStr .= ($gray>=$average) ? '1' : '0';
7 p6 B# v D% s" z# v - }
7 L& v" J7 Z8 } - ) J& s E/ D4 ?8 v8 N
- return $hashStr; . C1 z: k. `' s2 ?
- } ) ~# q4 Q3 z* y) E; L9 ?
- % G; Q# H. \) B! Z* T
- / B: A$ g8 y% O2 D; v2 e
- /**hash 图片文件
: n/ Y+ Q# Y; Z7 u/ b% E4 d4 g9 b - * @param string $filePath 文件地址路径 / H* j' N# E$ F: ?6 h0 ^
- * @return string 图片 hash 值,失败则是 false ! x F/ J3 I- u s# b3 j9 p' T6 I1 W
- * */ `/ X, Z, g6 F( p/ J8 X0 T; d
- public static function hashImageFile($filePath){
% w2 x8 f! z3 v( ? s: Z - $src = self::createImage($filePath); / u4 @7 k$ N" v" _+ w
- $hashStr = self::hashImage($src); 3 D* N z% \9 ?; j
- imagedestroy($src); ) e* T9 G# m2 @+ [8 p# k
-
( i2 p5 O* F+ `; y( b$ m4 x - return $hashStr; 5 c$ T7 {' q( u( B# t. w) q# G- Z
- }
! Y6 X- b$ U+ I7 L- r) b1 o - % a* h4 O7 g" u/ x5 w9 S! t
-
3 l- X) I4 H& f - /**比较两个 hash 值,是不是相似 : `" o% z5 b& a- X
- * @param string $aHash A图片的 hash 值
5 O: `6 n( j6 N( P0 M - * @param string $bHash B图片的 hash 值
- U/ V u7 |8 Q2 f - * @return bool 当图片相似则传递 true,否则是 false 7 G- A7 n" s1 R9 ^' l5 p; D
- * */ ' Q/ G; I, m% k2 N8 I+ g
- public static function isHashSimilar($aHash, $bHash){ / H! D/ F; v6 o' a+ \
- $aL = strlen($aHash); $bL = strlen($bHash); # h+ i n7 |$ h. Q0 R2 [* M! \
- if ($aL !== $bL){ return false; } ) }) L7 _! A/ s
-
5 T* ?9 c: q2 B - /*计算容许落差的数量*/
) r8 N. k5 ]) T& _. J; @- ?1 [ - $allowGap = $aL*(100-self::$similarity)/100; : r) O& M7 ` M, I, n
- 3 }1 p6 I6 u2 F5 W# [
- /*计算两个 hash 值的汉明距离*/ & T/ \( Y, r8 d1 y! A7 B8 s, B
- $distance = 0; + d; K, m! w6 i# ]
- for($i=0; $i<$aL; $i++){ . a: R+ Y) o6 G- B6 b) E j
- if ($aHash{$i} !== $bHash{$i}){ $distance++; } % R8 [- z+ h0 x+ K2 n1 h, V
- }
0 ?5 b* |. k* W! C X1 e9 t9 W -
" A: r4 A7 a, m( n - return ($distance<=$allowGap) ? true : false;
$ k! s8 X7 V/ K0 _4 X - } ; m, P6 j$ _0 k! r
- 7 @. s% z: E% F3 h6 W- ^
-
5 K# S" X7 W) @9 F - /**比较两个图片文件,是不是相似 / R: k; Y2 a% |8 b+ n+ C
- * @param string $aHash A图片的路径 ' C Y% u. ?, q2 K2 k: Z; Z
- * @param string $bHash B图片的路径 ' s/ i% g1 {; | K6 X" t. O
- * @return bool 当图片相似则传递 true,否则是 false
. W" C: A0 W7 W" C, T0 R1 E4 _ - * */
9 j4 M* J: b/ v+ b4 f - public static function isImageFileSimilar($aPath, $bPath){ 5 E) ^& |; j: B ]/ Y/ u
- $aHash = ImageHash::hashImageFile($aPath); # {* y [' z/ j# T3 c7 k
- $bHash = ImageHash::hashImageFile($bPath); + C- G x1 p0 n8 L& I
- return ImageHash::isHashSimilar($aHash, $bHash); ' |% A% T# z) c9 w+ N U
- } 7 @: F' ~3 E' b: ? n- z
-
1 R* @% W$ o* u. j+ D - }
# ?8 u4 `9 v0 ]4 B' d) Z& U- t
复制代码
! s8 `/ q% h- x- N; x/ S
3 B, {0 u# O3 d/ | |
|