虹软人脸检测和识别C# - API

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Drawing;
  4 using System.Drawing.Drawing2D;
  5 using System.Drawing.Imaging;
  6 using System.Linq;
  7 using System.Runtime.InteropServices;
  8 using System.Text;
  9 
 10 namespace ArcSoft_Face_Demo_X32
 11 {
 12     public class ArcSoft_FACE_API
 13     {
 14         public class FSDK_FACE_SHARE
 15         {
 16             public static string app_Id_X32 = "3yUbUj1c1YKeRuyW616PU8FB1cgei3sFpyqMRBDQVVo6";
 17             public static string sdk_Key_FD_X32 = "FCVQpXhiCm8GGbYxTVUpEc2yT6evA3stiYN68nDRJ7mG";
 18             public static string sdk_Key_FR_X32 = "FCVQpXhiCm8GGbYxTVUpEc36cVv5iBCyDsTTyHXYWtg8";
 19 
 20             //public static string app_Id_X64     = "3yUbUj1c1YKeRuyW616PU8F3rDRU6UzazKPvGkEdoYkR";
 21             //public static string sdk_Key_FD_X64 = "FshWfRcQPgrq9YGbmNRA6q7sp1wyGXVgfmwJacKZ1Vtn";
 22             //public static string sdk_Key_FR_X64 = "FshWfRcQPgrq9YGbmNRA6q7zyRD81y9qz9FvhJDdiwdv";
 23 
 24             public static IntPtr detectEngine = IntPtr.Zero;
 25             public static IntPtr recogitionEngine = IntPtr.Zero;
 26 
 27 
 28             public static int detectSize = 500 * 1024 * 1024;
 29             public static int recogitionSize = 500 * 1024 * 1024;
 30 
 31             public static int nScale = 16;
 32             public static int nMaxFaceNum = 10;
 33 
 34 
 35             //人脸矩形框信息
 36             public struct MRECT
 37             {
 38                 public int left;                    //左边
 39                 public int top;                     //上边
 40                 public int right;                   //右边
 41                 public int bottom;                  //下边
 42             }
 43 
 44             //定义图像格式空间
 45             public struct ASVLOFFSCREEN
 46             {
 47                 public int u32PixelArrayFormat;     //像素阵列格式
 48                 public int i32Width;                //宽度
 49                 public int i32Height;               //高度
 50                 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = System.Runtime.InteropServices.UnmanagedType.SysUInt)]
 51                 public System.IntPtr[] ppu8Plane;   //平面
 52                 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I4)]
 53                 public int[] pi32Pitch;             //倾斜
 54             }
 55 
 56 
 57             public static byte[] ReadBmpToByte(Bitmap image, ref int width, ref int height, ref int pitch)
 58             {
 59                 //将Bitmap锁定到系统内存中,获得BitmapData
 60                 BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
 61                 //位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
 62                 IntPtr ptr = data.Scan0;
 63                 //定义数组长度
 64                 int soureBitArrayLength = data.Height * Math.Abs(data.Stride);
 65                 byte[] sourceBitArray = new byte[soureBitArrayLength];
 66                 //将bitmap中的内容拷贝到ptr_bgr数组中
 67                 Marshal.Copy(ptr, sourceBitArray, 0, soureBitArrayLength); width = data.Width;
 68                 height = data.Height;
 69                 pitch = Math.Abs(data.Stride);
 70 
 71                 int line = width * 3;
 72                 int bgr_len = line * height;
 73                 byte[] destBitArray = new byte[bgr_len];
 74                 for (int i = 0; i < height; ++i)
 75                 {
 76                     Array.Copy(sourceBitArray, i * pitch, destBitArray, i * line, line);
 77                 }
 78                 pitch = line;
 79                 image.UnlockBits(data);
 80                 return destBitArray;
 81             }
 82 
 83             public static Bitmap CutFace(Bitmap srcImage, int StartX, int StartY, int iWidth, int iHeight)
 84             {
 85                 if (srcImage == null)
 86                 {
 87                     return null;
 88                 }
 89                 int w = srcImage.Width;
 90                 int h = srcImage.Height;
 91                 if (StartX >= w || StartY >= h)
 92                 {
 93                     return null;
 94                 }
 95                 if (StartX + iWidth > w)
 96                 {
 97                     iWidth = w - StartX;
 98                 }
 99                 if (StartY + iHeight > h)
100                 {
101                     iHeight = h - StartY;
102                 }
103                 try
104                 {
105                     Bitmap bmpOut = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
106                     Graphics g = Graphics.FromImage(bmpOut);
107                     g.DrawImage(srcImage, new Rectangle(0, 0, iWidth, iHeight), new Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);
108                     g.Dispose();
109                     return bmpOut;
110                 }
111                 catch
112                 {
113                     return null;
114                 }
115             }
116 
117             public static Image DrawRectangleInPicture2(Image bmp, MRECT[] rectArr, Color RectColor, int LineWidth, DashStyle ds)
118             {
119                 if (bmp == null) return null;
120                 Graphics g = Graphics.FromImage(bmp);
121                 Brush brush = new SolidBrush(RectColor);
122                 Pen pen = new Pen(brush, LineWidth);
123                 pen.DashStyle = ds;
124                 for (int i = 0; i < rectArr.Length; i++)
125                 {
126                     Point p0 = new Point(rectArr[i].left, rectArr[i].top);
127                     Point p1 = new Point(rectArr[i].right, rectArr[i].bottom);
128                     g.DrawRectangle(pen, new Rectangle(p0.X, p0.Y, Math.Abs(p0.X - p1.X), Math.Abs(p0.Y - p1.Y)));
129 
130                 }
131                 g.Dispose();
132                 return bmp;
133             }
134 
135             /// <summary>
136             /// 将一个字节数组转换为24位真彩色图
137             /// </summary>
138             /// <param name="imageArray">字节数组</param>
139             /// <param name="width">图像的宽度</param>
140             /// <param name="height">图像的高度</param>
141             /// <returns>位图对象</returns>
142             public static Bitmap ToGrayBitmap(byte[] imageArray, int width, int height)
143             {
144 
145                 //将用户指定的imageArray二维数组转换为一维数组rawValues
146                 byte[] rawValues = imageArray;
147                 //申请目标位图的变量,并将其内存区域锁定
148                 Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
149                 BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
150 
151                 //获得图像的参数
152                 int stride = bmpData.Stride; //扫描线的宽度
153 
154                 // int offset = stride - width;  转换为8位灰度图时
155                 int offset = stride - width * 3; //显示宽度与扫描线宽度的间隙,
156                                                  //与8位灰度图不同width*3很重要,因为此时一个像素占3字节
157 
158                 IntPtr iptr = bmpData.Scan0; //获得 bmpData的内存起始位置
159                 int scanBytes = stride * height; //用Stride宽度,表示内存区域的大小
160 
161                 //下面把原始的显示大小字节数组转换为内存中的实际存放的字节数组
162                 int posScan = 0, posReal = 0; //分别设置两个位置指针指向源数组和目标数组
163                 byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存
164 
165                 for (int x = 0; x < height; x++)
166                 {
167                     for (int y = 0; y < width; y++)
168                     {
169                         //转换为8位灰度图时
170                         //pixelValues[posScan++]=rawValues[posReal++];
171                         //}
172                         //posScan+=offset;
173                         //此处也与8位灰度图不同,分别对R,G,B分量赋值,R=G=B
174                         //posScan也由posScan++变为posScan+= 3;      
175 
176                         pixelValues[posScan] = pixelValues[posScan + 1] = pixelValues[posScan + 2] = rawValues[posReal++];
177                         posScan += 3;
178                     }
179                     posScan += offset; //行扫描结束,要将目标位置指针移过那段间隙
180                 }
181 
182                 //// 用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
183                 System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);
184                 bmp.UnlockBits(bmpData); //解锁内存区域
185 
186                 ////// ---------------------------------------------------------------------------
187 
188                 ////下面的代码是8位灰度索引图时才需要的,是为了修改生成位图的索引表,从伪彩修改为灰度
189                 //ColorPalette tempPalette;
190                 //using (Bitmap tempBmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
191                 //{
192                 //    tempPalette = tempBmp.Palette;
193                 //}
194                 //for (int i = 0; i < 256; i++)
195                 //{
196                 //    tempPalette.Entries[i] = Color.FromArgb(i, i, i);
197                 //}
198 
199                 //bmp.Palette = tempPalette;
200                 //-----------------------------------------------------------------------------
201                 //// 算法到此结束,返回结果
202                 return bmp;
203             }
204 
205 
206             public static Bitmap ToGrayBitmap2(byte[] imageArray, int width, int height)
207             {
208 
209                 //将用户指定的imageArray二维数组转换为一维数组rawValues
210                 byte[] rawValues = imageArray;
211                 //申请目标位图的变量,并将其内存区域锁定
212                 Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
213                 BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
214 
215                 //获得图像的参数
216                 int stride = bmpData.Stride; //扫描线的宽度
217 
218                 // int offset = stride - width;  转换为8位灰度图时
219                 int offset = stride - width * 3; //显示宽度与扫描线宽度的间隙,
220                                                  //与8位灰度图不同width*3很重要,因为此时一个像素占3字节
221 
222                 IntPtr iptr = bmpData.Scan0; //获得 bmpData的内存起始位置
223                 int scanBytes = stride * height; //用Stride宽度,表示内存区域的大小
224 
225                 //下面把原始的显示大小字节数组转换为内存中的实际存放的字节数组
226                 int posScan = 0, posReal = 0; //分别设置两个位置指针指向源数组和目标数组
227                 byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存
228 
229                 for (int x = 0; x < height; x++)
230                 {
231                     for (int y = 0; y < width; y++)
232                     {
233                         //pixelValues[posScan] = pixelValues[posScan + 1] = pixelValues[posScan + 2] = rawValues[posReal++];
234 
235                         pixelValues[posScan] = rawValues[posReal++];
236                         pixelValues[posScan + 1] = rawValues[posReal++];
237                         pixelValues[posScan + 2] = rawValues[posReal++];
238                         posScan += 3;
239                     }
240                     posScan += offset; //行扫描结束,要将目标位置指针移过那段间隙
241                 }
242 
243                 //// 用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
244                 System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);
245                 bmp.UnlockBits(bmpData); //解锁内存区域
246 
247                 return bmp;
248             }
249 
250             //图片转为base64编码的文本
251             private string ImgToBase64String(string Imagefilename)
252             {
253                 String strbaser64 = "";
254                 try
255                 {
256                     Bitmap bmp = new Bitmap(Imagefilename);
257                     pictureBox1.Image = bmp;
258                     //FileStream fs = new FileStream(Imagefilename + ".txt", FileMode.Create);
259                     //StreamWriter sw = new StreamWriter(fs);
260 
261                     MemoryStream ms = new MemoryStream();
262                     bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
263                     byte[] arr = new byte[ms.Length];
264                     ms.Position = 0;
265                     ms.Read(arr, 0, (int)ms.Length);
266                     ms.Close();
267                     strbaser64 = Convert.ToBase64String(arr);
268                     //sw.Write(strbaser64);
269 
270                     //sw.Close();
271                     //fs.Close();
272                     // MessageBox.Show("转换成功!");
273                 }
274                 catch (Exception ex)
275                 {
276                     MessageBox.Show("ImgToBase64String 转换失败\nException:" + ex.Message);
277                 }
278                 return strbaser64;
279             }
280 
281 
282             //base64编码的文本转为图片
283             private void Base64StringToImage(string txtFileName, PictureBox picbox)
284             {
285                 try
286                 {
287                     FileStream ifs = new FileStream(txtFileName, FileMode.Open, FileAccess.Read);
288                     StreamReader sr = new StreamReader(ifs);
289 
290                     String inputStr = sr.ReadToEnd();
291                     byte[] arr = Convert.FromBase64String(inputStr);
292                     MemoryStream ms = new MemoryStream(arr);
293                     Bitmap bmp = new Bitmap(ms);
294 
295                     //bmp.Save(txtFileName + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
296                     //bmp.Save(txtFileName + ".bmp", ImageFormat.Bmp);
297                     //bmp.Save(txtFileName + ".gif", ImageFormat.Gif);
298                     //bmp.Save(txtFileName + ".png", ImageFormat.Png);
299                     ms.Close();
300                     sr.Close();
301                     ifs.Close();
302                     picbox.Image = bmp;
303                     if (File.Exists(txtFileName))
304                     {
305                         File.Delete(txtFileName);
306                     }
307                     //MessageBox.Show("转换成功!");
308                 }
309                 catch (Exception ex)
310                 {
311                     MessageBox.Show("Base64StringToImage 转换失败\nException:" + ex.Message);
312                 }
313             }
314         }
315 
316         public class FSDK_FACE_DETECTION
317         {
318             //定义SDK版本信息--FD
319             public struct AFD_FSDK_Version
320             {
321                 public int lCodebase;               //代码库版本号
322                 public int lMajor;                  //主版本号
323                 public int lMinor;                  //次版本号
324                 public int lBuild;                  //编译版本号,递增
325                 public IntPtr Version;              //字符串形式的版本号
326                 public IntPtr BuildDate;            //编译时间
327                 public IntPtr CopyRight;            //copyright
328             }
329 
330             //定义人脸检查结果中人脸的角度
331             public enum AFD_FSDK_OrientCode
332             {
333                 AFD_FSDK_FOC_0 = 1,
334                 AFD_FSDK_FOC_90 = 2,
335                 AFD_FSDK_FOC_270 = 3,
336                 AFD_FSDK_FOC_180 = 4,
337                 AFD_FSDK_FOC_30 = 5,
338                 AFD_FSDK_FOC_60 = 6,
339                 AFD_FSDK_FOC_120 = 7,
340                 AFD_FSDK_FOC_150 = 8,
341                 AFD_FSDK_FOC_210 = 9,
342                 AFD_FSDK_FOC_240 = 10,
343                 AFD_FSDK_FOC_300 = 11,
344                 AFD_FSDK_FOC_330 = 12
345             }
346 
347             //定义脸部检查角度的优先级
348             public enum AFD_FSDK_OrientPriority
349             {
350                 AFD_FSDK_OPF_0_ONLY = 1,            //检测 0 度方向
351                 AFD_FSDK_OPF_90_ONLY = 2,           //检测 90 度方向
352                 AFD_FSDK_OPF_270_ONLY = 3,          //检测 270 度方向
353                 AFD_FSDK_OPF_180_ONLY = 4,          //检测 180 度方向
354                 AFD_FSDK_OPF_0_HIGHER_EXT = 5       //检测 0, 90, 180, 270 四个方向,0 度更优先
355             }
356 
357             //检测到的脸部信息
358             public struct AFD_FSDK_FACERES
359             {
360                 public int nFace;                   //人脸个数
361                 public IntPtr rcFace;               //人脸矩形框信息 — MRECT
362                 public IntPtr lfaceOrient;          //人脸角度信息 — AFD_FSDK_OrientCode
363             }
364 
365             /******************************************——ArcSoft Face Detection——******************************/
366             /*
367             *初始化脸部检测引擎
368             *函数原形
369                 MRESULT AFD_FSDK_InitialFaceEngine(
370                     MPChar AppId,
371                     MPChar SDKKey,
372                     MByte *pMem,
373                     MInt32 lMemSize,
374                     MHandle *pEngine,
375                     AFD_FSDK_OrientPriority iOrientPriority,
376                     MInt32 nScale,
377                     MInt32 nMaxFaceNum
378                 );
379                 AppId [in]                 用户申请 SDK 时获取的 App Id
380                 SDKKey [in]             用户申请 SDK 时获取的 SDK Key
381                 pMem [in]                 分配给引擎使用的内存地址
382                 lMemSize [in]             分配给引擎使用的内存大小
383                 pEngine [out]             引擎 handle
384                 iOrientPriority [in]     期望的脸部检测角度的优先级
385                 nScale [in]             用于数值表示的最小人脸尺寸 有效值范围[2,50] 推荐值 16
386                 nMaxFaceNum [in]         用户期望引擎最多能检测出的人脸数 有效值范围[1,100]
387             */
388             [DllImport("libarcsoft_fsdk_face_detection.dll", EntryPoint = "AFD_FSDK_InitialFaceEngine", CallingConvention = CallingConvention.Cdecl)]
389             public static extern int AFD_FSDK_InitialFaceEngine(string appId, string sdkKey, IntPtr pMem, int lMemSize, ref IntPtr pEngine, int iOrientPriority, int nScale, int nMaxFaceNum);
390 
391 
392             /*
393             *根据输入的图像检测出人脸位置,一般用于静态图像检测
394             *函数原形
395                 MRESULT AFD_FSDK_StillImageFaceDetection(
396                     MHandle hEngine,
397                     LPASVLOFFSCREEN pImgData,
398                     LPAFD_FSDK_FACERES pFaceRes
399                 );
400                 hEngine [in]             引擎 handle
401                 pImgData [in]             带检测图像信息
402                 pFaceRes [out]             人脸检测结果            
403             */
404             [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
405             public static extern int AFD_FSDK_StillImageFaceDetection(IntPtr pEngine, IntPtr pImgData, ref IntPtr pFaceRes);
406 
407 
408             /*
409             *获取 SDK 版本信息
410             *函数原形
411                 const AFD_FSDK_Version * AFD_FSDK_GetVersion(
412                     MHandle hEngine
413                 );
414                 hEngine [in]             引擎 handle
415 
416             */
417             [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
418             public static extern IntPtr AFD_FSDK_GetVersion(IntPtr pEngine);
419 
420 
421             /*
422             *销毁引擎,释放相应资源
423             *函数原形
424                 MRESULT AFD_FSDK_UninitialFaceEngine(
425                     MHandle hEngine
426                 );
427                 hEngine [in]             引擎 handle
428             */
429             [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
430             public static extern int AFD_FSDK_UninitialFaceEngine(IntPtr pEngine);
431             /*********************************************************************************************************/
432 
433         }
434 
435 
436         public class FSDK_FACE_RECOGNITION
437         {
438             public struct AFR_FSDK_VERSION
439             {
440                 public int lCodebase;               //代码库版本号
441                 public int lMajor;                  //主版本号
442                 public int lMinor;                  //次版本号
443                 public int lBuild;                  //编译版本号,递增
444                 public int lFeatureLevel;           //特征库版本号
445                 public IntPtr Version;              //字符串形式的版本号
446                 public IntPtr BuildDate;            //编译时间
447                 public IntPtr CopyRight;            //copyright
448             }
449 
450             //脸部信息
451             public struct AFR_FSDK_FACEINPUT
452             {
453                 public FSDK_FACE_SHARE.MRECT rcFace;                //脸部矩形框信息  — MRECT
454                 public int lfaceOrient;                             //脸部旋转角度 — AFD_FSDK_OrientCode
455             }
456 
457 
458             //脸部特征信息
459             public struct AFR_FSDK_FACEMODEL
460             {
461                 public IntPtr pbFeature;            //提取到的脸部特征
462                 public int lFeatureSize;            //特征信息长度
463             }
464 
465             public Int32 AFD_FSDK_OrientPriority;
466             public Int32 AFD_FSDK_OrientCode;
467 
468             /*************************************——ArcSoft Face Recognition——***********************************/
469             /*
470             *初始化引擎
471             *函数原形
472                 MRESULT AFR_FSDK_InitialEngine(
473                     MPChar AppId,
474                     MPChar SDKKey,
475                     Mbyte *pMem,
476                     MInt32 lMemSize,
477                     MHandle *phEngine
478                 );
479                 Appid [in]                 用户申请 SDK 时获取的 id
480                 SDKKey [in]             用户申请 SDK 时获取的 id
481                 pMem [in]                 分配给引擎使用的内存地址
482                 lMemSize [in]             分配给引擎使用的内存大小
483                 phEngine [out]             引擎 handle
484             */
485             [DllImport("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_InitialEngine", CallingConvention = CallingConvention.Cdecl)]
486             public static extern int AFR_FSDK_InitialEngine(string appId, string sdkKey, IntPtr pMem, int lMemSize, ref IntPtr pEngine);
487 
488             /*
489             *获取脸部特征
490             *函数原形        
491                 MRESULT AFR_FSDK_ExtractFRFeature (
492                     MHandle hEngine,
493                     LPASVLOFFSCREEN pInputImage,
494                     LPAFR_FSDK_FACEINPUT pFaceRes,
495                     LPAFR_FSDK_FACEMODEL pFaceModels
496                 );
497                 hEngine [in]             引擎 handle
498                 pInputImage [in]         输入的图像数据
499                 pFaceRes [in]             已检测到到的脸部信息
500                 pFaceModels [out]         提取的脸部特征信息
501             */
502             [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
503             public static extern int AFR_FSDK_ExtractFRFeature(IntPtr pEngine, IntPtr pImgData, IntPtr pFaceRes, IntPtr pFaceModels);
504 
505 
506             /*
507             *脸部特征比较
508             *函数原形        
509                 MRESULT AFR_FSDK_FacePairMatching(
510                     MHandle hEngine,
511                     AFR_FSDK_FACEMODEL *reffeature,
512                     AFR_FSDK_FACEMODEL *probefeature,
513                     MFloat *pfSimilScore
514                 );
515                 hEngine [in]             引擎 handle
516                 reffeature [in]         已有脸部特征信息
517                 probefeature [in]         被比较的脸部特征信
518                 pfSimilScore [out]         相似程度数值        
519             */
520             [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
521             public static extern int AFR_FSDK_FacePairMatching(IntPtr pEngine, IntPtr reffeature, IntPtr probefeature, ref float pfSimilScore);
522 
523             /*
524             *结束引擎
525             *函数原形        
526                 MRESULT AFR_FSDK_UninitialEngine(
527                     MHandle hEngine
528                 );
529                 hEngine [in]             引擎 handle
530             */
531             [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
532             public static extern int AFR_FSDK_UninitialEngine(IntPtr pEngine);
533 
534             /*
535             *获取 SDK 版本信息
536             *函数原形
537                 const AFR_FSDK_VERSION * AFR_FSDK_GetVersion(
538                     MHandle hEngine
539                 );
540                 hEngine [in]             引擎 handle
541             */
542             [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
543             public static extern IntPtr AFR_FSDK_GetVersion(IntPtr pEngine);
544             /*****************************************************************************************************/
545         }
546 
547     }
548 }

猜你喜欢

转载自www.cnblogs.com/hbtmwangjin/p/9101722.html