管理员
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图
( Q( B- V8 z5 Z3 y. N( E" t3 J# f; y' _
1 t: u% _: C: w1 b5 A7 ^由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。) c! e4 U6 {, O( e. x: m/ J
- <?php
5 R9 K3 l* F6 M" L - /**
1 {7 S0 q4 W# k0 {+ d* X7 h - * 图片相似度比较
6 m7 d; q( u9 d, N& Y4 n3 {5 D - * 3 q1 B' ~3 Z( o: N6 K3 g" }* l
- * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; ! d" z z9 T& v
- * @author jax.hu & {( J- [, W$ g
- *
7 u* d( D( c. K- M - * <code> ) T$ w! g& M, @, X9 A
- * //Sample_1
6 J; v9 B! s' `4 e u5 a% P - * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); / t) o: f P: b3 ?' R
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
& X! u$ Z/ s6 |+ c" Y* y+ @. { - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
1 R3 Q e% T/ w+ y) p# K$ W' \, m! z - *
$ ?( p9 p# f, f1 m ~: v4 } - * //Sample_2
3 t; _: R# K! r z8 e' W1 g9 K - * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
+ g) a" x" ^5 I: i' _ - * </code>
5 l( l+ r$ }( j - */
3 S$ R" g6 Q) E+ ~ -
0 u$ f/ [3 b: I" ^8 @4 a1 w - class ImageHash { / I j3 V$ x( Q5 ~. I7 P& V5 ?& \- e: }
-
0 {, X x ?2 v - /**取样倍率 1~10 9 Z- ^4 P c- f$ t
- * @access public
% d# W$ h7 I7 \; Y - * @staticvar int
* v/ D# x) h4 J - * */ $ h8 r+ I2 ~4 u) ^6 f. \) e# a
- public static $rate = 2;
0 i$ p4 o0 T, J) P( R -
9 E; M/ Z% b1 g' p1 v. v0 ]: E# j - /**相似度允许值 0~64 / v5 o3 E# q6 h2 J B( v5 W' o
- * @access public ; M5 N: H6 F2 y) M8 I/ N- g
- * @staticvar int
/ d! Q7 w, l+ d- ]# j s - * */
" n. M9 o6 f1 Q& G, _ - public static $similarity = 80; 7 T3 f* K( Y5 d3 f0 A: n7 a
- " O: {) L! d8 P/ C7 t0 w4 Y% S
- /**图片类型对应的开启函数
' W/ L5 l( O, a# X1 w' G8 y - * @access private
% J6 d; {; R/ x1 J: j - * @staticvar string - e6 m- I2 \ c; h" t' A
- * */
- V+ o7 Y# _& w! R0 e# o - private static $_createFunc = array( U7 t; ^: z0 i5 K* \5 P
- IMAGETYPE_GIF =>'imageCreateFromGIF', ) ~3 t4 R$ H1 j8 [: Y6 m4 z8 s, E7 h3 I
- IMAGETYPE_JPEG =>'imageCreateFromJPEG',
$ r& S7 `: M2 E/ ^' W. Z' m6 w0 F6 | - IMAGETYPE_PNG =>'imageCreateFromPNG',
& {' e; c ~ T' t! W9 b - IMAGETYPE_BMP =>'imageCreateFromBMP',
; H* Z/ F! W, _2 ~( ^; I - IMAGETYPE_WBMP =>'imageCreateFromWBMP',
: V, }* t8 r! h8 e, I) X - IMAGETYPE_XBM =>'imageCreateFromXBM',
2 k3 s+ {' I4 ~0 N - );
$ J) |" S; E7 y" E" D9 \$ w+ O - 7 L- o: Z/ x2 ]. a# _7 ^
- ' j* x7 R3 Y0 \& c( E
- /**从文件建立图片 3 [2 f+ |2 v0 d( a4 |* i
- * @param string $filePath 文件地址路径 / T p1 v4 O! B' t2 ]: B2 S7 t
- * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false + O) L5 f# F' b% \: ~+ I& ?( @
- * */
+ s. {2 m! z% I* ^, Y: }7 T - public static function createImage($filePath){
& L* H/ H# `& ^3 n3 F* S$ `5 ] - if(!file_exists($filePath)){ return false; } 8 A7 z( v( C" m/ N7 l
-
' @, x: d+ @6 \0 r! ` - /*判断文件类型是否可以开启*/ ; v: q* S) ~ N: ]& @5 ?
- $type = exif_imagetype($filePath);
6 Y+ g& y4 }% b8 v$ p) _: o2 T - if(!array_key_exists($type,self::$_createFunc)){ return false; }
: d) l1 W% |' \4 L, o - , x+ ], | X2 P3 o% q5 ~! k' x& i
- $func = self::$_createFunc[$type];
8 L5 L, m/ _3 m9 R' ?, c! x - if(!function_exists($func)){ return false; }
5 {' k- Q5 X! t - 5 ?8 y1 @! @* E$ Q4 t) m ]3 A/ v1 Z
- return $func($filePath);
8 g5 [5 ?/ ]9 ?: T% o. o - } 7 @) G9 S: s; V3 g) n4 _& }7 }5 Y
- 4 ~0 c' x; w( L0 R- O/ c. u
-
5 f- c5 W- t- F! M1 {+ ~* H - /**hash 图片
3 ]4 t0 ]% U5 a7 i' q5 P - * @param resource $src 图片 resource ID
' W1 r* q% K2 Z5 D - * @return string 图片 hash 值,失败则是 false
; S, @# q( r8 J/ ?2 |( _: E, ]9 } - * */
4 @. P/ j+ M- L2 C, h - public static function hashImage($src){ ' P. ]/ z2 ~8 \& _5 f; i
- if(!$src){ return false; }
6 J1 v/ ~/ b' `. U -
: b5 v6 T0 J! i, V - /*缩小图片尺寸*/
. _# f! E1 V8 [8 {8 p7 M - $delta = 8 * self::$rate;
$ K8 v7 m ?5 {. K9 A% g - $img = imageCreateTrueColor($delta,$delta);
+ I4 M' ]* p# w2 t7 V) z - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 8 k7 d) o' L. R- p1 U
-
8 g/ u2 Q2 H4 ?6 P: n* b& G0 @- l - /*计算图片灰阶值*/ : r% g6 @* r# Q* q0 e) y I' @
- $grayArray = array();
% f( E; c% a+ w" @2 h5 N9 a# m* T - for ($y=0; $y<$delta; $y++){
/ M" u9 M) _2 u8 W - for ($x=0; $x<$delta; $x++){
8 F; J2 Z5 _" g - $rgb = imagecolorat($img,$x,$y);
( z. |+ e, k' g! e - $col = imagecolorsforindex($img, $rgb); 6 F0 V; F9 N: ^
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
3 ^" A2 o/ z; l* U - ! m$ d+ ?6 P' K
- $grayArray[] = $gray;
2 c. a2 J! X: B- [" ? - } 1 i5 o, @8 j/ k7 G, M
- } # @" {2 e4 t- g& F
- imagedestroy($img); ( w& a; \! q; ]6 w
- * u# O; r1 `! l: A* o
- /*计算所有像素的灰阶平均值*/ % T. z' U, F+ @* t1 w
- $average = array_sum($grayArray)/count($grayArray);
( @ Z1 F0 k2 s8 }- @2 w -
2 ~% ]2 P/ Z6 ^: C5 }. a4 _8 z - /*计算 hash 值*/ . o( [( e; W4 ~- P3 u
- $hashStr = '';
2 J2 i1 ^; v( I3 k8 p" b4 o2 W - foreach ($grayArray as $gray){ 1 O) N- r$ Q/ j: b/ U: C: ^
- $hashStr .= ($gray>=$average) ? '1' : '0'; ! G& x2 H3 n# F, r2 z
- } * s% t3 |% |9 w3 ~" X
-
- f" F& q4 H5 U. D5 T - return $hashStr; 3 p2 Z! l- E) v# U# c( e% j
- } % F9 P1 X1 t; B
- 8 w* I( e w3 ?: l" J
-
& Z$ S: P2 `8 x9 L* _1 W" p - /**hash 图片文件 ; z& G9 b( H! |* f' M
- * @param string $filePath 文件地址路径 0 w0 \" Y+ P. {+ F* y3 `- M# L
- * @return string 图片 hash 值,失败则是 false 0 d* f, v2 X- u( ]1 V$ e$ K
- * */ ; ]/ e' b6 g# X- y' I
- public static function hashImageFile($filePath){ " L' G: |7 J. w+ @, {7 @* v
- $src = self::createImage($filePath);
5 D+ _2 v% f3 R, I# Y - $hashStr = self::hashImage($src);
( X2 s0 ^/ s4 C* M% H - imagedestroy($src); 6 Z! w$ b4 Q& M
-
4 i* R9 e" S5 O$ n* i) n - return $hashStr;
+ R" ~- r+ R6 ~9 ?. U) ? - } ; c" @# u# T( H! u) B. `. l
- 6 @, c+ l Y5 S/ c- a, J; A
- 5 Y, _) J5 b9 ^5 O% a* j( A
- /**比较两个 hash 值,是不是相似
2 h1 p" E, b+ a6 R4 H$ l6 D6 S - * @param string $aHash A图片的 hash 值 8 l/ I3 C9 M/ ]: X, S- W# G
- * @param string $bHash B图片的 hash 值
+ U7 ^& e& @0 s% j - * @return bool 当图片相似则传递 true,否则是 false
. |3 r# ^4 z2 c6 f6 {* n% P7 R5 E, m - * */
! U+ U; x1 B3 C - public static function isHashSimilar($aHash, $bHash){ 3 v7 p( y; g- @" } M9 B) p: H
- $aL = strlen($aHash); $bL = strlen($bHash);
1 o5 F% r5 F* i4 V' \ - if ($aL !== $bL){ return false; }
7 a9 q6 W" R7 [& L& ^) n8 S# l -
) ?8 n" t: R6 d - /*计算容许落差的数量*/ 2 |9 g% ^, X: k) M& r' Q' v8 N) S
- $allowGap = $aL*(100-self::$similarity)/100; ! k9 C3 }" L4 y; O) v3 B/ E
- 3 `' z( i V2 q1 ~0 b9 C; U$ V
- /*计算两个 hash 值的汉明距离*/ ( O2 J8 M1 M& h% u
- $distance = 0; & I5 X2 _, ^7 t$ d" P2 }3 _
- for($i=0; $i<$aL; $i++){ . w2 g1 H3 H' m' [. H
- if ($aHash{$i} !== $bHash{$i}){ $distance++; }
% C1 g9 s2 s$ Y, j - }
+ h) c4 W( }5 _/ \$ \9 Z/ Y -
# ^* S/ d2 F1 g- n* V - return ($distance<=$allowGap) ? true : false;
0 ^' Z' ?$ ]6 { - }
- z( p) w" o ? -
6 {2 M2 p+ L8 M, u4 `( e1 L* `. m1 C/ ~ - 5 x6 L+ }, e6 w0 Z; E" H/ y
- /**比较两个图片文件,是不是相似
# m* S. r" Y# Y - * @param string $aHash A图片的路径 + C$ o0 `4 i; |" {
- * @param string $bHash B图片的路径
, R* j. X( s& ^) C5 w/ p% P# q - * @return bool 当图片相似则传递 true,否则是 false 4 j+ K9 x t1 C) q3 j% f. F
- * */
6 z/ o' z& S/ J1 T3 Y6 U - public static function isImageFileSimilar($aPath, $bPath){ , ?# E1 O4 f" b
- $aHash = ImageHash::hashImageFile($aPath); " ~2 M* S/ l* L4 ^$ I- X
- $bHash = ImageHash::hashImageFile($bPath);
. q, i3 k C) | - return ImageHash::isHashSimilar($aHash, $bHash); ! E5 t0 [$ J6 l: T! y$ u8 y
- } # k7 j/ R! g) H4 D( L; h& U, ]- a
-
* H8 K5 O' w6 k1 j - }" ~. g$ B9 B; T4 V" A& o
复制代码
" W# v9 g) d o1 S* |: J
! U3 H' Q) o3 V- F$ y |
|