dlib人脸比对程序设计(C++)

10 篇文章 1 订阅

VS2017工程代码链接如下:


// face_mfcDlg.cpp: 实现文件
//

#include "stdafx.h"
#include "face_mfc.h"
#include "face_mfcDlg.h"
#include "afxdialogex.h"
#include <cmath>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CfacemfcDlg 对话框



CfacemfcDlg::CfacemfcDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_FACE_MFC_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CfacemfcDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_STATIC_RESULT, m_regResult);
}

BEGIN_MESSAGE_MAP(CfacemfcDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_open, &CfacemfcDlg::OnBnClickedopen)
	ON_BN_CLICKED(IDC_colse, &CfacemfcDlg::OnBnClickedcolse)
	ON_BN_CLICKED(IDC_BTN_GETIMAGE, &CfacemfcDlg::OnBnClickedBtnGetimage)
	ON_BN_CLICKED(IDC_BTN_DETECT, &CfacemfcDlg::OnBnClickedBtnDetect)
	ON_BN_CLICKED(IDC_BTN_FACEALG, &CfacemfcDlg::OnBnClickedBtnFacealg)
	ON_BN_CLICKED(IDC_BTN_FACEALIGN, &CfacemfcDlg::OnBnClickedBtnFacealign)
	ON_BN_CLICKED(IDC_BTN_FACETRACE, &CfacemfcDlg::OnBnClickedBtnFacetrace)
	ON_BN_CLICKED(IDC_BTN_FACECMP, &CfacemfcDlg::OnBnClickedBtnFacecmp)
END_MESSAGE_MAP()


// CfacemfcDlg 消息处理程序

BOOL CfacemfcDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	cv::namedWindow("view", cv::WINDOW_AUTOSIZE);
	//HWND,h 是类型描述,表示句柄(handle), Wnd 是变量对象描述,表示窗口,所以hWnd 表示窗口句柄。hWnd 属性,返回窗体或控件的句柄
	HWND hWnd = (HWND)cvGetWindowHandle("view");
	HWND hParent = ::GetParent(hWnd);//获得一个指定子窗口的父窗口句柄
	::SetParent(hWnd, GetDlgItem(IDC_PIC)->m_hWnd);//GetDlgItem返回窗口中指定参数ID的子元素的句柄,可以通过返回的句柄对窗口内的子元素进行操作
	//m_hWnd 指明与这个cwnd对象相关联的hwnd句柄
	//HWND SetParent(//测试的窗口句柄
//               HWND hWndChild,      // handle to window
//               //新父窗口句柄
//               HWND hWndNewParent   // new parent window
//               );
	::ShowWindow(hParent, SW_HIDE);//SW_HIDE	隐藏窗口并激活其他窗口

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CfacemfcDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CfacemfcDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CfacemfcDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CfacemfcDlg::OnBnClickedopen()
{
	// TODO: 在此添加控件通知处理程序代码
	m_flag = 1;
	CRect rect;
	//GetDlgItem,是根据继承关系的函数功能,返回窗口中指定参数ID的子元素的句柄,可以通过返回的句柄对窗口内的子元素进行操作
	CWnd *pWnd = GetDlgItem(IDC_PIC);
	pWnd->GetClientRect(&rect);//该函数获取窗口客户区的大小。注意一下:窗口的客户区为窗口中除标题栏、菜单栏之外的地方。
	int x = rect.Width();
	int y = rect.Height();
	m_capture.release();//释放鼠标的捕获状态
	m_capture = VideoCapture(0);
	if (!m_capture.isOpened())
	{
		fprintf(stderr, "Can not open camera.\n");//stderr --标准错误,是不带缓冲的
		return;
	}
	frontal_face_detector detector = get_frontal_face_detector();//正向人脸检测器,进行人脸检测
	shape_predictor pose_model;
	// shape_predictor("shape_predictor_68_face_landmarks.dat") /人脸 68 点特征检测器,进行人脸面部轮廓特征提取: 
	deserialize("E:\\project\\tb\\tb1\\model\\shape_predictor_68_face_landmarks.dat") >> pose_model;
	bool bFirst = 1;
	while (m_flag)
	{
		cv::Mat frame;
		m_capture >> frame;
		//fx、fy是沿x轴和y轴的缩放系数,Size dsize 输出图像的大小// m_dst输出图像
		//cv::resize(frame, m_dst, cv::Size(x, y), 0, 0, 1);  //CV_INTER_LINEAR
		cv::Mat image_fliped;
		cv::flip(frame, image_fliped, 1);
		frame = image_fliped;
		cv_image<bgr_pixel>cimg(frame);//Mat to something dlib can deal with
		//01-目标跟踪
		if (m_btrack)
		{
			//注意:使用时注释掉下面一行
			image_window m_win;

			if (bFirst)
			{
				m_tracker.start_track(cimg, centered_rect(point(200, 110), 86, 86));
				m_win.set_image(cimg);
				m_win.add_overlay(m_tracker.get_position());
				bFirst = 0;
			}
			m_tracker.update(cimg);
			m_win.set_image(cimg);
			m_win.clear_overlay();//Remove all overlays from the image_window.
			m_win.add_overlay(m_tracker.get_position());
		}
		//02-人脸检测
		if (m_bdetect)//人脸检测
		{
			std::vector<dlib::rectangle> faces = detector(cimg);//detector()函数检测人脸,返回一系列边界盒子
			std::vector<full_object_detection>shapes;
			for (unsigned long i = 0; i < faces.size(); ++i)
			{//人脸检测,并绘制矩形框
				if (m_brectangle)
				{
					cv::rectangle(frame, Rect(faces[i].left(), faces[i].top(), faces[i].width(), faces[i].height()), Scalar(0, 0, 255), 1, 1, 0);
				}
				shapes.push_back(pose_model(cimg, faces[i]));
			}
			if (!shapes.empty())
			{
				for (int i = 0; i < 68; i++)
				{
					//cv::rectangle(frame, Rect(100, 300, 20, 200), Scalar(255, 0, 0), 1, 1, 0);
					if (!m_brectangle)
					{//显示特征点
						cv::circle(frame, cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), 3, cv::Scalar(0, 0, 255), -1);
						//	shapes[0].part(i).x();//68个
						// zhanzl:显示特征点的数字
						//putText(frame, to_string(i), cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), CV_FONT_HERSHEY_PLAIN, 1, cv::Scalar(255, 0, 0), 1, 4);
					}
				}
			}
		}
		cv::imshow("view", frame);//m_dst
		cv::waitKey(3);
	}
}

void CfacemfcDlg::OnBnClickedcolse()
{
	// TODO: 在此添加控件通知处理程序代码
	m_flag = 0;
	CDialogEx::OnClose();
}


void CfacemfcDlg::OnBnClickedBtnGetimage()
{
	// TODO: 在此添加控件通知处理程序代码
	string picpath = "E:\\project\\tb2\\face_mfc\\face_mfc\\data\\002.jpg";
	imwrite(picpath, m_dst);
	Mat image = imread(picpath);
	//Mat  imagedst;
	CWnd *pWnd = GetDlgItem(IDC_IMAGE);
	CDC *pDC = NULL;
	//CString strPath;
	((CStatic*)GetDlgItem(IDC_IMAGE))->ModifyStyle(0xF, SS_BITMAP | SS_CENTERIMAGE);//SS_BITMAP这里把静态控件弄为灰白色了,不要它又直接输出caption
	//GetDC 函数:获取设备指针,函数功能描述:该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文的句柄.
	//GetDlgItem(ID) 通过资源ID获取对话框内控件的指针,//CDC*pDC=GetDlgItem(ID)->GetDC(); 获取某控件的设备绘图指针
	pDC = GetDlgItem(IDC_IMAGE)->GetDC();
	ShowImage(pDC, picpath, 0, 0);
	// 方法说明:    显示JPG和GIF、BMP图片
	// 参数说明:    CDC * pDC           设备环境对象
	// 参数说明:    CString strPath     要显示的图片路径
	// 参数说明:    int x               要显示的X位置
	// 参数说明:    int y               要显示的Y位置
	// 返回值:      BOOL                成功返回TRUE,否则返回FALSE
	ReleaseDC(pDC);//释放资源
}

//显示图片
BOOL CfacemfcDlg::ShowImage(CDC* pDC, string strPath, int x, int y)
{
	IPicture *pPic = NULL;
	OleLoadPicturePath(CComBSTR(strPath.c_str()), (LPUNKNOWN)NULL, 0, 0, IID_IPicture, (LPVOID*)&pPic);
	if (NULL == pPic)
	{
		return FALSE;
	}
	// 宽度+高度
	OLE_XSIZE_HIMETRIC hmWidth;
	OLE_YSIZE_HIMETRIC hmHeight;
	pPic->get_Width(&hmWidth);
	pPic->get_Height(&hmHeight);
	//宽度和高度  
	RECT rtWnd;
	pDC->GetWindow()->GetWindowRect(&rtWnd);
	int iWndWidth = rtWnd.right - rtWnd.left;
	int iWndHeight = rtWnd.bottom - rtWnd.top;
	//pPic->Render(*pDC, x, y, iWndWidth, iWndHeight, 0, hmHeight, hmWidth, -hmHeight, NULL);
	if (FAILED(pPic->Render(*pDC, x, y, iWndWidth, iWndHeight, 0, hmHeight, hmWidth, -hmHeight, NULL)))
	{
		pPic->Release();
		return false;
	}
	//释放资源
	pPic->Release();
	return true;
}

BOOL CfacemfcDlg::ShowImageRight(string strPath, int x, int y)
{
	//
	//strPath = "d:\\001.jpg";
	CWnd *pWnd = GetDlgItem(IDC_IMAGE);
	//showMatImgToWnd(pWnd,image);
	CDC *pDC = NULL;
	//设置静态控件的样式,使其可以使用位图,并使位图显示居中  
	((CStatic*)GetDlgItem(IDC_IMAGE))->ModifyStyle(0xF, SS_BITMAP | SS_CENTERIMAGE);

	pDC = GetDlgItem(IDC_IMAGE)->GetDC();
	ShowImage(pDC, strPath, 0, 0);


	//
	IPicture *pPic = NULL;
	OleLoadPicturePath(CComBSTR(strPath.c_str()), (LPUNKNOWN)NULL, 0, 0, IID_IPicture, (LPVOID*)&pPic);
	if (NULL == pPic)
	{
		return FALSE;
	}
	// 宽度+高度
	OLE_XSIZE_HIMETRIC hmWidth;
	OLE_YSIZE_HIMETRIC hmHeight;
	pPic->get_Width(&hmWidth);
	pPic->get_Height(&hmHeight);
	//宽度和高度  
	RECT rtWnd;
	pDC->GetWindow()->GetWindowRect(&rtWnd);
	int iWndWidth = rtWnd.right - rtWnd.left;
	int iWndHeight = rtWnd.bottom - rtWnd.top;

	if (FAILED(pPic->Render(*pDC, x, y, iWndWidth, iWndHeight, 0, hmHeight, hmWidth, -hmHeight, NULL)))
	{
		pPic->Release();
		return false;
	}
	//释放资源
	pPic->Release();

	ReleaseDC(pDC); //释放资源
	return true;

}

//显示图片-左侧
BOOL CfacemfcDlg::ShowImageLeft(string strPath, int x, int y)
{

	//strPath = "E:\\face_mfc\\data\\002.jpg";
	CWnd *pWnd = GetDlgItem(IDC_PIC);
	//showMatImgToWnd(pWnd,image);
	CDC *pDC = NULL;
	//设置静态控件的样式,使其可以使用位图,并使位图显示居中  
	((CStatic*)GetDlgItem(IDC_PIC))->ModifyStyle(0xF, SS_BITMAP | SS_CENTERIMAGE);

	pDC = GetDlgItem(IDC_PIC)->GetDC();
	ShowImage(pDC, strPath, 0, 0);


	//
	IPicture *pPic = NULL;
	OleLoadPicturePath(CComBSTR(strPath.c_str()), (LPUNKNOWN)NULL, 0, 0, IID_IPicture, (LPVOID*)&pPic);
	if (NULL == pPic)
	{
		return FALSE;
	}
	// 宽度+高度
	OLE_XSIZE_HIMETRIC hmWidth;
	OLE_YSIZE_HIMETRIC hmHeight;
	pPic->get_Width(&hmWidth);
	pPic->get_Height(&hmHeight);
	//宽度和高度  
	RECT rtWnd;
	pDC->GetWindow()->GetWindowRect(&rtWnd);
	int iWndWidth = rtWnd.right - rtWnd.left;
	int iWndHeight = rtWnd.bottom - rtWnd.top;

	if (FAILED(pPic->Render(*pDC, x, y, iWndWidth, iWndHeight, 0, hmHeight, hmWidth, -hmHeight, NULL)))
	{
		pPic->Release();
		return false;
	}
	//释放资源
	pPic->Release();

	ReleaseDC(pDC); //释放资源
	return true;


}

//人脸检测:画出矩形框,针对照片
void CfacemfcDlg::OnBnClickedBtnDetect()
{
	// TODO: 在此添加控件通知处理程序代码
	m_bdetect = 1;//人脸检测
	if (m_brectangle)//绘制矩形框
	{
		m_brectangle = 0;
	}
	else
	{
		m_brectangle = 1;//人脸检测,并绘制矩形框
	}

}


void CfacemfcDlg::OnBnClickedBtnFacealg()
{
	// TODO: 在此添加控件通知处理程序代码
	if (m_bdetect)
	{
		m_bdetect = 0;
	}
	else
	{
		m_bdetect = 1;//人脸检测
	}
}


void CfacemfcDlg::OnBnClickedBtnFacealign()
{
	// TODO: 在此添加控件通知处理程序代码
	//图片文件路径
	string filePath = "E:\\project\\tb2\\face_mfc\\face_mfc\\data\\002.jpg";
	//01-导入一张图片
	ShowImageLeft(filePath, 0, 0);
	//array2d<rgb_pixel> img;   //特别注意:转换后会失真
	//bgr_pixel
	array2d<bgr_pixel> img;
	load_image(img, filePath);
	pyramid_up(img);// Make the image larger so we can detect small faces.

	//02:初始化环境
	frontal_face_detector detector = get_frontal_face_detector();
	shape_predictor pose_model;
	deserialize("E:\\project\\tb\\tb1\\model\\shape_predictor_68_face_landmarks.dat") >> pose_model;

	//03-人脸检测
	std::vector<dlib::rectangle> faces = detector(img);

	// Now we will go ask the shape_predictor to tell us the pose of each face we detected
	std::vector<full_object_detection> shapes;
	for (unsigned long i = 0; i < faces.size(); ++i) {
		//人脸检测,并绘制矩形框 
		// Key1:注意转换失真问题:bgr_pixel
		cv::Mat img2 = dlib::toMat(img);
		cv::rectangle(img2, Rect(faces[i].left(), faces[i].top(), faces[i].width(), faces[i].height()), Scalar(0, 0, 255), 1, 1, 0);
		shapes.push_back(pose_model(img, faces[i]));//函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
	}
	//04-特征点标定:68	
/*	if (!shapes.empty()) {
		for (int i = 0; i < 68; i++) {
			cv::Mat img2 = dlib::toMat(img);
			//cv::rectangle(frame, Rect(100, 300, 20, 200), Scalar(255, 0, 0), 1, 1, 0);
			cv::circle(img2, cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), 3, cv::Scalar(0, 0, 255), -1);
			//shapes[0].part(i).x();//68个
			// zhanzl:显示特征点的数字
			putText(img2, to_string(i), cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), CV_FONT_HERSHEY_PLAIN, 1, cv::Scalar(255, 0, 0), 1, 4);
		}
	}*/

	//05-人脸对齐:face_chips为对齐后的目标
	dlib::array<array2d<bgr_pixel> > face_chips;
	dlib::extract_image_chips(img, get_face_chip_details(shapes), face_chips);
	for (size_t i = 0; i < face_chips.size(); i++)
	{
		cv::Mat img = dlib::toMat(face_chips[i]);
		string picpath;
		stringstream stream;
		stream << i;
		picpath = stream.str();
		picpath += ".jpg";
		picpath = "e://" + picpath;
		imwrite(picpath, img);
		//显示在控件上
		ShowImageRight(picpath, 0, 0);
	}
}


void CfacemfcDlg::OnBnClickedBtnFacetrace()
{
	// TODO: 在此添加控件通知处理程序代码
	/*
	//01-目标跟踪
	//图片文件路径
	string filePath = "d://001.jpg";
	//01-导入一张图片
	ShowImageLeft(filePath, 0, 0);
	//array2d<rgb_pixel> img;   //特别注意:转换后会失真
	//bgr_pixel
	array2d<bgr_pixel> img;
	load_image(img, filePath);
	pyramid_up(img);


	correlation_tracker tracker;
	tracker.start_track(img, centered_rect(point(93, 110), 60, 86));
	tracker.update(img);
	win.set_image(img);
	win.clear_overlay();
	win.add_overlay(tracker.get_position());
	*/
	if (m_btrack)
	{
		m_btrack = 0;
	}
	else
	{
		m_btrack = 1;
	}
}


void CfacemfcDlg::OnBnClickedBtnFacecmp()
{
	// TODO: 在此添加控件通知处理程序代码
	//01-初始化检测器+图片
	frontal_face_detector  detector = get_frontal_face_detector();
	//导入模型:特征点标定 
	shape_predictor sp;//关键点预测器
	deserialize("E:\\project\\tb\\tb1\\model\\shape_predictor_68_face_landmarks.dat") >> sp;
	//导入模型:人脸识别
	anet_type net;
	//dlib_face_recognition_resnet_model_v1.dat
	deserialize("E:\\project\\tb\\tb1\\model\\dlib_face_recognition_resnet_model_v1.dat") >> net;
	string path1 = "E:\\project\\tb2\\face_mfc\\face_mfc\\data\\kebi.jpg";
	string path2 = "E:\\project\\tb2\\face_mfc\\face_mfc\\data\\qiaodan.jpg";
	matrix<rgb_pixel> img1;//使用参数用matrix或array2d来表示图像
	load_image(img1, path1);//load_image方法读取图像

	matrix<rgb_pixel> img2;
	load_image(img2, path2);

	ShowImageLeft(path1,0,0);
	ShowImageRight(path2,0,0);
	//02-特征点对齐
	std::vector<matrix<rgb_pixel>> faces1;
	std::vector<matrix<rgb_pixel>> faces2;
	//03-人脸检测img1:
	//注意:检测多张人脸时,会触发中断
	for (auto face : detector(img1))
	{
		//001-特征点检测
		auto shape = sp(img1, face);
		//0002-特征点对齐
		matrix<rgb_pixel> face_chip;
		extract_image_chip(img1, get_face_chip_details(shape, 150, 0.25), face_chip);
		//003-特征点存储(对齐后的点)
		faces1.push_back(move(face_chip));
	}
	if (faces1.size()==0)
	{
		cout << "No faces found in image!" << endl;
		//return 1;	
	}
	//03-人脸检测img2:
	//注意:检测多张人脸时,会触发中断
	for (auto face : detector(img2)) 
	{
		//001-特征点检测
		auto shape = sp(img2, face);
		//0002-特征点对齐
		matrix<rgb_pixel> face_chip;
		extract_image_chip(img2, get_face_chip_details(shape, 150, 0.25), face_chip);
		//003-特征点存储(对齐后的点)
		faces2.push_back(move(face_chip));
    }
	if (faces2.size() == 0) 
	{
		cout << "No faces found in image!" << endl;
		//return 1;	
	}
    //04-人脸特征向量化
	std::vector<matrix<float, 0, 1>> face_descriptors1 = net(faces1);
	std::vector<matrix<float, 0, 1>> face_descriptors2 = net(faces2);
	//05-距离计算
	float f = length(face_descriptors1[0] - face_descriptors2[0]);
	CString str;
	str.Format("欧式距离:%.2f ", f);
	m_regResult.SetWindowText(str);
	//AfxMessageBox(str);

}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值