C# 人脸识别

C# 人脸识别- (人脸考勤)

哈喽!大家晚上好!
这是我在博客的第一篇文章,主要功能是人脸识别(上位机方向,可接单片机做下属控制,图像处理使用的是OpenCV, 其核心算法是虹软3.0 开源 人脸识别算法,下面就为大家介绍准备工作

第一步:

1.虹软官网申请应用

卓耿在这里插入图片描述
这个是申请的算法:拿到SDK_KEY 和 APP_ID 下载对应的SDK 如下:
在这里插入图片描述

在这里插入图片描述
将这三个文件放在项目Debug 根目录下 里面包含dome 没有图像处理基础比较难理解 多花点时间啃下来

在这里插入图片描述
这个是下载的dome 可以在里面 先拿来练手

第二 开发环境搭建(准备工作)

1.配置appid ,sdkkey

// 初始化引擎  也就是 SDK的密钥 需要使用对于的Key开锁使用其功能

        /// <summary>
        /// 初始化引擎
        /// </summary>
        private void InitEngines()
        {
            //读取配置文件
            AppSettingsReader reader = new AppSettingsReader();
            string appId = "AURkzRw3k6YQyMexHr8XhEuS3cfEtXC5biYY9TXLLrTb";
            string sdkKey64 = "9MqSe2Mf9SrTArYdy5uGZPCTPsDPmJgKyHfKcxTK1Yqx"; 
            string sdkKey32 = "9MqSe2Mf9SrTArYdy5uGZPCTLcz9NuidSSbJY31GcqF7"; 
            rgbCameraIndex = 0; //(int)reader.GetValue("RGB_CAMERA_INDEX", typeof(int));
            irCameraIndex = 0; //(int)reader.GetValue("IR_CAMERA_INDEX", typeof(int));
            //判断CPU位数
            var is64CPU = false;//Environment.Is64BitProcess;
            if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(is64CPU ? sdkKey64 : sdkKey32))
            {
                //禁用相关功能按钮
                ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
                MessageBox.Show(string.Format("请在App.config配置文件中先配置APP_ID和SDKKEY{0}!", is64CPU ? "64" : "32"));
                return;
            }

            //在线激活引擎    如出现错误,1.请先确认从官网下载的sdk库已放到对应的bin中,2.当前选择的CPU为x86或者x64
            int retCode = 0;
            try
            {
                retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32);
            }
            catch (Exception ex)
            {
                //禁用相关功能按钮
                ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    MessageBox.Show("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!");
                }
                else
                {
                    MessageBox.Show("激活引擎失败!");
                }
                return;
            }
            Console.WriteLine("Activate Result:" + retCode);

            //初始化引擎
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
            //Video模式下检测脸部的角度优先值
            int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
            //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
            //人脸在图片中所占比例,如果需要调整检测人脸尺寸请修改此值,有效数值为2-32
            int detectFaceScaleVal = 16;
            //最大需要检测的人脸个数
            int detectFaceMaxNum = 5;
            //引擎初始化时需要初始化的检测功能组合
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
            //初始化引擎,正常值为0,其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
            Console.WriteLine("InitEngine Result:" + retCode);
            AppendText((retCode == 0) ? "引擎初始化成功!\n" : string.Format("引擎初始化失败!错误码为:{0}\n", retCode));
            if (retCode != 0)
            {
                //禁用相关功能按钮
                ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
            }

            //初始化视频模式下人脸检测引擎
            uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
            int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
            retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
            //RGB视频专用FR引擎
            detectFaceMaxNum = 1;
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);

            //IR视频专用FR引擎
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_IR_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoIRImageEngine);

            Console.WriteLine("InitVideoEngine Result:" + retCode);


            initVideo();
        }

摄像头初始化

 /// <summary>
        /// 摄像头初始化
        /// </summary>
        private void initVideo()
        {
            filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            //如果没有可用摄像头,“启用摄像头”按钮禁用,否则使可用
            if (filterInfoCollection.Count == 0)
            {
                btnStartVideo.Enabled = false;
            }
            else
            {
                btnStartVideo.Enabled = true;
            }
        }

读取指定文件中的照片



/// <summary>
        /// 人脸库图片选择按钮事件
        /// </summary>
        private void ChooseMultiImg1()
        {
            lock (locker)
            {
                strImageFile = m_ComIni.IniReadValue("ImageFile", "File");

                //List<string> imagePathListTemp = new List<string>();
                Dictionary<string, string> imagePathListTemp = new Dictionary<string, string>();
                var numStart = imagePathList.Count;
                int isGoodImage = 0;
                //保存图片路径并显示
                //    string[] fileNames = Director(@"E:\曹镇代码备份\自助登记\8.0\自助机\D8FK01S\bin\Debug\IDCard\Capture").ToArray();//openFileDialog.FileNames;
                string[] fileNames = Director(strImageFile).ToArray();

                for (int i = 0; i < fileNames.Length; i++)
                {
                    //图片格式判断
                    if (checkImage(fileNames[i]))
                    {
                        string imagename = fileNames[i].Split('\\')[fileNames[i].Split('\\').Length - 1];

                        imagePathListTemp.Add(imagename, fileNames[i]);
                    }
                }
                int a = 1;
                //人脸检测以及提取人脸特征
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                {
     
                    Invoke(new Action(delegate
                    {
                        chooseMultiImgBtn.Enabled = false;
                        matchBtn.Enabled = false;
                        btnClearFaceList.Enabled = false;
                        chooseImgBtn.Enabled = false;
                        btnStartVideo.Enabled = false;
                    }));


                    foreach (var item in imagePathListTemp)
                    {
                        Image image = ImageUtil.readFromFile(item.Value);
                        if (image == null)
                        {
                            continue;
                        }
                        if (image.Width > 1536 || image.Height > 1536)
                        {
                            image = ImageUtil.ScaleImage(image, 1536, 1536);
                        }
                        if (image == null)
                        {
                            continue;
                        }
                        if (image.Width % 4 != 0)
                        {
                            image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
                        }
                        //人脸检测
                        ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
                        //判断检测结果
                        if (multiFaceInfo.faceNum > 0)
                        {
                            imagePathList.Add(item.Value);
                            MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
                            image = ImageUtil.CutImage(image, rect.left, rect.top, rect.right, rect.bottom);
                        }
                        else
                        {
                            if (image != null)
                            {
                                image.Dispose();
                            }
                            continue;
                        }
                        //显示人脸
                        this.Invoke(new Action(delegate
                        {
                            if (image == null)
                            {
                                image = ImageUtil.readFromFile(item.Value);

                                if (image.Width > 1536 || image.Height > 1536)
                                {
                                    image = ImageUtil.ScaleImage(image, 1536, 1536);
                                }
                            }

                            isGoodImage += 1;
                            if (image != null)
                            {
                                image.Dispose();
                            }
                        }));
                    }

                    //提取人脸特征
                    for (int i = numStart; i < imagePathList.Count; i++)
                    {
                        ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
                        Image image = ImageUtil.readFromFile(imagePathList[i]);
                        if (image == null)
                        {
                            continue;
                        }
                        IntPtr feature = FaceUtil.ExtractFeature(pImageEngine, image, out singleFaceInfo);
                        this.Invoke(new Action(delegate
                        {
                            if (singleFaceInfo.faceRect.left == 0 && singleFaceInfo.faceRect.right == 0)
                            {
                                AppendText(string.Format("{0}号未检测到人脸\r\n", i));
                            }
                            else
                            {
                                AppendText(string.Format("已提取{0}张人脸特征值,[left:{1},right:{2},top:{3},bottom:{4},orient:{5}]\r\n", i, singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.right, singleFaceInfo.faceRect.top, singleFaceInfo.faceRect.bottom, singleFaceInfo.faceOrient));
                                string imagename = fileNames[i].Split('\\')[fileNames[i].Split('\\').Length - 1];
                                imagesFeatureList.Add(imagename, feature);
                                m_ComIniFace.IniWriteValue(M_Section, imagename, imagename + "|" + GetIntPtrInt(feature).ToString());
                            }
                        }));
                        if (image != null)
                        {
                            image.Dispose();
                        }
                    }

                   
                    Invoke(new Action(delegate
                        {
                            
                            m_ImageCount = imagePathListTemp.Count;
                            AppendText(string.Format("{0}条人脸特征提取完成", imagePathList.Count));                          
                            IsMainStartFace = true;
                        }));
                }));

            }
        }

Video组件Paint事件绘制 获取每一帧图片 计算出人脸后处理计算相似度

  /// <summary>
        /// RGB摄像头Paint事件,图像显示到窗体上,得到每一帧图像,并进行处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void videoSource_Paint(object sender, PaintEventArgs e)
        {
            if (rgbVideoSource.IsRunning)
            {
                //得到当前RGB摄像头下的图片
                Bitmap bitmap = rgbVideoSource.GetCurrentVideoFrame();
                if (bitmap == null)
                {
                    return;
                }
                //检测人脸,得到Rect框
                ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
                //得到最大人脸
                ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
                //得到Rect
                MRECT rect = maxFace.faceRect;
                //检测RGB摄像头下最大人脸
                Graphics g = e.Graphics;
                float offsetX = rgbVideoSource.Width * 1f / bitmap.Width;
                float offsetY = rgbVideoSource.Height * 1f / bitmap.Height;
                float x = rect.left * offsetX;
                float width = rect.right * offsetX - x;
                float y = rect.top * offsetY;
                float height = rect.bottom * offsetY - y;
                //根据Rect进行画框
                g.DrawRectangle(Pens.Red, x, y, width, height);
                if (trackRGBUnit.message != "" && x > 0 && y > 0)
                {
                    //将上一帧检测结果显示到页面上
                    g.DrawString(trackRGBUnit.message, font, trackRGBUnit.message.Contains("活体") ? blueBrush : yellowBrush, x, y - 15);
                }

                //保证只检测一帧,防止页面卡顿以及出现其他内存被占用情况
                if (isRGBLock == false)
                {
                    isRGBLock = true;
                    //异步处理提取特征值和比对,不然页面会比较卡
                    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                    {
                        if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)
                        {
                            try
                            {
                                lock (rectLock)
                                {
                                    allRect.left = (int)(rect.left * offsetX);
                                    allRect.top = (int)(rect.top * offsetY);
                                    allRect.right = (int)(rect.right * offsetX);
                                    allRect.bottom = (int)(rect.bottom * offsetY);
                                }

                                bool isLiveness = false;

                                //调整图片数据,非常重要
                                ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
                                if (imageInfo == null)
                                {
                                    return;
                                }
                                int retCode_Liveness = -1;
                                //RGB活体检测
                                ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness);
                                //判断检测结果
                                if (retCode_Liveness == 0 && liveInfo.num > 0)
                                {
                                    int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
                                    isLiveness = (isLive == 1) ? true : false;
                                }
                                if (imageInfo != null)
                                {
                                    MemoryUtil.Free(imageInfo.imgData);
                                }
                                if (isLiveness)
                                {
                                    //提取人脸特征
                                    IntPtr feature = FaceUtil.ExtractFeature(pVideoRGBImageEngine, bitmap, maxFace);
                                    float similarity = 0f;
                                    //得到比对结果
                                    int result = compareFeature(feature, out similarity);
                                    MemoryUtil.Free(feature);
                                    if (result > -1)
                                    {
                                        //将比对结果放到显示消息中,用于最新显示
                                        trackRGBUnit.message = string.Format(" {0}号 {1},{2}", result, similarity, string.Format("RGB{0}", isLiveness ? "活体" : "假体"));

                                    }
                                    else
                                    {
                                        //显示消息
                                        trackRGBUnit.message = string.Format("RGB{0}", isLiveness ? "活体" : "假体");
                                    }
                                }
                                else
                                {
                                    //显示消息
                                    trackRGBUnit.message = string.Format("RGB{0}", isLiveness ? "活体" : "假体");

                                }
                                SetUpdate();
                                if (isLiveness)
                                {
                                    matchBtn1();
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                            }
                            finally
                            {
                                if (bitmap != null)
                                {
                                    bitmap.Dispose();
                                }
                                isRGBLock = false;
                            }
                        }
                        else
                        {
                            lock (rectLock)
                            {
                                allRect.left = 0;
                                allRect.top = 0;
                                allRect.right = 0;
                                allRect.bottom = 0;
                                SetElecd(false);
                            }
                        }
                        isRGBLock = false;
                    }));
                }
            }
        }
 

人脸匹配事件

       /// <summary>
        /// 匹配事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        [HandleProcessCorruptedStateExceptions]
        private void matchBtn1()
        {
            try
            {
                List<string> strFace = ReadFaceIni();
                //   if (imagesFeatureList.Count == 0)
                if (strFace.Count == 0)
                //if(ImagearrayList.Count==0)
                {
                    AppendText("请注册人脸!");
                    Util_SpeakName("没有获取到人脸库信息");
                    //MessageBox.Show("请注册人脸!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                ChooseImg1(rgbVideoSource.GetCurrentVideoFrame());
                if (image1Feature == IntPtr.Zero)
                {
                    if (rgbVideoSource.GetCurrentVideoFrame() == null)
                    {
                        AppendText("未识别到人脸!");
                        Util_SpeakName("未识别到人脸");
                        //MessageBox.Show("请选择识别图!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        AppendText("验证失败,未识别图未提取到特征值!");
                        Util_SpeakName("验证失败未识别图未提取到特征值");
                        // MessageBox.Show("比对失败,识别图未提取到特征值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    return;
                }
                //标记已经做了匹配比对,在开启视频的时候要清除比对结果
                isCompare = true;
                float compareSimilarity = 0f;
                int compareNum = 0;
                //AppendText(string.Format("------------------------------开始比对,时间:{0}------------------------------\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
                //for (int i = 0; i < imagesFeatureList.Count; i++)
                //{
                //    IntPtr feature = imagesFeatureList[i];
                //    float similarity = 0f;
                //    int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref similarity);
                //    //增加异常值处理
                //    if (similarity.ToString().IndexOf("E") > -1)
                //    {
                //        similarity = 0f;
                //    }
                //    AppendText(string.Format("与{0}号比对结果:{1}\r\n", i, similarity));
                //    // imageList.Items[i].Text = string.Format("{0}号({1})", i, similarity);
                //    if (similarity > compareSimilarity)
                //    {
                //        compareSimilarity = similarity;
                //        compareNum = i;
                //    }
                //}
                int i = 0;
                Dictionary<string, float> dicFace = new Dictionary<string, float>();


                for (int FaceCount = strFace.Count; FaceCount >= 1; FaceCount--)
                // foreach (var item in imagesFeatureList)
                // foreach(MianImage item in ImagearrayList)
                {
                    // IntPtr feature = item.ImageInPtr;
                    string strFaceData = m_ComIniFace.IniReadValue(M_Section, strFace[FaceCount - 1]);
                    string[] FaceMain = strFaceData.Split('|');
                    IntPtr feature = GetIntPtr(Convert.ToInt32(FaceMain[1]));   //item.Value;
                    float similarity = 0f;
                    int ret = 0;
                    try
                    {
                        ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref similarity);
                    }
                    catch (Exception ex)
                    {

                    }
                    finally
                    {
                        GC.Collect();
                    }
                    
                    //增加异常值处理
                    if (similarity.ToString().IndexOf("E") > -1)
                    {
                        similarity = 0f;
                    }
                    //AppendText(string.Format("与{0}比对结果:{1}\r\n", item.Key, similarity));
                    // imageList.Items[i].Text = string.Format("{0}号({1})", i, similarity);
                    if (similarity > compareSimilarity)
                    {
                        compareSimilarity = similarity;
                        string strGuid = FaceMain[0].Split(')')[0].Replace("(", "").Trim();
                        dicFace.Add(strGuid + "|" + FaceMain[0], similarity);
                        compareNum = i;
                    }
                    i++;
                }

                if (compareSimilarity > 0.9f)
                {
                    foreach (var item in dicFace)
                    {
                        if (item.Value.Equals(compareSimilarity))
                        {

                            string strrun = "";
                            string strGUID = item.Key.Split('|')[0];
                            string IamgeFile = item.Key.Split('|')[1];
                            string strsql = string.Format("select * from UserTable where UserID='{0}'", strGUID);
                            DataSet _ds = null;
                            if (AppSettings.GetIsSqlLite())
                            {
                                _ds = new DataSet();
                                _ds.Tables.Add(manager.GetData(strGUID));
                            }
                            else
                            {
                                _ds = db.Execute(strsql, "UserTable", ref strrun);
                            }
                            if (_ds != null && _ds.Tables != null && _ds.Tables[0].DefaultView.Count > 0)
                            {
                                this.Invoke(new MethodInvoker(delegate ()
                                {
                                    AppendText("验证成功!");

                                    Util_SpeakName(string.Format("验证成功{0}欢迎回来", _ds.Tables[0].DefaultView[0]["UserName"].ToString()));

                                    SetElecd(true);
                                    lab_Name.Text = _ds.Tables[0].DefaultView[0]["UserName"].ToString();
                                    lab_Age.Text = _ds.Tables[0].DefaultView[0]["UserAge"].ToString();
                                    Lab_Dpt.Text = _ds.Tables[0].DefaultView[0]["UserDpt"].ToString();
                                    panImage.BackgroundImage = Image.FromFile(string.Format("{0}\\{1}", strImageFile, IamgeFile));
                                    SetUpdate();

                                }));

                            }







                            //lblCompareInfo.Text = " " + item.Key + "号," + compareSimilarity;
                        }
                    }





                }
                //AppendText(string.Format("------------------------------比对结束,时间:{0}------------------------------\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));


            }
            catch (Exception ex)
            {

            }
        }

最后实现:
在这里插入图片描述
第一次写文章 请高抬贵手,有不足之处请指点一二!!!
本期先到这里 下期再见!!!

以上是重要计算代码 需要源码请加博主微信:
在这里插入图片描述

  • 12
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、主要内容:OpenCV能够实现强大丰富的图像处理,但是它缺少一个能够支持它运行的界面。Csharp经过多年的发展,得益于它的“所见及所得”能力,非常方便编写界面。这两者如果能够“双剑合璧”,将有效帮助实际工作产出。本课着重推荐GOCW采用“Csharp基于CLR直接调用Opencv编写的算法库”方法,能够将最新的OpenCV技术引入进来,同时保证生成程序的最小化。    为了进一步说明Csharp和OpenCV的结合使用,首先一个较为完整的基于winform实现答题卡识别的例子,相比较之前的实现,本次进一步贴近生产实际、内涵丰富,对算法也进行了进一步提炼。同时我们对WPF下对OpenCV函数的调用、OpenCV.js的调用进行相关教授。       二、课程结构1、 EmguCV、OpenCVSharp和GOCW之间进行比较(方便代码编写、能够融入最新的算法、速度有保障、方便调试找错、拒绝黑箱化);2、视频采集模块的构建,视频采集和图像处理之间的关系;3、视频采集专用的SDK和“陪练”系统的介绍;4、在视频增强类项目中和图像处理项目中,算法的选择;5、Csharp界面设计、图片的存储和其他构建设计;6、较为完整的答题卡识别例子,兼顾界面设计和算法分析;8、WPF基于GOCW也同样可以基于GOCW实现算法调用;webForm虽然也可以通过类似方法调用,但是OpenCV.JS的方法更现代高效。9、关于软件部署的相关要点和窍门。       三、知识要点:1、基本环境构建和程序框架;2、CLR基本原理和应用方法;3、接入、采集、模拟输入;4、图像处理,通过构建循环采集图片;5、增强和实时处理;6、基于投影等技术的答题卡识别算法;7、存储、转换;8、部署交付。        课程能够帮助你掌握Csharp调用Opencv的基本方法,获得相应框架代码和指导;从而进一步提升实现“基于图像处理”的解决方案能力。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值