OpenCV重写冈萨雷斯——灰度变换、直方图、反向投影

本文介绍了OpenCV中的一些基本灰度变换,包括图像反转、对数变换和幂律(Gamma)变换。接着深入探讨了直方图处理,如计算直方图、H-S直方图、直方图均衡化和反向投影。直方图匹配用于调整图像对比度,并通过实例展示了直方图均衡在过曝图片上的应用。最后提到了局部直方图均衡和反向投影匹配的概念。
摘要由CSDN通过智能技术生成

一些基本的灰度变换函数

图像反转

//图像翻转 s = L - 1 - r
//使用书中的图片直接反转会产生错误 只会反转左侧少部分,
//注:在给Mat矩阵中像素位置赋值的时候 要先初始化Mat矩阵各个位置像素值,否则会报异常
//    应该养成定义矩阵的时候就将其像素值初始化。
void ImageInvert()
{
	Mat src;
	Mat src1, src2, src3;
	Mat merge[] = {src1, src2, src3};

	//这个tif格式文件明明只有一个通道 但是opencv函数计算却是三个通道 但是对三个通道进行操作 原因未知
	//src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0304(a)(breast_digital_Xray).tif",0);
	//这个jpg为三通道
	src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\1.jpg");
	int channels = src.channels();
	imshow("src", src);

	if (channels == 3)
	{
		cv:; split(src, merge);
	}
	else if (channels == 1)
	{
		merge[0] = src;
	}
	for (int i = 0; i < src.rows; i++)
	for (int j = 0; j < src.cols; j++)
	{
		if (channels == 1)
			merge[0].at<uchar>(i, j) = 255 - src.at<uchar>(i, j);
		else if (channels == 3)
		{
			merge[0].at<uchar>(i,j) = 255 - src.at<Vec3b>(i, j)[0];
			merge[1].at<uchar>(i, j) = 255 - src.at<Vec3b>(i, j)[1];
			merge[2].at<uchar>(i, j) = 255 - src.at<Vec3b>(i, j)[2];
		}
	}
	if (channels == 1)
	{
		imshow("dst1", merge[0]);
	}
	else if (channels == 3)
	{
		cv::merge(merge, 3, src);
		imshow("dst1",src);
	}
}

在这里插入图片描述

对数变换

//对数变换  s = clog(1+r)  对数变换会让低灰度动态范围变大 反对数变换 会让高灰度动态范围变大
//对数变换需要归一化 log(255) = 2.5.... 
//flag = true 对数变换 flag = false 反对数变换
void ImageLog(bool flag)
{
	Mat src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0305(a)(DFT_no_log).tif",0);
	Mat dst = src.clone();
	if (flag)
	for (int i = 0; i < src.rows;i++)
	for (int j = 0; j < src.cols; j++)
	{
		dst.at<uchar>(i, j) = 1 * log( 1 + src.at<uchar>(i, j))/log(256)*255;
	}
	else
	for (int i = 0; i < src.rows; i++)
	for (int j = 0; j < src.cols; j++)
	{
		dst.at<uchar>(i, j) = (pow(src.at<uchar>(i, j) / 1,2) - 1) / (pow(255,2) - 1) * 255;
	}
	imshow("src", src);
	imshow("dst", dst);
}
  • 对数变换 暗区域灰度变化范围变大
    在这里插入图片描述
  • 反对数变换 亮区域灰度变化范围变大
    在这里插入图片描述

幂律(Gamma)变换

//幂律变换 Gamma > 1 亮区域灰度动态范围扩大, Gamma < 1 暗区域灰度动态范围扩大
void ImageGamma(float Gamma)
{
	//Fig0305(a)(DFT_no_log).tif
	//Fig0307(a)(intensity_ramp).tif
	//Fig0308(a)(fractured_spine).tif
	//Fig0309(a)(washed_out_aerial_image).tif
	Mat src = imread("E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\Fig0309(a)(washed_out_aerial_image).tif", 0);
	Mat dst = src.clone();
	unsigned char LUT[256];
	for (int i = 0; i < 256; i++)
	{
		LUT[i] = cv::saturate_cast<uchar>(pow((float)(i/255.0), Gamma)*255.0);
	}
	MatIterator_<uchar>iterator = dst.begin<uchar>();
	MatIterator_<uchar>iteratorEnd = dst.end<uchar>();
	while (iterator != iteratorEnd)
	{
		*iterator = LUT[*iterator];
		iterator++;
	}
	
	imshow("src", src);
	imshow("dst", dst);
}
  • Gamma < 0.3 暗区域灰度变换变大
    在这里插入图片描述

  • Gamma > 1 亮区域活动范围变大

  • 在这里插入图片描述

直方图处理

计算直方图

//显示一副图像的直方图
/*
C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )

参数详解:

onst Mat* images:输入图像

 int nimages:输入图像的个数

 const int* channels:需要统计直方图的第几通道

 InputArray mask:掩膜,,计算掩膜内的直方图  ...Mat()

 OutputArray hist:输出的直方图数组

 int dims:需要统计直方图通道的个数

 const int* histSize:指的是直方图分成多少个区间,就是 bin的个数

 const float** ranges: 统计像素值得区间

 bool uniform=true::是否对得到的直方图数组进行归一化处理

 bool accumulate=false:在多个图像时,是否累计计算像素值得个数
*/
/*
normalize 归一化
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。

NORM_INF:此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)

NORM_L1 :  归一化数组的L1-范数(绝对值的和)

NORM_L2: 归一化数组的(欧几里德)L2-范数
*/
void HistCalc()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat src1 = imread(st + "Fig0316(1)(top_left).tif",0);
	Mat src2 = imread(st + "Fig0316(2)(2nd_from_top).tif",0);
	Mat src3 = imread(st + "Fig0316(3)(third_from_top).tif",0);
	Mat src4 = imread(st + "Fig0316(4)(bottom_left).tif",0);
	MatND hist1, hist2, hist3, hist4;
	imshow("src1", src1);
	imshow("src2", src2);
	imshow("src3", src3);
	imshow("src4", src4);
	const int channles[1] = { 0 };
	const int histSize[1] = { 256 };
	float range[2] = { 0, 255 };
	const float *ranges[1] = {range};
	calcHist(&src1, 1,channles, Mat(), hist1, 1,histSize, ranges);
	normalize(hist1, hist1, 0, 255, NORM_MINMAX);
	calcHist(&src2, 1, channles, Mat(), hist2, 1, histSize, ranges);
	normalize(hist2, hist2, 0, 255, NORM_MINMAX);
	calcHist(&src3, 1, channles, Mat(), hist3, 1, histSize, ranges);
	normalize(hist3, hist3, 0, 255, NORM_MINMAX);
	calcHist(&src4, 1, channles, Mat(), hist4, 1, histSize, ranges);
	normalize(hist4, hist4, 0, 255, NORM_MINMAX);

	Mat maphist2 = Mat::zeros(Size(256, 256), CV_8UC1), maphist3 = Mat::zeros(Size(256, 256), CV_8UC1), maphist4 = Mat::zeros(Size(256, 256), CV_8UC1), maphist1 = Mat::zeros(Size(256, 256), CV_8UC1);
	for (int i = 1; i < 256; i++)
	{
		line(maphist1, Point(i - 1, 255 - hist1.at<float>(i-1)),   Point(i, 255 - hist1.at<float>(i)), Scalar(255));
		line(maphist2, Point(i - 1, 255 - hist2.at<float>(i - 1)), Point(i, 255 - hist2.at<float>(i)), Scalar(255));
		line(maphist3, Point(i - 1, 255 - hist3.at<float>(i - 1)), Point(i, 255 - hist3.at<float>(i)), Scalar(255));
		line(maphist4, Point(i - 1, 255 - hist4.at<float>(i - 1)), Point(i, 255 - hist4.at<float>(i)), Scalar(255));
	}
	imshow("Hist1", maphist1);
	imshow("Hist2", maphist2);
	imshow("Hist3", maphist3);
	imshow("Hist4", maphist4);
}

在这里插入图片描述

计算 H-S 直方图

//H-S直方图
//OpenCV中HSV 中H分量范围是0-180 S范围是0-255 V范围是0-255
void HSHist()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat src1 = imread(st + "1.jpg");
	Mat src2 = imread(st + "2.tif");
	Mat dst1 = src1.clone(),dst2 = src2.clone();
	Mat HShist1(362, 512, CV_8UC1, Scalar(0)), HShist2(362, 512, CV_8UC1, Scalar(0));
	MatND hist1,hist2;

	cvtColor(src1, dst1, CV_BGR2HSV);
	cvtColor(src2, dst2, CV_BGR2HSV);
	const int channles[2] = {0,1};
	const int histSize[] = { 180, 255 };
	float range1[] = { 0, 180 }, range2[] = { 0, 255 };
	const float* ranges[] = { range1, range2 };
	calcHist(&dst1, 1, channles, Mat(), hist1, 2, histSize, ranges);
	calcHist(&dst2, 1, channles, Mat(), hist2, 2, histSize, ranges);
	normalize(hist1, hist1, 0, 255, NORM_MINMAX);
	normalize(hist2, hist2, 0, 255, NORM_MINMAX);
	for (int i = 0; i < 255;i++)
	for (int j = 0; j < 180; j++)
	{
		rectangle(HShist1, Point(i * 2, j * 2), Point(i * 2 + 2, j * 2 + 2), Scalar(hist1.at<float>(j,i)));
		rectangle(HShist2, Point(i * 2, j * 2), Point(i * 2 + 2, j * 2 + 2), Scalar(hist2.at<float>(j,i)));
	}


	imshow("src1", src1);
	imshow("src2", src2);
	imshow("HShist1", HShist1);
	imshow("HShist2", HShist2);
}

在这里插入图片描述

断点 查看 H-S直方图如何生成

//用于观察HS图像是如何形成的 H为行坐标 S为列坐标 组成三维图
void HStest()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat src = imread(st + "1.jpg");
	Mat dst = src.clone();
	cvtColor(src, dst, CV_BGR2HSV);
	Mat mats[] = { Mat(dst.size(), CV_8UC1, Scalar(0)), Mat(dst.size(), CV_8UC1, Scalar(0)), Mat(dst.size(), CV_8UC1, Scalar(0)) };
	split(dst, mats);
	Mat H = mats[0];
	Mat S = mats[1];
	Mat V = mats[2];
	Mat histS,histH,histHS;
	const int channles[] = { 0};
	const int histSize[] = { 180 };
	float range1[] = { 0, 180 };
	const float* ranges[] = { range1 };
	calcHist(&H, 1, channles, Mat(), histH, 1, histSize, ranges);

	double maxH, maxS, minH, minS, minHS, maxHS;

	const int histSize1[] = { 255 };
	float range11[] = { 0, 255 };
	const float* ranges1[] = { range11 };
	calcHist(&S, 1, channles, Mat(), histS, 1, histSize1, ranges1);

	const int channles2[] = { 0,1};
	const int histSize2[] = { 180,255 };
	float range2[] = { 0, 180 },range3[] = { 0, 255 };
	const float* ranges2[] = { range2 ,range3};
	calcHist(&dst, 1, channles2, Mat(), histHS, 2, histSize2, ranges2);

	minMaxLoc(histH, &minH, &maxH);
	minMaxLoc(histS, &minS, &maxS);
	minMaxLoc(histHS, &minHS, &maxHS);
}

直方图均衡

void EquHist()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat src = imread(st + "Fig0354(a)(einstein_orig).tif", 0);
	Mat AfterEquHist(src.rows, src.cols, CV_8UC1, Scalar(0));
	MatND hist,hist1;
	const int channles[] = { 0 };
	const int histSize[] = { 256 };
	float range[] = { 0, 255 };
	const float* ranges[] = {range};
	//const int 
	calcHist(&src,1, channles, Mat(), hist,1, histSize, ranges);
	normalize(hist, hist, 0, 255, NORM_MINMAX);
	Mat maphist1(256,256,CV_8UC1,Scalar(0));
	for (int i = 1; i < 256; i++)
	{
		line(maphist1, Point(i - 1, 255 - hist.at<float>(i - 1)), Point(i, 255 - hist.at<float>(i)), Scalar(255));
	}

	equalizeHist(src, AfterEquHist);


	calcHist(&AfterEquHist, 1, channles, Mat(), hist1, 1, histSize, ranges);
	normalize(hist1, hist1, 0, 255, NORM_MINMAX);
	Mat maphist2(256, 256, CV_8UC1, Scalar(0));
	for (int i = 1; i < 256; i++)
	{
		line(maphist2, Point(i - 1, 255 - hist1.at<float>(i - 1)), Point(i, 255 - hist1.at<float>(i)), Scalar(255));
	}


	imshow("src",src);
	imshow("hist", maphist1);
	imshow("AfterEquHist", AfterEquHist);
	imshow("hist1", maphist2);
}

在这里插入图片描述

类直方图均衡化

///直接形象的归一化但是与公式不同
//类似归一化函数 拓展到0-255
void EquHistNormalize()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat grayImage = imread(st + "Fig0354(a)(einstein_orig).tif", 0);
	Mat dst = grayImage.clone();
	MatND hist;
	const int channles[1] = { 0 };
	const int histSize[1] = { 256 };
	float range[2] = { 0, 255 };
	const float* ranges[1] = { range };
	calcHist(&grayImage, 1, channles,Mat(), hist, 1, histSize, ranges);

	int num = 100;
	int minvalue, maxvalue;
	for (minvalue = 0; minvalue < hist.rows; minvalue++)
	{
		if (hist.at<float>(minvalue) > num)
			break;
	}
	for (maxvalue = hist.rows; maxvalue < 0; maxvalue--)
	{
		if (hist.at<float>(maxvalue) > num)
			break;
	}

	Mat LUTlist(1, 256, CV_8UC1, Scalar(0));
	for (int i = 0; i < 256; i++)
	{
		if (i<minvalue)
			LUTlist.at<uchar>(i) = 0;
		else if(i>maxvalue)
			LUTlist.at<uchar>(i) = 255;
		else
			LUTlist.at<uchar>(i) = 255*(i-minvalue)/(maxvalue-minvalue);
	}
	LUT(grayImage, LUTlist, dst);

	imshow("原图",grayImage);
	imshow("直方图均衡",dst);
}

在这里插入图片描述

直方图均衡 与公式匹配

//从公式本质出发实现
void EquHistMath()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat grayImage = imread(st + "Fig0316(1)(top_left).tif", 0);
	Mat dst = grayImage.clone();
	MatND hist;
	const int channles[1] = { 0 };
	const int histSize[1] = { 256 };
	float range[2] = {0,256};
	const float* ranges[] = { range };
	calcHist(&grayImage, 1, channles, Mat(),hist, 1, histSize, ranges);

	Mat LUTlist(1,256,CV_8UC1,Scalar(0));
	int tmp = 0;
	for (int i = 0; i < hist.rows; i++)
	{
		tmp += hist.at<float>(i);
		LUTlist.at<uchar>(i) = static_cast<uchar>(tmp * 255 / (grayImage.rows*grayImage.cols));
	}
	LUT(grayImage, LUTlist, dst);
	imshow("src",grayImage);
	imshow("dst",dst);

}

在这里插入图片描述

直方图匹配

//进行直方图匹配操作
//按照直方图规定的公式来看 直方图规定化操作不一定需要两个相同尺寸的图片 只需要一个规定的直方图累积概率分布
void HistMatch()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";

	//加载原图像
	Mat srcImage = imread(st + "Fig0316(1)(top_left).tif", 0);
	//加载用来匹配的图像
	Mat dstImage = imread(st + "1.jpg", 0);

	MatND hist1, hist2;
	const int channles[1] = { 0 };
	const int histSize[1] = { 256 };
	float range[2] = { 0, 255 };
	const float* ranges[] = { range };
	calcHist(&srcImage, 1, channles, Mat(), hist1, 1, histSize, ranges);
	calcHist(&dstImage, 1, channles, Mat(), hist2, 1, histSize, ranges);

	float histadd1[256];
	float histadd2[256];
	float tmp1 = 0,tmp2 = 0;
	float pixelnum1 = srcImage.rows*srcImage.cols;
	float pixelnum2 = dstImage.rows*dstImage.cols;
	for (int i = 0; i < 256; i++)
	{
		tmp1 += hist1.at<float>(i) / pixelnum1;
		tmp2 += hist2.at<float>(i) / pixelnum2;
		histadd1[i] = tmp1;
		histadd2[i] = tmp2;
	}



	float diff = 1;
	Mat LUTlist(1, 256, CV_8UC1, Scalar(0));
	for (int i = 0; i < 256; i++)
	{
		diff = 1;
		LUTlist.at<uchar>(i) = i;
		for (int j = 0; j < 256; j++)
		{
			float value = abs(histadd1[i] - histadd2[j]);
			if (value < diff)
			{
				diff = abs(histadd1[i] - histadd2[j]);
				LUTlist.at<uchar>(i) = j;
			}
		}
	}
	Mat HistMatch(srcImage.rows, srcImage.cols, CV_8UC1, Scalar(0));
	LUT(srcImage, LUTlist, HistMatch);
	imshow("HistMatch", dstImage);
	imshow("src",srcImage);
	imshow("AfterMatchHistImage", HistMatch);

}
  • 将过曝的图片 根据 Lena 直方图进行直方图匹配
    在这里插入图片描述

局部直方图均衡

//局部直方图均衡化 
void LocalEquHist(Size a)
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat src = imread(st + "Fig0326(a)(embedded_square_noisy_512).tif", 0);
	Mat dst = src.clone();
	for (int i = (a.height - 1) / 2 ; i < src.rows - (a.height - 1) / 2 ;i++)
	for (int j = (a.width - 1) / 2 ; j < src.cols - (a.width - 1) / 2 ; j++)
	{
		int x = j - (a.width - 1) / 2;
		int y = i - (a.height - 1) / 2;
		int h = a.height;
		int w = a.width;
		Mat tmp = dst(Rect(x,y , w,h )).clone();
		equalizeHist(tmp, tmp);
		dst.at<uchar>(i, j) = tmp.at<uchar>((a.height - 1) / 2 , (a.width - 1) / 2);
	}
	imshow("src",src);
	imshow("dst", dst);
}
  • 核的大小为 11,11
    在这里插入图片描述

反向投影

反向投影

//反向投影
void BackProject()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat srcImage = imread(st + "1.jpg");
	Mat hist;
	Mat dstImage(srcImage.rows, srcImage.cols, CV_8UC1, Scalar(0));
	cvtColor(srcImage, dstImage, CV_BGR2HSV);

	const int channles[1] = { 0 };
	const int histrange[1] = { 10 };
	float range[2] = { 0, 255 };
	const float* ranges[] = { range };
	calcHist(&srcImage, 1, channles, Mat(), hist, 1, histrange, ranges);
	normalize(hist, hist, 0,255, NORM_MINMAX);
	//imshow("hist", hist);
	Mat backImage;
	cv::calcBackProject(&dstImage,1, channles, hist, backImage, ranges);
	imshow("backImage",backImage);
	imshow("src",srcImage);

	



}

在这里插入图片描述

反省投影匹配

//反向投影 目标匹配 时间比较慢 匹配效果也不算太好
void BackImagematch()
{
	string st = "E:\\冈萨雷斯OpenCV重写\\DIP3E_CH03_Original_Images\\DIP3E_Original_Images_CH03\\";
	Mat WaitMatchImage = imread(st + "1.jpg");
	Mat ModelImage = imread(st + "1match.jpg");
	Mat hist1,hist2;
	int height = ModelImage.rows;
	int weight = ModelImage.cols;
	Mat HSV1, HSV2;
	
	const int channles[1] = { 0 };
	//这个histrange设置的越大 精度也就越高  量化的太严重 会导致 匹配到多个目标
	const int histrange[1] = { 255 };
	float range[2] = { 0, 255 };
	const float* ranges[] = { range };
	cvtColor(WaitMatchImage, HSV1, CV_BGR2HSV);
	cvtColor(ModelImage, HSV2, CV_BGR2HSV);
	calcHist(&HSV2, 1, channles, Mat(), hist2, 1, histrange, ranges);

	Mat MatchResult(WaitMatchImage.rows, WaitMatchImage.cols, CV_8UC1, Scalar(0));
	Mat tmp(ModelImage.rows, ModelImage.cols, CV_8UC1, Scalar(0));
	int MatchValue = ModelImage.cols * ModelImage.rows;
	int MatchValuerow = 0, MatchValuecol = 0;
	for (int i = ModelImage.rows / 2; i < WaitMatchImage.rows - ModelImage.rows / 2 - 1; i++)
	for (int j = ModelImage.cols / 2; j < WaitMatchImage.cols - ModelImage.cols / 2 - 1; j++)
	{
		tmp = HSV1(Rect(j - ModelImage.cols / 2, i - ModelImage.rows / 2, ModelImage.cols, ModelImage.rows)).clone();
		calcHist(&tmp, 1, channles, Mat(), hist1, 1, histrange, ranges);
		int a = 0;
		for (int z = 0; z < histrange[0]; z++)
		{
			a += abs(hist1.at<float>(z)-hist2.at<float>(z));
		}
		if (MatchValue > a)
		{
			MatchValue = a;
			MatchValuerow = i;
			MatchValuecol = j;
		}
		MatchResult.at<uchar>(i, j) = (uchar)a;
	}
	normalize(MatchResult, MatchResult, 0, 255, NORM_MINMAX);
	imshow("结果图", MatchResult);
	imshow("待匹配图像",WaitMatchImage);
	imshow("目标图像",ModelImage);

	WaitMatchImage(Rect(MatchValuecol - ModelImage.cols / 2, MatchValuerow - ModelImage.rows / 2, ModelImage.cols, ModelImage.rows)) = Scalar::all(255);
	imshow("合成图像", WaitMatchImage);
}

在这里插入图片描述




混一天和努力一天,
看不出任何差别,
三天看不到任何变化,
七天也看不到任何距离!
但是,一个月后,
会看到话题不同,
三个月后,
会看到气场不同,
半年后,
会看到距离不同,
一到三年后,
会看到人生道路不同!
不要假装很努力,
结果不会陪你演戏!

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vector_LW

我们终将成龙 加油

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值