管理员
![Rank: 64](template/yeei_dream1/css/yeei//star_level3.gif) ![Rank: 64](template/yeei_dream1/css/yeei//star_level3.gif) ![Rank: 64](template/yeei_dream1/css/yeei//star_level3.gif) ![Rank: 64](template/yeei_dream1/css/yeei//star_level3.gif)
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图/ j( ^+ Y8 K- b+ F5 ] C- j J$ `
- z1 V6 ?! X6 B2 s. H
* t. {5 H Q/ }/ c
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。
3 R7 X' q6 Q, R' E9 j6 I- <?php
3 V6 X$ |: f5 m# _ - /**
: I1 D3 ] T, C. z: o. p - * 图片相似度比较 4 _7 c+ ^ p q
- * & F6 X0 ^: x; _7 i' B- I4 }/ f: u& U
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ) ?; V8 O8 }- x9 e
- * @author jax.hu / i* j% u7 T0 k$ C% v
- *
6 T$ T. {/ f/ `. A( ^' `( V - * <code>
! ?5 g* Q( O, H3 d8 A! f - * //Sample_1 . Y( {) g! z3 F% a" i9 _
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg');
+ r/ s7 M4 h# y+ _ - * $bHash = ImageHash::hashImageFile('wsz.12.jpg'); - Q; r+ B4 E5 N/ y3 Q
- * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
) W: C+ R) M) E" i( u7 x& R - * 3 v7 D( A; U; d" j2 | B2 C* M
- * //Sample_2
. N' |+ B* S& J - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
# c1 z5 l2 W* d( g" |: W/ ^ - * </code>
( H/ L8 }) F0 N, H' B - */
( A; q! n; ~ j0 V6 y - - B4 n; ?: m( Q1 d/ z' W
- class ImageHash { : y1 S0 W# W: V1 J
-
& |# T( Z$ Z0 q( x& Y2 B - /**取样倍率 1~10 ( J$ ]' \4 c" h' h6 B$ A: S
- * @access public
( e7 D- Q3 [: T2 L( |# Q - * @staticvar int
* b- u+ i& V& ]" n1 y+ q - * */
- W" [! }9 J+ w) [6 H - public static $rate = 2; 9 `. l) W6 B3 C, A
-
1 V% i* H+ f2 T - /**相似度允许值 0~64 ) D; h" W# v' k5 y
- * @access public 6 R: n/ o0 {5 `& S3 i
- * @staticvar int
, J6 a8 W9 e+ } - * */
1 p4 q7 j! o; o - public static $similarity = 80;
; b9 i! J: _5 H$ u) j - 4 d8 e) E& F/ a; x+ y
- /**图片类型对应的开启函数 : W) N$ G; f: [) T& t
- * @access private 3 T ~1 W# n8 O- y
- * @staticvar string
8 F6 w4 u% W4 a4 P4 d$ r - * */ 5 F0 p7 g4 N" o. B5 `
- private static $_createFunc = array( 8 S k1 K3 U9 b9 Y9 a
- IMAGETYPE_GIF =>'imageCreateFromGIF',
$ A1 a& S8 @* b8 C; x) v* ^ - IMAGETYPE_JPEG =>'imageCreateFromJPEG',
( J- V& M! e( U4 } - IMAGETYPE_PNG =>'imageCreateFromPNG', 6 }: {2 D4 x& G: p. J: O9 o5 r3 }
- IMAGETYPE_BMP =>'imageCreateFromBMP', 0 y0 \7 R0 O6 {# C% h3 E* c
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
' Y2 v( w% T t" l: B - IMAGETYPE_XBM =>'imageCreateFromXBM',
; ]" J: g, F `2 w5 E% d - ); 0 z8 `; m; ?* n3 m" ~+ H- @
-
. j0 d" Y' n; \$ S/ b - 4 y' ]' g: L* h7 v
- /**从文件建立图片
! I8 T z, \: B' k- s7 l - * @param string $filePath 文件地址路径
" C$ ` j; R' R% ~- \- H1 g6 ^ - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false 1 M" T1 Z* ?/ B4 l- Q! h0 M( X
- * */ , s- Q7 P! |0 ~8 O
- public static function createImage($filePath){ : v$ J" B" n5 v# s% \
- if(!file_exists($filePath)){ return false; } ( z" ?; z' w4 u
- ) k! x* l% R& }& v
- /*判断文件类型是否可以开启*/
" p2 |. Y- S% _ - $type = exif_imagetype($filePath); 5 A% [! s- ^7 ~4 ^# \, C3 ^0 s
- if(!array_key_exists($type,self::$_createFunc)){ return false; }
P8 C2 `& Q# h: Z9 s - 8 J/ y0 d( W7 K i& V
- $func = self::$_createFunc[$type];
6 ^9 y" t- t* ]* g4 c, X0 v - if(!function_exists($func)){ return false; } 5 S% N5 O3 \' c, k2 _/ @* @1 r; t
-
+ I4 t3 y" d: j$ N" ^6 G+ K- Z - return $func($filePath); 1 `0 Q! o( i* F' e o1 N$ z
- } % j$ r" e; q4 n& U
-
' |6 n. E- d) f) V6 G# a2 [1 G* Z8 i3 T -
5 v. I+ G! W) w6 U - /**hash 图片
/ k" C' K- V, h0 s* `1 O" f - * @param resource $src 图片 resource ID 2 T9 D: t& J) U9 T
- * @return string 图片 hash 值,失败则是 false
! r1 h6 \/ c9 e1 e - * */
% w4 ]& }. T8 h4 Z& \ - public static function hashImage($src){
- I7 {4 C0 @! \8 L( v* o - if(!$src){ return false; } 3 {" ]8 L0 R% V- j$ _2 _( D, g
-
4 Z' ^9 g4 s3 J6 B - /*缩小图片尺寸*/
$ _& X# ]0 @8 C2 }, M8 s- B - $delta = 8 * self::$rate;
6 {3 h9 c+ A, g2 X) x8 d - $img = imageCreateTrueColor($delta,$delta); 7 h# S0 O4 k! p# n
- imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
* {, t, Q( b$ a s# ^ -
4 b+ J/ k- Y. U) v2 O - /*计算图片灰阶值*/
, k6 a( H+ X/ p# ?( y" x/ k - $grayArray = array();
+ f* n1 F3 {+ \ b0 `* r/ v5 r - for ($y=0; $y<$delta; $y++){
8 I1 v: F: O! k3 R Q6 @0 R* m! i - for ($x=0; $x<$delta; $x++){
3 ~1 J0 y4 I9 C) V& ?, M U3 u# p - $rgb = imagecolorat($img,$x,$y);
/ a) r3 b; I/ y d - $col = imagecolorsforindex($img, $rgb); 0 g% Z3 b6 R/ P! C0 H. h! b, J
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; ! W9 I! Q4 Q$ U) l
- * X# l( _4 |* T+ i) i( N4 N& N
- $grayArray[] = $gray;
/ q( X6 O; g: T+ E6 K0 [' _) P# n - } S" c0 P( c3 L4 N6 G5 T
- }
- o) `, `7 y# k$ |! Y u3 G - imagedestroy($img);
& I% A$ o8 ?: g \9 U$ t -
9 o' K9 L2 H9 a( A - /*计算所有像素的灰阶平均值*/
4 q7 x4 z( m" V+ q6 v - $average = array_sum($grayArray)/count($grayArray);
3 U* Z3 y, y2 H - 1 F x. j; t3 x5 K2 N. J# q
- /*计算 hash 值*/
$ V0 ]. f- P) l& w6 b- b, H. X) B: A - $hashStr = ''; - v) v2 [' H1 ~- s$ a R
- foreach ($grayArray as $gray){
0 t/ E p( V: Z4 k* C B+ R$ y- e% E4 i - $hashStr .= ($gray>=$average) ? '1' : '0'; % [* x+ Z+ F8 ]) t
- }
8 L9 ]$ A$ C. y( R( R! H+ T1 h1 L - " g: x5 Y) u; X$ B+ `9 z' b
- return $hashStr;
- |3 h1 h* Y }* V1 v* f - }
2 v: o: f l- o6 S - 7 k& w3 U# j$ C8 w( d. x0 F
-
3 s9 \0 ^; X G) r - /**hash 图片文件 3 V9 ~( @/ Q! o* d
- * @param string $filePath 文件地址路径 , `8 ^; C" G$ L; K# Z
- * @return string 图片 hash 值,失败则是 false
% i: X8 H) i% J- P" h- y( \ - * */ 0 U! f/ J' O/ p& {& G; l
- public static function hashImageFile($filePath){ 3 C) W% i- S* W4 t; H% X: s k- F9 V/ H
- $src = self::createImage($filePath);
, s0 T1 O, o* @, z! i9 g. s - $hashStr = self::hashImage($src);
5 A# i6 h2 U& `% I; N/ I4 k; F" d - imagedestroy($src);
+ w9 L& N' y: T -
+ |7 @0 d" p) o# Z9 Q' @/ ? - return $hashStr;
R/ ?! b, K. [( G - } # d) @( B; d; A, R: }1 O- D' R
- & f+ B; G- I" u6 U$ S8 ]
-
! P6 I1 e( {* @" X0 B$ p - /**比较两个 hash 值,是不是相似
3 Z0 Z9 Y, P4 j' g- } - * @param string $aHash A图片的 hash 值
% m3 T, p. S8 F6 j5 u - * @param string $bHash B图片的 hash 值 ( N" W# G2 w+ D g& r% B2 e
- * @return bool 当图片相似则传递 true,否则是 false
6 x2 O0 f( q# m* B& B! o - * */
8 E5 j' q0 Y/ G R( [ - public static function isHashSimilar($aHash, $bHash){
. c+ K9 ?& ^# V7 ~4 k- L - $aL = strlen($aHash); $bL = strlen($bHash);
^9 w& E) a" b0 q8 s8 }: [& { - if ($aL !== $bL){ return false; } * @: }7 H& ^* J6 X* `3 `: K
- ! Z/ v. [: r7 \, a; i6 ?9 N
- /*计算容许落差的数量*/
9 y7 L: |+ F/ L0 z: M9 p - $allowGap = $aL*(100-self::$similarity)/100; " I0 X$ Q y4 k# b
- & X% B" \2 b3 `
- /*计算两个 hash 值的汉明距离*/
5 c) ]' a( K5 ~; H4 w4 K" L6 C6 q, f/ e - $distance = 0; 1 M4 ?' z) y* W! ]) r, z1 G& o
- for($i=0; $i<$aL; $i++){ ' R2 ]9 V: [, W; M- }0 f$ a
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
" ]7 t# v6 D& S! m) k: ~9 |3 m, ] - } & K* U" _; f1 F
-
9 u& y. _' c8 \2 c$ O1 [ - return ($distance<=$allowGap) ? true : false; C' a. _% v* `5 f
- }
0 r% e+ w: X: ?) S) a1 k- T4 Y+ L9 | -
( d3 ?7 e8 R" Q* u- s - L" l% @, C9 n: j
- /**比较两个图片文件,是不是相似
/ ^! z3 V+ ~, P - * @param string $aHash A图片的路径 ) w9 G e4 a( K$ |8 ~6 }) `1 y! G& ~
- * @param string $bHash B图片的路径
7 ~' B9 L, V$ |* I9 j* l - * @return bool 当图片相似则传递 true,否则是 false M4 l" V% [* t8 n; R- k
- * */
0 a) m- D j" J$ P S7 z6 z - public static function isImageFileSimilar($aPath, $bPath){
% O' x0 D7 O9 W/ D - $aHash = ImageHash::hashImageFile($aPath); & x+ M2 i" s" O: ~9 t$ r
- $bHash = ImageHash::hashImageFile($bPath);
* p% `! g' R% ]7 C' D7 f - return ImageHash::isHashSimilar($aHash, $bHash);
* d M; K& H: a z - }
- L6 n, F% M( j. z -
2 h6 e6 j$ i g8 }6 M0 ]/ {) W - }
* n+ ^) Q2 O0 t1 C4 O
复制代码 & `/ K9 E; ] z: `2 s; K
0 x$ x( ~" Z' d" V2 a' y6 E6 C
|
|