管理员
   
论坛积分
分
威望 点
贡献值 个
金币 枚
|
摘要: 本文讲的是分享一个PHP简易的图片相似度比较类, 由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。 代码如下 复制代码 <?php /** * 图- T5 h+ n" X: ~$ x
, |5 I# z* M2 ~* Q4 ~5 a- a. G: c# t5 {2 `; i+ ^ i/ [
由于相似图片搜索的php实现的 API 不怎么符合我的用途,所以我重新定义 API 的架构,改写成比较简单的函数方式,虽然还是用对象的方式包装。1 ^0 L% H: f0 {0 a% F2 [
- <?php 5 Z/ G, }! x! q- E
- /** `. C) F- d4 l+ v' J1 }; X
- * 图片相似度比较
n, ]" T+ o7 ?1 I k! b* u' R - *
( [ ]& [0 n: C - * @version $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax [ DISCUZ_CODE_1 ]nbsp; 0 ]6 ]5 {! o$ k* {% l
- * @author jax.hu 7 n6 D/ s# c: }) ]1 u
- *
8 h p- v$ Z7 ?! [" \" I( b' n - * <code>
) c# x$ h6 R, v8 }7 P - * //Sample_1 8 k4 E4 C8 L: i) w( e' z+ x' b* v8 X" g
- * $aHash = ImageHash::hashImageFile('wsz.11.jpg'); ! U( C6 j8 f/ Y6 z H
- * $bHash = ImageHash::hashImageFile('wsz.12.jpg');
' k% n# D+ \& A: u& B) i - * var_dump(ImageHash::isHashSimilar($aHash, $bHash));
. e) r, g! c3 y9 n5 F+ b - *
* k. }: d' X; c - * //Sample_2 $ p) ~& t; i4 u
- * var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg'));
! R2 v" D( S% I' C- T - * </code>
# U. x( y4 s" s5 C1 \7 i' X - */ 7 R9 m' S* Z+ l8 B
- + I- V" J4 R) \, Z5 d
- class ImageHash { - Q/ E b" B" s
-
3 a0 L: v3 x. T0 H* E - /**取样倍率 1~10
5 O0 i$ D0 X, e& Q - * @access public # a+ t: ^# p! m! r8 B' U
- * @staticvar int + m" a0 I9 e B* W9 H
- * */ / M) g3 T1 u" p! j
- public static $rate = 2; 1 T, ^8 P7 Y# J7 L8 c
-
1 R+ W4 _6 A: G: f - /**相似度允许值 0~64 . |0 |6 L7 n( f/ b Z7 }. ^2 `
- * @access public ' U3 U0 z; m6 X: Z: N
- * @staticvar int ) Q# a$ z* c/ Q5 o" R
- * */ - P4 c; b4 S; J3 ^8 [
- public static $similarity = 80; 1 S* S7 c; o) ]; @
-
6 Y5 U* ~7 \9 Y6 l1 j - /**图片类型对应的开启函数
3 N5 D( N: N( y4 n1 p, E - * @access private 8 W1 O" |& m1 ^4 }
- * @staticvar string
2 L5 l$ r1 x2 N) h: B - * */ 9 {! ~* f' e5 c
- private static $_createFunc = array( " T2 I' z0 O" u" G- h
- IMAGETYPE_GIF =>'imageCreateFromGIF',
& W1 n2 f+ v: k - IMAGETYPE_JPEG =>'imageCreateFromJPEG', ' G( y% ^3 l2 g( M
- IMAGETYPE_PNG =>'imageCreateFromPNG', 3 q: l+ {2 a0 s" g& X
- IMAGETYPE_BMP =>'imageCreateFromBMP', + a; g1 a- \# Y/ \6 e% l, p1 B
- IMAGETYPE_WBMP =>'imageCreateFromWBMP',
S2 x; o9 v% A; Z( o+ { - IMAGETYPE_XBM =>'imageCreateFromXBM', ) `$ B: y: @) r X( z
- );
5 t; k! U. I; s5 a -
: R" B2 O$ i' E" S3 {( H; Y5 L -
7 Y* d8 m' }1 P( L# B+ t7 z - /**从文件建立图片
) A! U- X2 X' P0 Y6 ~$ t - * @param string $filePath 文件地址路径
/ F9 I& ^) X `9 |% o - * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
9 i) G5 p, S8 o - * */
: s- [) {3 r* P+ n* ] - public static function createImage($filePath){
* |9 p8 e, D! O9 u0 ^& n - if(!file_exists($filePath)){ return false; }
# \% X" ~$ M- C. S- ~+ W -
, m( M1 }1 _/ ]! [$ _ - /*判断文件类型是否可以开启*/
$ C' H/ P5 _/ N8 ?1 N! E - $type = exif_imagetype($filePath);
3 k) O( @. y T7 |1 k - if(!array_key_exists($type,self::$_createFunc)){ return false; } ; i( G. F* F: S& w% T
-
3 R2 b7 A1 G [5 _+ ^ - $func = self::$_createFunc[$type];
) l% b% J" N8 {$ I8 R1 v6 \ - if(!function_exists($func)){ return false; } # s+ s$ y) u E& X
-
4 ^3 m. F' \$ u) V7 M - return $func($filePath); 5 g8 Q3 V2 e9 ~9 v& B; ^
- }
/ J% s4 m1 y7 ~ - 8 Y, |: B6 I7 i1 Z1 e
- . J7 |+ s- [) U& y0 v- S& K1 L; o
- /**hash 图片
( F8 P. p7 R, V5 L+ l! I- g: b - * @param resource $src 图片 resource ID / \9 v [+ [' ?/ H$ U7 ^0 h; e
- * @return string 图片 hash 值,失败则是 false % G1 {; a1 S4 }# Y6 G' ~5 l
- * */
6 d7 p. L' y. X; J d! n; R V - public static function hashImage($src){ 7 e' D1 B1 K ~; R# }2 \) h4 i
- if(!$src){ return false; }
+ g- m! r0 ]2 p& M -
- e) Y; F2 \2 ^( Z - /*缩小图片尺寸*/
7 z) o" }2 E6 U( }( Z% b - $delta = 8 * self::$rate;
1 N! v8 `- F0 T1 o - $img = imageCreateTrueColor($delta,$delta);
% Y$ G! g c! U, r; r6 k$ G - imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 9 S. |+ C. \* I3 j* I) i4 X. M+ o
-
9 O0 _; T5 l4 v, k' \% T - /*计算图片灰阶值*/ " x8 O3 S; B: E: x2 J! O* f" A
- $grayArray = array(); 0 ?3 y) k1 k9 r6 p0 |
- for ($y=0; $y<$delta; $y++){ 9 h6 c+ I1 e& x& I; m8 w
- for ($x=0; $x<$delta; $x++){
# S9 I) w7 W/ C) p - $rgb = imagecolorat($img,$x,$y); ! N5 a4 D: N* l, v- l9 ]
- $col = imagecolorsforindex($img, $rgb); - r; R7 I0 C3 ~6 E- H3 R' r
- $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; / W5 S8 V- l: ? Y. d- K
-
0 B W% z$ g: u. ^8 d8 t% I* p - $grayArray[] = $gray;
- M' W. s" ]% ]/ P- w. D; A: N$ h - }
0 _! g6 ?8 t" j/ w. z, Z2 @ - } * n) j* h0 i, O1 T4 b. V9 P' W
- imagedestroy($img); : j5 d; T1 j K
- : M1 T& l( L$ o9 {0 k. C; K
- /*计算所有像素的灰阶平均值*/
7 x0 a. T' I7 ^3 _$ D- d - $average = array_sum($grayArray)/count($grayArray); W, n( v, Z( W- |4 d
- 1 e8 d# k# @5 f: n! d/ e
- /*计算 hash 值*/
* x+ {7 T9 ^3 m) h# E - $hashStr = '';
) l5 W Q2 I9 r% Q - foreach ($grayArray as $gray){
/ E5 _! |1 q8 |& @0 j - $hashStr .= ($gray>=$average) ? '1' : '0'; ; {5 p) M' ?, _: \7 [0 u
- } ) Z" _" U7 K& G2 g! N
-
8 g! C. j' F0 R# O/ U - return $hashStr; % t8 U8 f3 |# U, f. w% N7 T
- } ; s: B4 J3 X& j1 M8 Z% R/ P/ r
-
E" x+ N# I* w# [5 D0 L - . q. B2 N; l: |) z3 g r) j
- /**hash 图片文件
- i) S, ^7 D9 f. A! m0 m8 C - * @param string $filePath 文件地址路径
# Y* g. P) J# { s1 i$ C - * @return string 图片 hash 值,失败则是 false 2 o3 w3 U9 V7 b! a9 r, M
- * */ ( x1 G; z5 s$ @2 c/ a
- public static function hashImageFile($filePath){
) b0 O( k. t8 c u - $src = self::createImage($filePath); : S: ]8 w! k% e, d" D
- $hashStr = self::hashImage($src);
* \! |1 T$ U5 E: b$ x4 V - imagedestroy($src); 7 I; C9 c6 ]+ ]+ y9 z
-
# m* d' |, I) t' V, G6 ]7 \4 G# R2 L, { - return $hashStr;
6 O g7 E' R( V* _ - } / g+ }4 O5 E$ a# ]6 U4 X
- . w( R8 V! }1 E4 a9 A
-
. g7 R; K0 l$ S6 L m$ d" R - /**比较两个 hash 值,是不是相似
& r' g! ^4 K- D$ Q( `" _. e - * @param string $aHash A图片的 hash 值 : B0 k6 E4 F( M9 B
- * @param string $bHash B图片的 hash 值 , p& Z& n; V! k B
- * @return bool 当图片相似则传递 true,否则是 false ; y: H/ n0 e) M' W5 w) x2 p1 W
- * */ % ?' ^1 c( n/ ` p3 p( R
- public static function isHashSimilar($aHash, $bHash){
! c& F- ~% ^" Q5 ~6 u' X - $aL = strlen($aHash); $bL = strlen($bHash);
, P0 H6 d" ] Y, |; m8 S - if ($aL !== $bL){ return false; }
8 [; }( |; N$ c! z - % s) u2 C" a+ ?4 ]
- /*计算容许落差的数量*/ 5 F: m V+ o9 u( C- _; o
- $allowGap = $aL*(100-self::$similarity)/100;
% ?# S+ C! X; z$ I3 Y& B1 n0 Q. c -
. U& q; o, E/ j+ G: o0 o - /*计算两个 hash 值的汉明距离*/
0 _% C6 {) n5 E1 T: V w - $distance = 0;
% i) k# Q1 l( Z" P - for($i=0; $i<$aL; $i++){
* H. d9 I' |' [- F! t - if ($aHash{$i} !== $bHash{$i}){ $distance++; } , ~+ ^9 S" j. |2 X3 ^
- }
2 J; |7 O$ m |+ _ -
/ W# ]1 L4 r: Y1 I8 z! l" n( i - return ($distance<=$allowGap) ? true : false; ) T1 X' O- x2 `
- }
1 x& j3 t1 B. R( b5 T - ) V' f5 R( b. V0 i) t) f
- : x& P2 p3 J( U8 H
- /**比较两个图片文件,是不是相似
' `* A; u/ P8 O0 v$ X6 \# _% F - * @param string $aHash A图片的路径 1 \8 @% V3 a2 O5 Z7 Q" }0 g
- * @param string $bHash B图片的路径
& J( C; K5 b" D% P: w9 \: _ - * @return bool 当图片相似则传递 true,否则是 false
) Z. N, O z+ A$ S8 ^ - * */ : A& O: D3 G# y; T* W, j) k
- public static function isImageFileSimilar($aPath, $bPath){
- ^. z9 O4 b" u2 I2 s7 j; X6 |! l - $aHash = ImageHash::hashImageFile($aPath); # e& B! o9 D3 o4 z+ \3 o
- $bHash = ImageHash::hashImageFile($bPath);
) z5 `1 b! K( F& M, b' k - return ImageHash::isHashSimilar($aHash, $bHash); $ ^# o- k( y& x9 x6 C) @
- } 1 b% i9 J, K0 g
-
0 z* Z- ?/ s7 D j* Q - }
; i- i% E- m4 r" U [
复制代码
! @% G; }+ K$ o+ y
$ \5 l- i1 N1 K* O6 p% I |
|