opencv曲线生长二

#include "stdafx.h"
#include <opencv2\opencv.hpp>  
#include <iostream>
#include <stack>
using namespace cv;
using namespace std;
double _ptolinesegdist(int x0,int y0,int x1,int y1,int x2,int y2);

//起点生长
void findline(Mat& MatIn,int growstartx,int growstarty ,vector<Point>& ptsclosed)//iGrowPoint为种子点的判断条件,iGrowJudge为生长条件  
{  
       
    //初始化原始种子点    
    int DIR[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};  
     
    //int iJudge=countNonZero(MatTemp);  
    vector<Point> seeds;
	seeds.push_back(Point(growstartx,growstarty));
	Point curgrow;
	//ptsclosed.clear();
	ptsclosed.push_back(Point(growstartx,growstarty));
	MatIn.at<uchar>(growstartx,growstarty)=0;
	while(!seeds.empty())
	{
				curgrow=seeds.back();
				seeds.pop_back();
				int i=curgrow.x;
				int j=curgrow.y;			;
               
                    for(int iNum=0;iNum<8;iNum++)  
                    {  
                        int iCurPosX=i+DIR[iNum][0];  
                        int iCurPosY=j+DIR[iNum][1];  
                        if(iCurPosX>=0 && iCurPosX<=(MatIn.rows-1) && iCurPosY>=0 && iCurPosY<=(MatIn.cols-1))  
                        {  
                            if(MatIn.at<uchar>(iCurPosX,iCurPosY)==255 )//生长条件,自己调整  
                            {                                  
								seeds.push_back(Point(iCurPosX,iCurPosY));  
								ptsclosed.push_back(Point(iCurPosX,iCurPosY));
								MatIn.at<uchar>(iCurPosX,iCurPosY)=0;
								//break;
                            }  
                        }  
                    }  
	}
       
}  

void findlines(Mat& MatIn,  vector<vector<Point>>& ptsclosed,int minpts)
{
	 for(int i=0;i<MatIn.rows;i++)  
        {  
            for(int j=0;j<MatIn.cols;j++)  
            {  
                if(MatIn.at<uchar>(i,j)==255 )  
                {  
					vector<Point> pts;
					findline(MatIn,i,j,pts);
					if(pts.size()>=minpts)
					{
						ptsclosed.push_back(pts);
					}
				}
			}
	 }
}

//统计核内的高亮点
int neighbourpoints(Mat &MatIn,int x,int y,int kernel)
{
	int brights=0;
	 for(int i=0-kernel;i<=kernel;i++)
	 {
		 for(int j=0-kernel;j<=kernel;j++)
		 {		
			 int iCurPosX=i+x;  
             int iCurPosY=j+y;
			 if(iCurPosX<=0 ||iCurPosX>=(MatIn.rows-1)|| iCurPosY<=0 || iCurPosY>=(MatIn.cols-1)) continue;
			 if(MatIn.at<uchar>(iCurPosX,iCurPosY)==255)brights++;
		 }
	}
	 return brights;
}

int neighbourpointsInRect(Mat &MatIn,int x,int y,int x1,int y1)
{
	int brights=0;
	 for(int i=x;(x<x1)?i<=x1:i>=x1;(x<x1)?i++:i--)
	 {
		 for(int j=y;(y<y1)?j<=y1:j>=y1;(y<y1)?j++:j--)
		 {					  
			 if(MatIn.at<uchar>(i,j)==255)brights++;
		 }
	}
	 return brights;
}

void neighbourpointsInRect2(Mat &MatIn,int x,int y,int x1,int y1,vector<Point>&pts)
{
	 
	 for(int i=x;(x<x1)?i<=x1:i>=x1;(x<x1)?i++:i--)
	 {
		 for(int j=y;(y<y1)?j<=y1:j>=y1;(y<y1)?j++:j--)
		 {					  
			 if(MatIn.at<uchar>(i,j)==255){
				 pts.push_back(Point(i,j)); 
			 }
		 }
	}
}

int ptInptv(vector<Point>&ptv,int x,int y)
{
	if(ptv.size()==0)return 0;
	for(int i=0;i<ptv.size();i++){
		if(ptv[i].x==x&&ptv[i].y==y)return 1;
	}
	return 0;
}
int ptInptvv(vector<vector<Point>>&ptvv,int x,int y)
{
    if(ptvv.size()==0)return 0;
	for(int i=0;i<ptvv.size();i++){
		 if(ptInptv(ptvv[i],x,y))return 1;
	}
	return 0;
}
 
int isConnectedBetween2Points(Mat &MatIn,int x,int y,int x1,int y1)
{
	if(x==x1||y==y1){
		for(int i=x;x<x1?i<=x1:i>=x1;x<x1?i++:i--){
			for(int j=y;y<y1?j<=y1:j>=y1;y<y1?j++:j--){
				if(MatIn.at<uchar>(i,j)!=255)return 0;
			}
		}
		return 1;
	}
	int minx,miny;
	int maxx,maxy;
	minx=min(x,x1);
	miny=min(y,y1);
	maxx=max(x,x1);
	maxy=max(y,y1);

	Rect rc(miny,minx,maxy-miny+1,maxx-minx+1);	 
	//PrintMat2(MatIn,minx,miny,maxx-minx+1,maxy-miny+1);
	Mat subMat=MatIn(rc);
	//PrintMat(subMat);
	/*vector<Point> pts;
	findline(subMat.clone(),x-minx,y-miny,pts);
	if(pts.size()>=2)
	{
		if(ptInptv(pts,x-minx,y-miny)&&ptInptv(pts,x1-minx,y1-miny))return 1;
	}*/
#if 0
	vector<vector<Point>> ptsclosed;  
    findlines(  subMat.clone(),    ptsclosed,2);  
      
     //AfxMessageBox("");exit(0);  
    if(ptInptvv(ptsclosed,x-minx,y-miny)&&ptInptvv(ptsclosed,x1-minx,y1-miny))  
    {  
            return 1;  
    }  
#else
		vector<Point>  ptsclosed;  
    findline(  subMat.clone(),x-minx,y-miny,    ptsclosed);  
      
     //AfxMessageBox("");exit(0);  
    if(ptInptv(ptsclosed,x1-minx,y1-miny))  
    {  
            return 1;  
    } 
#endif
	return 0;
}
void _selectshortpath( vector<Point>& pts,int x,int y, int x1,int y1)
{	 
	//minx,miny------>minx,maxy---->maxx,maxy
	if(isnibpt(Point(x,y),Point(x1,y1))){
		pts.push_back(Point(x,y));
		pts.push_back(Point(x1,y1));
		return;
	}
	for(int i =x;(x<x1)?(i<=x1):(i>=x1);(x<x1)?(i++):(i--)){
		pts.push_back(Point(i,y));
	}
	for(int i =y;(y<y1)?(i<=y1):(i>=y1);(y<y1)?(i++):(i--)){
		pts.push_back(Point(x1,i));
	}
}
int ptsinptnib(Mat& MatIn,int x,int y,uchar v)
{
	int DIR[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
	int tot=0;
	 for(int iNum=0;iNum<8;iNum++)  
        {  
            int iCurPosX= x+DIR[iNum][0];  
            int iCurPosY= y+DIR[iNum][1];  
            if(iCurPosX>=0 && iCurPosX<=(MatIn.rows-1) && iCurPosY>=0 && iCurPosY<=(MatIn.cols-1))  
            {  
				 
                 if(MatIn.at<uchar>(iCurPosX,iCurPosY)==v )//生长条件,自己调整  
                 {  
					tot++;
                  }  
             }  
          }  
	 return tot;
}
int selectshortpath(Mat& MatIn,vector<Point>& pts,int x,int y, int x1,int y1,int iGroweasy)
{	 
	//minx,miny------>minx,maxy---->maxx,maxy
	if(iGroweasy){
		vector<Point>ptshighlight;
		neighbourpointsInRect2(MatIn,x,y,x1,y1,ptshighlight); 
		for(int i=0;i<ptshighlight.size()-1;i++){
			_selectshortpath(pts,ptshighlight[i].x,ptshighlight[i].y,ptshighlight[i+1].x,ptshighlight[i+1].y);
		}	
		return 1;
	}
	int minx,miny;
	int maxx,maxy;
	minx=min(x,x1);
	miny=min(y,y1);
	maxx=max(x,x1);
	maxy=max(y,y1);

	Rect rc(miny,minx,maxy-miny+1,maxx-minx+1);	 
	//PrintMat2(MatIn,minx,miny,maxx-minx+1,maxy-miny+1);
	Mat subMat=MatIn(rc).clone();

	typedef struct _pointorder{
		int x;
		int y;
		int order;
		_pointorder(int a,int b,int c){
			x=a;
			y=b;
			order=c;
		};
		_pointorder(){			
		};
	}PTO;
	vector<vector<PTO>> lines;
	vector<PTO>pathpts;	

	PTO ptnow;
	
	int DIR[8][2]={/*{-1,-1},{-1,0},{-1,1},{0,-1},*/{0,1},/*{1,-1},*/{1,0},{1,1}}; 	

	pathpts.push_back(PTO(x-minx,y-miny,0));
	int flagx=x1>x?1:-1;
	int flagy=y1>y?1:-1;

		while(pathpts.size())
		{		
_do1:
			ptnow=pathpts.back();
			for(int iNum=ptnow.order;iNum<3;iNum++)  
			{  
				int iCurPosX=ptnow.x+DIR[iNum][0]*flagx;  
				int iCurPosY=ptnow.y+DIR[iNum][1]*flagy;  
				if(iCurPosX>=0 && iCurPosX<=(subMat.rows-1) && iCurPosY>=0 && iCurPosY<=(subMat.cols-1))  
				{ 
						if(iCurPosX==x1-minx&&iCurPosY==y1-miny)
						{
							lines.push_back(pathpts);
							goto _do2;							 
						}
						else if(subMat.at<uchar>(iCurPosX,iCurPosY)==0 )//生长条件,自己调整  
						{  
							 pathpts.push_back(PTO(iCurPosX,iCurPosY,0));	
							 goto _do1;
							/* break;*/
						} 
				}  
			}
_do2:
			pathpts.pop_back();
			if(pathpts.size()){
							ptnow=pathpts.back();
							pathpts.pop_back();
							ptnow.order++;
							pathpts.push_back(ptnow);
			}
		}

	if(lines.size()==0)return 0;
	int smalllength=BIGINT;
	int smallindex=0;
	for(int i=0;i<lines.size();i++){
		if(smalllength>lines[i].size()){
			smalllength=lines[i].size();
			smallindex=i;
		}
	}
	
	for(int i=0;i<lines[smallindex].size();i++){
		pts.push_back(Point(lines[smallindex][i].x+minx,lines[smallindex][i].y+miny));
	}
	return 1;
	/*for(int i=0;i<subMat.rows;i++){
		for(int j=0;j<subMat.cols;j++){
		}
	}*/
	//findlines(subMat.clone(),ptss,2);
	//if(ptss.size()){
	//	for(int i=0;i<ptss.size();i++){
	//		 if(ptInptv(ptss[i],x-minx,y-miny)&&ptInptv(ptss[i],x1-minx,y1-miny))return ; 
	//	}		
	//}
	//_selectshortpath( pts,  x,  y,   x1,  y1);
}
//获取mat的指定亮度点的个数 灰度图
void getmatvaluepts(Mat&src_gray,vector<Point>&pts,int v)
{

	for(int i=0;i<src_gray.rows;i++){
		for(int j=0;j<src_gray.cols;j++){
			if(src_gray.at<uchar>(i,j)==v){
				pts.push_back(Point(i,j));
			}
		}
	}
}
//获取浮点mat的极值点,x,y
void getShortMatPeakPts(Mat&src_gray,vector<Point>&pts,bool x,bool checkdouble)
{
	if(x){
		for(int i=0;i<src_gray.rows;i++){
			for(int j=1;j<src_gray.cols-1;j++){
				if((src_gray.at<short>(i,j-1)>0&&src_gray.at<short>(i,j+1)<0)
					||(src_gray.at<short>(i,j-1)<0&&src_gray.at<short>(i,j+1)>0))
				{
					if(checkdouble){
						int findsameed=0;
						for(int k=0;k<pts.size();k++){
							if(pts[i].x==i&&pts[i].y==j){
								findsameed=1;
								break;
							}
						}
						if(findsameed)continue;
					}
					pts.push_back(Point(i,j));
				}
			}
		}
	}else{
		for(int i=0;i<src_gray.cols;i++){
			for(int j=1;j<src_gray.rows-1;j++){
				if((src_gray.at<short>(j-1,i)>0&&src_gray.at<short>(j+1,j )<0)
					||(src_gray.at<short>(j-1,i)<0&&src_gray.at<short>(j+1,j)>0))
				{
					if(checkdouble){
						int findsameed=0;
						for(int k=0;k<pts.size();k++){
							if(pts[i].x==i&&pts[i].y==j){
								findsameed=1;
								break;
							}
						}
						if(findsameed)continue;
					}
					pts.push_back(Point(j,i));
				}
			}
		}
	}
}
//获取矩阵的极值点 灰度图
void getMatSecondDerivativeExtreme(Mat&src_gray,vector<Point>&ptspeak)
{
  int scale = 1;
  int delta = 0;
  int ddepth = CV_16S;  

  /// Generate grad_x and grad_y
  Mat grad_x, grad_y;
  Mat abs_grad_x, abs_grad_y;

  /// Gradient X
  //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
  Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
  getShortMatPeakPts( grad_x, ptspeak,1 ,0); 

  /// Gradient Y
  //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
  Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
  getShortMatPeakPts ( grad_y, ptspeak,0,1 );  

}
//全图曲线生长
Mat fullCurseGrow(Mat& MatIn, int kernel,int iGroweasy,int lowptsinline)
{
	vector<Point>ptspeak;
	getMatSecondDerivativeExtreme(MatIn,ptspeak);
	vector<vector<Point>> ptvv;
	findlines(MatIn.clone(),ptvv,lowptsinline);
	Mat m=MatIn.clone();
	for(int i=0;i<ptspeak.size();i++){
		if(ptInptvv(ptvv,ptspeak[i].x,ptspeak[i].y)){
			m=curseGrow(m,ptspeak[i].x,ptspeak[i].y,kernel,iGroweasy,1);
		}
	}
	return m;
}
//曲线k延伸,如果最近k距离点有亮点,就延伸过去
Mat curseGrow(Mat& MatIn,int growstartx,int growstarty ,int kernel,bool iGroweasy,bool smallfirstflag)
{
	Mat Matnow=MatIn.clone();//(MatIn.size(),CV_8UC1,Scalar(0));  

    vector<Point> seeds;
	seeds.push_back(Point(growstartx,growstarty));

	Point curgrow;
    
	while(!seeds.empty()){
				curgrow=seeds.back();
				seeds.pop_back();
				Matnow.at<uchar>(curgrow.x,curgrow.y)=255;							 
			   /*if(neighbourpoints(MatIn,curgrow.x,curgrow.y,kernel)>=kernel*2+1){
				   continue;
			   }*/
			   vector<Point> brightptsforign;
			  for(int i=0-kernel-1;i<=kernel+1;i++){
				  for(int j=0-kernel-1;j<=kernel+1;j++){
				   int iCurPosX=i+curgrow.x;  
                   int iCurPosY=curgrow.y+j;
				  if(iCurPosX<=0 ||iCurPosX>=(MatIn.rows-1)|| iCurPosY<=0 || iCurPosY>=(MatIn.cols-1)) continue;   
				  if(i==0&&j==0)continue;
				  if(Matnow.at<uchar>(iCurPosX,iCurPosY)==255)
					  brightptsforign.push_back(Point(iCurPosX,iCurPosY));
				  }
			  }
			  // for(int i=-kernel-1;i<=kernel+1;i+=(kernel+1)*2){
				 // for(int j= -kernel;j<=kernel;j++){
				 //  int iCurPosX=i+curgrow.x;  
     //              int iCurPosY=curgrow.y+j;
				 // if(iCurPosX<0 ||iCurPosX>(MatIn.rows-1)|| iCurPosY<0 || iCurPosY>(MatIn.cols-1)) continue;   
				 // if(Matnow.at<uchar>(iCurPosX,iCurPosY)==255)
				 // brightptsforign.push_back(Point(iCurPosX,iCurPosY));
				 // }
			  //}

			  int smalldistance=smallfirstflag*BIGINT;
			  Point goodpt;
			   for(int i=0;i<brightptsforign.size();i++)
			   {
				   if(!isConnectedBetween2Points(Matnow,brightptsforign[i].x,brightptsforign[i].y,curgrow.x,curgrow.y))
				   {
					   int distance=(brightptsforign[i].x-curgrow.x)*(brightptsforign[i].x-curgrow.x)+(brightptsforign[i].y-curgrow.y)*(brightptsforign[i].y-curgrow.y);
					   if((smallfirstflag&&distance<smalldistance)||(!smallfirstflag&&distance>smalldistance)){
						   smalldistance=distance;
						   goodpt=brightptsforign[i];
					   }
					 /*  Matnow.at<uchar>(brightptsforign[i].x,brightptsforign[i].y)=255;
					   vector<Point> goodpath;
					   selectshortpath(Matnow,goodpath,curgrow.x,curgrow.y,brightptsforign[i].x,brightptsforign[i].y);
					   for(int i=0;i<goodpath.size();i++){
						   Matnow.at<uchar>(goodpath[i].x,goodpath[i].y)=255;
					   }
					   seeds.push_back(Point(brightptsforign[i].x,brightptsforign[i].y));*/
					   //break;
				   }
			   } 
			   if(smalldistance!=smallfirstflag*BIGINT){
				    Matnow.at<uchar>(goodpt.x,goodpt.y)=255;
					   vector<Point> goodpath;
					   if(selectshortpath(Matnow,goodpath,curgrow.x,curgrow.y,goodpt.x,goodpt.y,iGroweasy))
					   {
						   for(int i=0;i<goodpath.size();i++)
						   {
							   Matnow.at<uchar>(goodpath[i].x,goodpath[i].y)=255;
						   }
						   seeds.push_back(Point(goodpt.x,goodpt.y));
					   }
			   }
	}
     
    return Matnow;    
}
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently,
// but are changed infrequently

#pragma once

#ifndef _SECURE_ATL
#define _SECURE_ATL 1
#endif

#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN            // Exclude rarely-used stuff from Windows headers
#endif

#include "targetver.h"

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit

// turns off MFC's hiding of some common and often safely ignored warning messages
#define _AFX_ALL_WARNINGS

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions


#include <afxdisp.h>        // MFC Automation classes



#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h>           // MFC support for Internet Explorer 4 Common Controls
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>             // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

#include <afxcontrolbars.h>     // MFC support for ribbons and control bars









#ifdef _UNICODE
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#endif
#include <stdarg.h>
#include <iostream>
#include <opencv2\opencv.hpp>  

using namespace cv;
using namespace std;
typedef int (*CALLBACK_T)(const char* file,void * p);
int stepDirFileNoRecurse(CString& strDir,CALLBACK_T fncallback,void* p);
void fprint(const char* format,...);
void flog(const char* fmt,...);
CString gettimestring( );
void sortrc(vector<Rect>& boundRect ,vector<Rect>& res,int bigfirst=1);
void combinenibrcs(vector<Rect>& boundRect ,vector<Rect>& res);
int isnibrc(Rect&rc0,Rect&rc1);
void combinenibrc(Rect&rc0,Rect&rc1,Rect& rcnew);
int iscorrectcharrcs(vector<Rect>& boundRect,vector<Rect>&rcsorder);
int getmaxcontinuecharrcs(vector<Rect>& boundRectorder,vector<Rect>&continuercs);
void savecontour(Mat& img,vector<Rect>&boundrc,const char*fname,vector<Mat>&mats);
void findcontourslog(Mat &img,const char* filepath,int ind,vector<Mat>&mats);
int main_test(const char* fileimage, float f,float s,int ind,vector<Mat>&mats);
DWORD WINAPI knn_simple_test2(void*p);
void knn_train(Mat& traindata, Mat& trainlabel,  int k);
int knn_predict(Mat& testdata,int k );
void enhenceimg(Mat &img,Mat &imageEnhance,float r);
int gammaenhence(Mat  &image,Mat& gray_Contrast,float r)  ;
void z2(Mat &img,Mat &b2);
int sobelxy( Mat&src_gray,Mat&grad );
void BoundarySeedFill(Mat &src, Point ptStart) ;
Mat otsu(Mat& img);
void findLines(Mat &_inputimg, vector<deque<Point>> &_outputlines);
Scalar random_color(RNG& _rng);
bool isnibpt(Point&p1,Point& p2);
Mat RegionGrow(Mat MatIn,int iGrowPoint,int iGrowJudge);
Mat RegionGrowSeed(Mat src, Point2i pt, int th)  ;
void getmingraybygrad(Mat& old,Mat&sob,uchar minth,vector<Point>& pts);
Mat RegionGrowFromPoint(Mat MatIn,int growstartx,int growstarty ,int iGrowJudge);
Mat curseGrow(Mat& MatIn,int growstartx,int growstarty ,int kernel,bool iGroweasy,bool smallfirstflag);
void findlines(Mat& MatIn,  vector<vector<Point>>& ptsclosed,int minpts);
void neighbourpointsInRect2(Mat &MatIn,int x,int y,int x1,int y1,vector<Point>&pts);
#define BIGINT 666666
#define MINDISTCHARINIMG 2
#define MAXDILATESIZEFORREMOVEGOOD       21
#define MINCOUTOURAREA   9
#define MINCOUTOURHEIGHT  5
#define MINCOUTOURWIDTH   5
#define 	MINCOUTOURRECTAREA 1
int isclosecurse(vector<Point>& pts);
Rect convertrc(Rect rc);
int rectinrect(int x,int y,int w,int h ,int x1,int y1,int w1,int h1);
void getcontoursfrommatrect(Mat& gray,Rect rc,vector<Rect>&boundingrcs);
void drawcontour(Mat& img,vector<Rect>&boundrc);
void getsobelinnerrcsfromgray(Mat & gray,vector<Rect>&rectsAll);
int main_test_sobel(const char* fileimage, float f,float s,int ind,vector<Mat>&mats);
void drawcontour2(Mat& img,vector<Rect>&boundrc,const char* winname);
void getcontours(Mat& m2v, vector<Rect>&boundingrcs);
void getsobelinnerrcsfromgray2(Mat & gray,vector<Rect>&rectsAll);
void getsobelinnerrcsfromgrayinr(Mat & gray,vector<Rect>&rectsAll);
int main_test_inr(const char* fileimage, float f,float s,int ind,vector<Mat>&mats);
int sobel2xy( Mat&src_gray,Mat&grad );
int maxValueHist(Mat& Frame, int downValue,int topvalue);
Mat fullCurseGrow(Mat& MatIn, int kernel,int iGroweasy,int lowptsinline);
完整版本的曲线生长

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值