当前位置: 首页>>代码示例>>C++>>正文


C++ CBlobResult::GetNumBlobs方法代码示例

本文整理汇总了C++中CBlobResult::GetNumBlobs方法的典型用法代码示例。如果您正苦于以下问题:C++ CBlobResult::GetNumBlobs方法的具体用法?C++ CBlobResult::GetNumBlobs怎么用?C++ CBlobResult::GetNumBlobs使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在CBlobResult的用法示例。


在下文中一共展示了CBlobResult::GetNumBlobs方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。

示例1: extractBall

void extractBall()
{
    imgTransform(BALL_HUE_U, BALL_HUE_L, BALL_SAT_U, BALL_SAT_L, VAL_U, VAL_L);

	blobRes = CBlobResult(dst, NULL, 0);
	blobRes.Filter( blobRes, B_EXCLUDE, CBlobGetArea(), B_LESS, BLOB_SIZE_MIN );// keep blobs larger than BLOB_SIZE_MIN
	numOfBlobs = blobRes.GetNumBlobs(); cout << numOfBlobs << endl;
	blobRes.Filter( blobRes, B_EXCLUDE, CBlobGetArea(), B_GREATER, BALL_SIZE_MAX );// keep blobs smaller than BALL_SIZE_MAX
	numOfBlobs = blobRes.GetNumBlobs(); cout << numOfBlobs << endl;
	blobRes.Filter( blobRes, B_INCLUDE, CBlobGetCompactness(), B_GREATER, BALL_COMPACTNESS );// keep blobs greater than BALL_COMPACTNESS
	numOfBlobs = blobRes.GetNumBlobs(); cout << numOfBlobs << endl;

	for(int i=0; i<numOfBlobs; i++)
		blobs[i] = blobRes.GetBlob(i);
};
开发者ID:pratikac,项目名称:freekick,代码行数:15,代码来源:main.cpp

示例2: nextIteration

void ForegroundDetector::nextIteration(const Mat &img)
{
    if(bgImg.empty())
    {
        return;
    }

    Mat absImg = Mat(img.cols, img.rows, img.type());
    Mat threshImg = Mat(img.cols, img.rows, img.type());

    absdiff(bgImg, img, absImg);
    threshold(absImg, threshImg, fgThreshold, 255, CV_THRESH_BINARY);

    IplImage im = (IplImage)threshImg;
    CBlobResult blobs = CBlobResult(&im, NULL, 0);

    blobs.Filter(blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, minBlobSize);

    vector<Rect>* fgList = detectionResult->fgList;
    fgList->clear();

    for(int i = 0; i < blobs.GetNumBlobs(); i++)
    {
        CBlob *blob = blobs.GetBlob(i);
        CvRect rect = blob->GetBoundingBox();
        fgList->push_back(rect);
    }

}
开发者ID:flytbase,项目名称:flyt_open_tld_3d,代码行数:29,代码来源:ForegroundDetector.cpp

示例3: findBlobs

void ScheinrieseApp::findBlobs() {
    CBlobResult blobs;
    int i;
    CBlob *currentBlob;
    IplImage *original, *originalThr;

    // load an image and threshold it
    original = cvLoadImage("pic1.png", 0);
    cvThreshold( original, originalThr, 100, 0, 255, CV_THRESH_BINARY );

    // find non-white blobs in thresholded image
    blobs = CBlobResult( originalThr, NULL, 255 );
    // exclude the ones smaller than param2 value
    blobs.Filter( blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, param2 );

    // get mean gray color of biggest blob
    CBlob biggestBlob;
    CBlobGetMean getMeanColor( original );
    double meanGray;

    blobs.GetNth( CBlobGetArea(), 0, biggestBlob );
    meanGray = getMeanColor( biggestBlob );

    // display filtered blobs
    cvMerge( originalThr, originalThr, originalThr, NULL, displayedImage );

    for (i = 0; i < blobs.GetNumBlobs(); i++ )
    {
        currentBlob = blobs.GetBlob(i);
        currentBlob->FillBlob( displayedImage, CV_RGB(255,0,0));
    }
}
开发者ID:TheProduct,项目名称:Scheinriese,代码行数:32,代码来源:ScheinrieseApp.tess.cpp

示例4: getNearestBlob

CBlob getNearestBlob(CBlobResult blobs, coord coordinate){
	
	int tot = blobs.GetNumBlobs();
	CBlob Blob;
	float distance[10]; // 10 è il numero massimo di blob trovabile in un video
	float minimum;
	
	coord tempCoord;

	//Questo ciclo for fa la distanza manhattan tra le coordinate passate e tutti i blob catturati e crea il vettore con tutte le distanze.
	for (int i=0; i<tot; i++){
		Blob = blobs.GetBlob(i);
		tempCoord.set( (int) Blob.MaxX(), (int) Blob.MinX(), (int) Blob.MaxY(), (int) Blob.MinY());
		distance[i] = sqrt((double)(tempCoord.cX - coordinate.cX)*(tempCoord.cX - coordinate.cX) + (tempCoord.cY - coordinate.cY)*(tempCoord.cY - coordinate.cY));
	}

	int minDistanceId=0;
	
	//Questo ciclo for becca la minima distanza fra tutte quelle calcolate
	for (int j=0; j<tot; j++){
		minimum = min( distance[j], distance[minDistanceId]);	
		if ( distance[j] == minimum ) minDistanceId = j;
		}

	//Ottenuta la minima distanza si va a ritornare il Blob corrispondente
	Blob = blobs.GetBlob( minDistanceId );
	//delete[] distance;
	return Blob;

}
开发者ID:FranoTech,项目名称:video-tracker,代码行数:30,代码来源:extractBlob.cpp

示例5: computeWhiteMaskOtsu

CBlobResult computeWhiteMaskOtsu(Mat& imgRGBin, Mat& imgHSVIn, CBlobResult& blobs, int limitRGB, int limitHSV, double RGBratio, double HSVratio, int bmin, int bmax, int i){
	waitKey(30);
	Mat BGRbands[3];  
	split(imgRGBin,BGRbands);
	Mat imgHSV;
	cvtColor(imgHSVIn,imgHSV,CV_BGR2HSV);
	Mat HSVbands[3];  
	split(imgHSV,HSVbands);
	Mat maskHSV, maskRGB, maskT;

	int otsuTRGB = getThreshVal_Otsu_8u(BGRbands[2]);
	do{
		threshold(BGRbands[2],maskRGB,otsuTRGB,255,THRESH_BINARY);
		otsuTRGB++;
	}while(countNonZero(maskRGB)>(RGBratio*limitRGB) & otsuTRGB<=255);
	int otsuTHSV = getThreshVal_Otsu_8u(HSVbands[1]);
	do{	
		threshold(HSVbands[1],maskHSV,otsuTHSV,255,THRESH_BINARY_INV);
		otsuTHSV--;
	}while(countNonZero(maskHSV)>(HSVratio*limitHSV) & otsuTHSV>=0); // 0.1
	bitwise_or(maskHSV,maskRGB,maskT);
	int blobSizeBefore = blobs.GetNumBlobs();
    blobs = blobs + CBlobResult( maskT ,Mat(),8);
	blobs.Filter( blobs, B_EXCLUDE, CBlobGetLength(), B_GREATER, bmax );
	blobs.Filter( blobs, B_EXCLUDE, CBlobGetLength(), B_LESS, bmin );
	int blobSizeAfter = blobs.GetNumBlobs();
	Mat newMask(maskT.size(),maskT.type());
    newMask.setTo(0);
    for(;i<blobs.GetNumBlobs();i++){
		double area = blobs.GetBlob(i)->Area();
		if(area < 5000 && area > 400)
			blobs.GetBlob(i)->FillBlob(newMask,CV_RGB(255,255,255),0,0,true);
    }
	if(countNonZero(maskRGB)>400 && countNonZero(maskHSV)>400 && blobSizeBefore!=blobSizeAfter){
		vector<Mat> BGRbands;  split(imgRGBin,BGRbands);
		Mat maskedRGB = applyMaskBandByBand(newMask,BGRbands);
		bitwise_not(newMask,newMask);
		split(imgHSVIn,BGRbands);
		Mat maskedHSV = applyMaskBandByBand(newMask,BGRbands);
		blobs = computeWhiteMaskOtsu(maskedRGB, maskedHSV, blobs, countNonZero(maskRGB),countNonZero(maskHSV),RGBratio, HSVratio, bmin, bmax, i-1);
	}		
	return blobs;
}
开发者ID:Apicio,项目名称:yV9R8ge87FnJ,代码行数:43,代码来源:detection.cpp

示例6: while

/**
- FUNCTION: CBlobResult
- FUNCTIONALITY: Copy constructor
- PARAMETERS:
- source: object to copy
- RESULT:
- RESTRICTIONS:
- AUTHOR: Ricard Borràs
- CREATION DATE: 25-05-2005.
- MODIFICATION: Date. Author. Description.
*/
CBlobResult::CBlobResult( const CBlobResult &source )
{
  m_blobs = blob_vector( source.GetNumBlobs() );

  // creem el nou a partir del passat com a paràmetre
  m_blobs = blob_vector( source.GetNumBlobs() );
  // copiem els blobs de l'origen a l'actual
  blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin();
  blob_vector::iterator pBlobsDst = m_blobs.begin();

  while( pBlobsSrc != source.m_blobs.end() )
  {
    // no podem cridar a l'operador = ja que blob_vector és un 
    // vector de CBlob*. Per tant, creem un blob nou a partir del
    // blob original
    *pBlobsDst = new CBlob(**pBlobsSrc);
    pBlobsSrc++;
    pBlobsDst++;
  }
}
开发者ID:andudu,项目名称:myf4k,代码行数:31,代码来源:BlobResult.cpp

示例7: drawInitialBlobs

void drawInitialBlobs(IplImage * tmp_frame, CBlobResult blobs){

	
	coord drawCoord;

	for (int i=0; i<blobs.GetNumBlobs();i++){
		
		//!Creating the coordinate struct
		drawCoord.set( (int) blobs.GetBlob(i).MaxX(), (int) blobs.GetBlob(i).MinX(), (int) blobs.GetBlob(i).MaxY(), (int) blobs.GetBlob(i).MinY());

		drawBlob(tmp_frame, drawCoord, 255, 255, 0);
	}
}
开发者ID:FranoTech,项目名称:video-tracker,代码行数:13,代码来源:extractBlob.cpp

示例8: tick

/* Fetch a frame (if available) and process it, calling appropriate 
 callbacks when data becomes available. */
void MarkerCapture::tick(){
    IplImage *thresh_frame = NULL;
    CBlobResult blobs;
    
    // Acquire the lock, update the current frame.
    pthread_mutex_lock(&frame_mutex);
    current_frame = cvCloneImage(cvQueryFrame(camera));
    if(color_acquired && current_frame){
        thresh_frame = apply_threshold(current_frame, target_color);
    }else{
        // create a suplicant.
        thresh_frame = cvCreateImage(cvGetSize(current_frame),IPL_DEPTH_8U,1);
    }
    pthread_mutex_unlock(&frame_mutex);
    // Lock released. Done messing with buffers.
    
    if(frame_update_callback){
        (*frame_update_callback)(this, current_frame, thresh_frame);
    }
    if(color_acquired){
        blobs = detect_blobs(thresh_frame, CV_BLOB_SIZE_MIN);
        if(blobs.GetNumBlobs() >= 2){ // need 2 or more blobs for positional fix.
            MarkerPositionEstimate position;
            // fetch the two largest blobs, by area.
            CBlob blob0, blob1;
            blobs.GetNthBlob(CBlobGetArea(), 0, blob0);
            blobs.GetNthBlob(CBlobGetArea(), 1, blob1);
            // perform positional calculations
            position.distance = distance(blob0, blob1);
            position.angle = angle(blob0, blob1);
            position.blob0_center = blob_center(blob0);
            position.blob1_center = blob_center(blob1);
            // call the update handler.
            if(position_update_callback){
                (*position_update_callback)(this, position);
            }
        }
        blobs.ClearBlobs();
    }
    
    pthread_mutex_lock(&frame_mutex);
    cvReleaseImage(&current_frame);
    cvReleaseImage(&thresh_frame);
    pthread_mutex_unlock(&frame_mutex);
    
    int curr_time = clock();
    fps = CLOCKS_PER_SEC/(double)(curr_time - time);
    time = curr_time;
}
开发者ID:kenkeiter,项目名称:interface,代码行数:51,代码来源:markers.cpp

示例9: centroidMM

vector<Bubble> OMRSheet::getBubbles(int xi1, int yi1, int xi2, int yi2){
    vector <Bubble> bubbles;
    cout<<"Bubble area "<<bubbleArea;
    int minArea = bubbleArea/2, maxArea = bubbleArea*1.5;
    CBlobResult blobs = ImageUtils::findBlobs(rawSheet, minArea, maxArea, cvRect(xi1, yi1, xi2-xi1, yi2-yi1));
    for (int i = 0; i < blobs.GetNumBlobs(); i++ )
    {
        CvRect rect = blobs.GetBlob(i)->GetBoundingBox();
        Point centroid = ImageUtils::findCentroid(rawSheet, &rect);
        Point centroidMM((centroid.x() - x1)/15, (centroid.y() - y1)/15);
        Bubble bubble(blobs.GetBlob(i), &centroidMM, &centroid);
        bubbles.push_back(bubble);
        
    }
    
    return bubbles;
}
开发者ID:AshWilliams,项目名称:ezOMR,代码行数:17,代码来源:OMRSheet.cpp

示例10: while

/**
- FUNCTION: CBlobResult
- FUNCTIONALITY: Copy constructor
- PARAMETERS:
	- source: object to copy
- RESULT:
- RESTRICTIONS:
- AUTHOR: Ricard Borr�s
- CREATION DATE: 25-05-2005.
- MODIFICATION: Date. Author. Description.
*/
CBlobResult::CBlobResult( const CBlobResult &source )
{
    // creem el nou a partir del passat com a par�metre
    //m_blobs = Blob_vector( source.GetNumBlobs() );
    m_blobs.reserve(source.GetNumBlobs());
    // copiem els blobs de l'origen a l'actual
    Blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin();
    Blob_vector::iterator pBlobsDst = m_blobs.begin();
    while( pBlobsSrc != source.m_blobs.end() )
    {
        // no podem cridar a l'operador = ja que Blob_vector �s un
        // vector de CBlob*. Per tant, creem un blob nou a partir del
        // blob original
        m_blobs.push_back(new CBlob(**pBlobsSrc));
        pBlobsSrc++;
    }
}
开发者ID:uUlmKnierim,项目名称:everywhereDisplaySoftware,代码行数:28,代码来源:BlobResult.cpp

示例11: extractBots

void extractBots()
{
    //RED TEAM
    imgTransform(TEAM_R_HUE_U, TEAM_R_HUE_L, TEAM_R_SAT_U, TEAM_R_SAT_L, VAL_U, VAL_L);
	blobRes = CBlobResult(dst, NULL, 0);
	blobRes.Filter( blobRes, B_EXCLUDE, CBlobGetArea(), B_LESS, BLOB_SIZE_MIN );// keep blobs larger than BLOB_SIZE_MIN
	numOfBlobs = blobRes.GetNumBlobs(); cout << numOfBlobs << endl;
    if(numOfBlobs == 2)
    {
        for (int i=0; i<2; i++)
            blobRes.GetBlob(i)

	for(int i=0; i<numOfBlobs; i++)
		blobs[i] = blobRes.GetBlob(i);
};


void printBlobs()
{

	CBlobGetXCenter getXC;
	CBlobGetYCenter getYC;
    CBlobGetArea    getArea;
    CBlobGetCompactness getCompactness;


	printf("-----Printng Blobs------\n");
	for(int i=0; i<numOfBlobs; i++)
	{
		printf("%d\t(%3.2f,%3.2f),%3.2f %3.2f\n", i, getXC(blobs[i]), getYC(blobs[i]), getArea(blobs[i]), getCompactness(blobs[i]));		
	}
	printf("\n");

	cvNamedWindow("old", 1);
	cvNamedWindow("new", 1);
	cvMoveWindow("old", 0,0);
	cvMoveWindow("new", 0,400);

	cvShowImage("old", img);
	cvShowImage("new", dst);
	cvWaitKey();

};
开发者ID:pratikac,项目名称:freekick,代码行数:43,代码来源:main.cpp

示例12: extractBlob

coord extractBlob(CBlobResult blobs, coord selectedCoord){
   
	coord coordinate;
	CBlob Blob;

	if ( blobs.GetNumBlobs()==0 ) {
		coordinate.flag=false; 
		return coordinate;
	}
	else {
		
		//!Get the blob info
		Blob = getNearestBlob( blobs, selectedCoord);
		
		//!Creating the coordinate struct
		coordinate.set( (int) Blob.MaxX(), (int) Blob.MinX(), (int) Blob.MaxY(), (int) Blob.MinY());
		
		return coordinate;
	}

}
开发者ID:FranoTech,项目名称:video-tracker,代码行数:21,代码来源:extractBlob.cpp

示例13: resultat

/**
- FUNCTION: + operator
- FUNCTIONALITY: Joins the blobs in source with the current ones
- PARAMETERS:
- source: object to copy the blobs
- RESULT:
- object with the actual blobs and the source blobs
- RESTRICTIONS:
- AUTHOR: Ricard Borràs
- CREATION DATE: 25-05-2005.
- MODIFICATION: Date. Author. Description.
*/
CBlobResult CBlobResult::operator+( const CBlobResult& source )
{	
  //creem el resultat a partir dels blobs actuals
  CBlobResult resultat( *this );

  // reservem memòria per als nous blobs
  resultat.m_blobs.resize( resultat.GetNumBlobs() + source.GetNumBlobs() );

  // declarem els iterador per recòrrer els blobs d'origen i desti
  blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin();
  blob_vector::iterator pBlobsDst = resultat.m_blobs.end();

  // insertem els blobs de l'origen a l'actual
  while( pBlobsSrc != source.m_blobs.end() )
  {
    pBlobsDst--;
    *pBlobsDst = new CBlob(**pBlobsSrc);
    pBlobsSrc++;
  }

  return resultat;
}
开发者ID:andudu,项目名称:myf4k,代码行数:34,代码来源:BlobResult.cpp

示例14: on_trackbar

// threshold trackbar callback
void on_trackbar( int dummy )
{
	if(!originalThr)
	{
		originalThr = cvCreateImage(cvGetSize(original), IPL_DEPTH_8U,1);
	}

	if(!displayedImage)
	{
		displayedImage = cvCreateImage(cvGetSize(original), IPL_DEPTH_8U,3);
	}
	
	// threshold input image
	cvThreshold( original, originalThr, param1, 255, CV_THRESH_BINARY );

	// get blobs and filter them using its area
	CBlobResult blobs;
	int i;
	CBlob *currentBlob;

	// find blobs in image
	blobs = CBlobResult( originalThr, NULL, 255 );
	blobs.Filter( blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, param2 );

	// display filtered blobs
	cvMerge( originalThr, originalThr, originalThr, NULL, displayedImage );

	for (i = 0; i < blobs.GetNumBlobs(); i++ )
	{
		currentBlob = blobs.GetBlob(i);
		currentBlob->FillBlob( displayedImage, CV_RGB(255,0,0));
	}
	 
    cvShowImage( wndname, displayedImage );
	
}
开发者ID:ashokvms,项目名称:cvblobslib,代码行数:37,代码来源:blobTest.cpp

示例15: main

int main(int argc, char * argv[])
{
	vector <string> imgNames;
	vector <string> imgNamesMask;
	char strFrame[20];
	readImageSequenceFiles(imgNames, imgNamesMask);

	list<TrackLine> trackLineArr;

	// read org frame and forground for process
	// you can modify it to read video by add a segment alg
	for(unsigned int i = 40; i < imgNames.size() - 1; i++)
	{ 		
		Mat frame = imread(imgNames[i]);
		Mat grayImg;
		cvtColor(frame, grayImg, CV_RGB2GRAY);

		Mat maskImage = imread(imgNamesMask[i], 0);

		// get blobs and filter them using its area
		// use 'cvblobslib' to get the object blobs
		threshold( maskImage, maskImage, 81, 255, CV_THRESH_BINARY );
		medianBlur(maskImage, maskImage, 3);
			
		IplImage ipl_maskImage = maskImage;
		CBlobResult blobs = CBlobResult( &ipl_maskImage, NULL, 0 );
		blobs.Filter( blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, 30 );	// filter blobs that area smaller than a certern num
		
		list<CBlob *> remBlob;
		for (int k = 0; k < blobs.GetNumBlobs(); k++)
		{
			remBlob.push_back(blobs.GetBlob(k));
		}

		printf("%d\n", trackLineArr.size());
		for (list<TrackLine>::iterator trackIter = trackLineArr.begin(); trackIter != trackLineArr.end(); )
		{
			//kf predicition, get kfRect
			Mat kfPrediction = (trackIter->kf).predict();
			Point kfPrePt((int)(kfPrediction.at<float>(0)), (int)(kfPrediction.at<float>(1)));
			Rect kfRect(kfPrePt.x - (trackIter->box).width / 2, kfPrePt.y - (trackIter->box).height / 2, (trackIter->box).width, (trackIter->box).height);
			//ct predicition, get ctRect
			int ctError = 0;
			Rect ctRect(trackIter->box);
			float score = (trackIter->ct).predicition(grayImg, ctRect);
			rectangle(frame, kfRect, Scalar(0, 200, 0));	//green, kf predicition box
			rectangle(frame, ctRect, Scalar(0, 0, 200));	//red, ct predicition box
			//union predicit rectangle
			//if they have no same area, we consider ct is wrong, because kalman is physical movement
			float areaScale = (float)(sqrt((kfRect & ctRect).area() *1.0 / kfRect.area()));
			Point movePoint((int)((ctRect.x - kfRect.x) * areaScale), (int)((ctRect.y - kfRect.y) * areaScale));
			Rect unionPreRect = kfRect + movePoint;

			//calc object box
			Rect objRect;
			int j = 0;
			for (list<CBlob *>::iterator blobIter = remBlob.begin(); blobIter != remBlob.end(); )
			{
				Rect detRect((*blobIter)->GetBoundingBox());
				float detArea = (float)((*blobIter)->Area());
				if ((unionPreRect & detRect).area() > 0)
				{
					if (j++ == 0) objRect = detRect;
					else objRect = objRect | detRect;
					blobIter = remBlob.erase(blobIter);
				}
				else blobIter++;
			}

			// let box's area equal
			float objArea = (float)(objRect.area());
			objRect = Rect((int)(objRect.x + objRect.width / 2.0 - unionPreRect.width / 2.0), 
				(int)(objRect.y + objRect.height / 2.0 - unionPreRect.height / 2.0), 
				unionPreRect.width, unionPreRect.height);

			float detAreaScale = (float)(sqrt(objArea * 1.0 / unionPreRect.area()));
			if (detAreaScale > 1.0) detAreaScale = 1.0;
			Point detMovePoint((int)((objRect.x - unionPreRect.x) * detAreaScale), (int)((objRect.y - unionPreRect.y) * detAreaScale));
			Rect unionCorrRect = unionPreRect + detMovePoint;

			// if detect area > 0
			if (objArea > 0)
			{
				trackIter->box = unionCorrRect;
				rectangle(frame, unionCorrRect, Scalar(200,0,0), 1);
				//kf correct
				Mat_<float> measurement(2,1);
				measurement(0) = (float)((trackIter->box).x + (trackIter->box).width / 2.0);
				measurement(1) = (float)((trackIter->box).y + (trackIter->box).height / 2.0);
				(trackIter->kf).correct(measurement);
				//ct update
				(trackIter->ct).update(grayImg, trackIter->box);

				trackIter++;
			}
			// else we beleve tracking miss
			else
			{
				if ((trackIter->miss)++ == 5) trackIter = trackLineArr.erase(trackIter);
				else trackIter++;
//.........这里部分代码省略.........
开发者ID:elfmedy,项目名称:2013,代码行数:101,代码来源:RunTracker.cpp


注:本文中的CBlobResult::GetNumBlobs方法示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。