// Compute Simple descriptors.
void ComputeSimpleDescriptors(CFloatImage &image, FeatureSet &features)
{
    //Create grayscale image used for Harris detection
    CFloatImage grayImage=ConvertToGray(image);

    vector<Feature>::iterator i = features.begin();
    while (i != features.end()) {
        Feature &f = *i;
		//these fields should already be set in the computeFeatures function
		int x = f.x;
		int y = f.y;

		// now get the 5x5 window surrounding the feature and store them in the features
		for(int row=(y-2); row<=(y+2); row++)
		{
			for(int col=(x-2); col<=(x+2); col++)
			{
				//if the pixel is out of bounds, assume it is black
				if(row<0 || row>=grayImage.Shape().height || col<0 || col>=grayImage.Shape().width)
				{
					f.data.push_back(0.0);
				}
				else
				{
					f.data.push_back(grayImage.Pixel(col,row,0));
				}
			}
		}
		printf("feature num %d\n", i->id);
        i++;
    }
}
void ComputeHarrisFeatures(CFloatImage &image, FeatureSet &features)
{
    //Create grayscale image used for Harris detection
    CFloatImage grayImage=ConvertToGray(image);

    //Create image to store Harris values
    CFloatImage harrisImage(image.Shape().width,image.Shape().height,1);

    //Create image to store local maximum harris values as 1, other pixels 0
    CByteImage harrisMaxImage(image.Shape().width,image.Shape().height,1);

    //compute Harris values puts harris values at each pixel position in harrisImage. 
    //You'll need to implement this function.
    computeHarrisValues(grayImage, harrisImage);
        
    // Threshold the harris image and compute local maxima.  You'll need to implement this function.
    computeLocalMaxima(harrisImage,harrisMaxImage);

    // Prints out the harris image for debugging purposes
    CByteImage tmp(harrisImage.Shape());
    convertToByteImage(harrisImage, tmp);
    WriteFile(tmp, "harris.tga");

    // TO DO--------------------------------------------------------------------
    //Loop through feature points in harrisMaxImage and fill in information needed for 
    //descriptor computation for each point above a threshold. We fill in id, type, 
    //x, y, and angle.
	CFloatImage A(grayImage.Shape());
	CFloatImage B(grayImage.Shape());
	CFloatImage C(grayImage.Shape());

	CFloatImage partialX(grayImage.Shape());
	CFloatImage partialY(grayImage.Shape());

	GetHarrisComponents(grayImage, A, B, C, &partialX, &partialY);
	int featureCount = 0;
    for (int y=0;y<harrisMaxImage.Shape().height;y++) {
        for (int x=0;x<harrisMaxImage.Shape().width;x++) {
                
            // Skip over non-maxima
            if (harrisMaxImage.Pixel(x, y, 0) == 0)
				continue;

            //TO DO---------------------------------------------------------------------
            // Fill in feature with descriptor data here. 
            Feature f;
			f.type = 2;
			f.id = featureCount++;
			f.x = x;
			f.y = y;
			f.angleRadians = GetCanonicalOrientation(x, y, A, B, C, partialX, partialY);
				//atan(partialY.Pixel(x, y, 0)/partialX.Pixel(x, y, 0));
			// Add the feature to the list of features
            features.push_back(f);
        }
    }
}
Exemplo n.º 3
0
// Compute features using Harris corner detection method
void ComputeHarrisFeatures(CFloatImage &image, FeatureSet &features)
{
	// Create grayscale image used for Harris detection
	CFloatImage grayImage = ConvertToGray(image);

	// Create image to store Harris values
	CFloatImage harrisImage(image.Shape().width,image.Shape().height,1);

	// Create image to store local maximum harris values as 1, other pixels 0
	CByteImage harrisMaxImage(image.Shape().width,image.Shape().height,1);

	// Create image to store orientation values
	CFloatImage orientationImage(image.Shape().width, image.Shape().height, 1);

	// Compute the harris score at each pixel position, storing the result in in harrisImage. 
	computeHarrisValues(grayImage, harrisImage, orientationImage);

	// Threshold the harris image and compute local maxima.
	computeLocalMaxima(harrisImage,harrisMaxImage);

	// Save images
	CByteImage tmp(harrisImage.Shape());
	CByteImage tmp2(harrisImage.Shape());
	convertToByteImage(harrisImage, tmp);
	convertToByteImage(grayImage, tmp2);
	WriteFile(tmp2, "grayImg.tga");
	WriteFile(tmp, "harris.tga");
	WriteFile(harrisMaxImage, "harrisMax.tga");

	// Loop through feature points in harrisMaxImage and fill in id, type, x, y, and angle 
	// information needed for descriptor computation for each feature point, then add them
	// to feature set
	int id = 0;
	for (int y=0; y < harrisMaxImage.Shape().height; y++) {
		for (int x=0; x < harrisMaxImage.Shape().width; x++) {
			if (harrisMaxImage.Pixel(x, y, 0) == 1) {
				Feature f;
				f.id = id;
				f.type = 2;
				f.x = x;
				f.y = y;
				f.angleRadians = orientationImage.Pixel(x,y,0);
				
				features.push_back(f);
				id++;
			}
		}
	}
}
Exemplo n.º 4
0
int EPOSTPDriver::InnerRemixBmpInEPOS(byte* pBmpData, int bytePerLine, int bmpWidth, int bmpHeight, long lx, long ly, byte* pPrintData, int PrintDataLength)
{
  mBmpWidth = bmpWidth;
  mBmpHeight = bmpHeight;
  
  if (!isGrayData(bmpWidth, bytePerLine))
  {
    //灰度化
    mpGrayData = ConvertToGray(pBmpData, bytePerLine, bmpWidth, bmpHeight);
  }
  else
  {
    mpGrayData = pBmpData;
  }
  
  //二值化
  mpBlankInfo = ConvertToBW(mpGrayData, bmpWidth, bmpHeight);
  
  // releasePointers(&mpGrayData);
  delete[] mpGrayData;
  mpGrayData = nullptr;
  
  myxPos = 0;
  
 pPrintData[myxPos + 0] = 0x1b;
 pPrintData[myxPos + 1] = 0x40;
 
 myxPos += 2;
 
 int lYinPix = abs(ly) * 203.f / 254.f;
 int icounts = lYinPix / 255;
 
 if (ly > 0)
 {
   for (int i = 0; i < icounts; ++i)
   {
     pPrintData[myxPos + 0] = 0x1b;
     pPrintData[myxPos + 1] = 0x4a;
     pPrintData[myxPos + 2] = 0xff;
     myxPos += 3;
   }
   
   lYinPix &= 0xff;
   if (lYinPix > 0 || lYinPix < 0)
   {
     pPrintData[myxPos + 0] = 0x1b;
     pPrintData[myxPos + 1] = 0x4a;
     pPrintData[myxPos + 2] = (byte)lYinPix;
     myxPos += 3;
   }
 }
 
 long height = bmpHeight;
 
 int skipLines = 0;
 byte** DataBlock = 0;
 long ht = 0;
 long lXinPix = lx * 203 / 254;
 
 float skipBlankLineFactor = 1;
 float skipBlankColFactor = 1;
 
 while (ht < height)
 {
   skipLines = 0;
   
   byte tempByte = (byte)(0x80 >> (ht & 0x07));
   while ((mpBlankInfo[ht >> 3] & tempByte) && (ht < height))
   {
     ++ht;
     ++skipLines;
   }
   
   if (skipLines > 0)
   {
     int iSkipYpos = skipLines * skipBlankLineFactor;
     int iSkipCounts = iSkipYpos / 255;
     for (int i = 0; i < iSkipCounts; ++i)
     {
       pPrintData[myxPos + 0] = 0x1b;
       pPrintData[myxPos + 1] = 0x4a;
       pPrintData[myxPos + 2] = 0xff;
       myxPos += 3;
     }
     
     iSkipYpos &= 0xff;
     if (iSkipYpos != 0)
     {
       pPrintData[myxPos + 0] = 0x1b;
       pPrintData[myxPos + 1] = 0x4a;
       pPrintData[myxPos + 2] = (byte)iSkipYpos;
       myxPos += 3;
     }
   }
   
   if (ht >= height)
   {
     break;
   }
   
   if (lXinPix > 0)
   {
     pPrintData[myxPos + 0] = 0x1b;
     pPrintData[myxPos + 1] = 0x24;
     pPrintData[myxPos + 2] = lXinPix % 256;
     pPrintData[myxPos + 3] = lXinPix / 256;
     myxPos += 4;
   }
   
   DataBlock = get24Rows(ht);
   
   if (DataBlock == nullptr)
   {
       break;
   }
   
   ht += 24;
   
   twentyfourPointPerCol(DataBlock, pPrintData, 1);
   
   for (long i = 0; i < mBmpWidth; ++i)
   {
       delete[] DataBlock[i];
   }
   
   delete[] DataBlock;
 }
 
 pPrintData[myxPos + 0] = 0x00;
 ++myxPos;
  
  return myxPos;
}
// Compute MOPs descriptors.
void ComputeMOPSDescriptors(CFloatImage &image, FeatureSet &features)
{
	CFloatImage grayImage=ConvertToGray(image);
	CFloatImage blurredImage;
	
	Convolve(grayImage, blurredImage, ConvolveKernel_7x7);

	CFloatImage postHomography = CFloatImage();
	CFloatImage gaussianImage = GetImageFromMatrix((float *)gaussian5x5Float, 5, 5);


	//first make the image invariant to changes in illumination by subtracting off the mean
	int grayHeight = grayImage.Shape().height;
	int grayWidth = grayImage.Shape().width;

	// now make this rotation invariant
    vector<Feature>::iterator featureIterator = features.begin();
    while (featureIterator != features.end()) {
		Feature &f = *featureIterator;

		CTransform3x3 scaleTransform = CTransform3x3();
		CTransform3x3 translationNegative;
		CTransform3x3 translationPositive;
		CTransform3x3 rotation;

		double scaleFactor = 41/8;
		scaleTransform[0][0] = scaleFactor;
		scaleTransform[1][1] = scaleFactor;

		translationNegative = translationNegative.Translation(f.x,f.y);
		translationPositive = translationPositive.Translation(-4, -4);
		rotation = rotation.Rotation(f.angleRadians * 180/ PI);
		
		CTransform3x3 finalTransformation = translationNegative * rotation * scaleTransform * translationPositive;
		//CFloatImage sample61x61Window = 
		//CFloatImage pixelWindow = GetXWindowAroundPixel(grayImage, f.x, f.y, 61);

		WarpGlobal(blurredImage, postHomography, finalTransformation, eWarpInterpLinear, 1.0f);

		//now we get the 41x41 box around the feature
		for(int row=0; row< 8; row++)
		{
			for(int col=0;col< 8;col++)
			{
				f.data.push_back(postHomography.Pixel(col, row, 0));
			}
		}
		/*
		// now we do the subsampling first round to reduce to a 20x20
		int imgSize = 41;
		subsample(&f, imgSize, gaussianImage);

		//second round of subsampling to get it to a 10x10
		imgSize = 20;
		subsample(&f, imgSize, gaussianImage);	

		imgSize = 10;
		CFloatImage img = featureToImage(f, imgSize, imgSize);
		CFloatImage blurredImg(img.Shape());
		Convolve(img, blurredImg, gaussianImage);
		featuresFromImage(&f,blurredImg,imgSize,imgSize);
		
		int count = 0;
		for(int y=0; y<imgSize; y++)
		{
			for(int x=0; x<imgSize; x++)
			{
				if(x == 3 || x == 7 || y == 3 || y == 7)
				{
					f.data.erase(f.data.begin() + count);
				}
				else
				{
					count++;
				}
			}
		}
		*/
		normalizeIntensities(&f, 8, 8);
		featureIterator++;

	}
}
Exemplo n.º 6
0
int OKIDriver::InnerRemixBmpInOKI(byte* pBmpData, int bytePerLine, int bmpWidth, int bmpHeight, long lx, long ly, byte* pPrintData, int PrintDataLength)
{
  mBmpWidth = bmpWidth;
  mBmpHeight = bmpHeight;
  
  if (!isGrayData(bmpWidth, bytePerLine))
  {
    //灰度化
    mpGrayData = ConvertToGray(pBmpData, bytePerLine, bmpWidth, bmpHeight);
  }
  else
  {
    mpGrayData = pBmpData;
  }
  
  //二值化
  mpBlankInfo = ConvertToBW(mpGrayData, bmpWidth, bmpHeight);
  
  delete[] mpGrayData;
  mpGrayData = nullptr;
  
  myxPos = 0;
  
 pPrintData[myxPos + 0] = 0x18;
 pPrintData[myxPos + 1] = 0x0d;
 
 pPrintData[myxPos + 2] = 0x1b;
 pPrintData[myxPos + 3] = 0x36;
 
 myxPos += 4;
 
 int lYinPix = fabs(ly) * 120.f / 254.f;
 int icounts = lYinPix / 255;
 
 if (ly > 0)
 {
   for (int i = 0; i < icounts; ++i)
   {
     pPrintData[myxPos + 0] = 0x0d;
     pPrintData[myxPos + 1] = 0x1b;
     pPrintData[myxPos + 2] = 0x25;
     pPrintData[myxPos + 3] = 0x35;
     pPrintData[myxPos + 4] = 0xff;
     myxPos += 5;
   }
   
   lYinPix &= 0xff;
   if (lYinPix > 0 || lYinPix < 0)
   {
     pPrintData[myxPos + 0] = 0x0d;
     pPrintData[myxPos + 1] = 0x1b;
     pPrintData[myxPos + 2] = 0x25;
     pPrintData[myxPos + 3] = 0x35;
     pPrintData[myxPos + 4] = lYinPix;
     myxPos += 5;
   }
 }
 
 long height = bmpHeight;
 
 int skipLines = 0;
 byte** DataBlock = 0;
 long ht = 0;
 long lXinPix = lx * 180 / 254;
 
 while (ht < height)
 {
   skipLines = 0;
   
   byte tempByte = (byte)(0x80 >> (ht & 0x07));
   while ((mpBlankInfo[ht >> 3] & tempByte) && (ht < height))
   {
     ++ht;
     ++skipLines;
   }
   
   if (skipLines > 0)
   {
     int iSkipYpos = skipLines;
     
     iSkipYpos = (float)iSkipYpos / 180.f * 120.f;
     int iSkipCounts = iSkipYpos / 255;
     for (int i = 0; i < iSkipCounts; ++i)
     {
       pPrintData[myxPos + 0] = 0x0d;
       pPrintData[myxPos + 1] = 0x1b;
       pPrintData[myxPos + 2] = 0x25;
        pPrintData[myxPos + 3] = 0x35;
        pPrintData[myxPos + 4] = 0xff;
        myxPos += 5;
     }
     
     iSkipYpos &= 0xff;
     if (iSkipYpos != 0)
     {
       pPrintData[myxPos + 0] = 0x0d;
       pPrintData[myxPos + 1] = 0x1b;
       pPrintData[myxPos + 2] = 0x25;
       pPrintData[myxPos + 3] = 0x35;
       pPrintData[myxPos + 4] = iSkipYpos;
       myxPos += 5;
     }
   }
   
   if (ht >= height)
   {
     break;
   }
   
   if (lXinPix > 0)
   {
     pPrintData[myxPos + 0] = 0x1b;
     pPrintData[myxPos + 1] = 0x25;
     pPrintData[myxPos + 2] = 0x36;
     pPrintData[myxPos + 3] = lXinPix % 256;
     pPrintData[myxPos + 4] = lXinPix / 256;
     myxPos += 5;
   }
   
   DataBlock = get24Rows(ht);
   
   if (DataBlock == nullptr)
   {
       break;
   }
   
   ht += 24;
   
   twentyfourPointPerCol(DataBlock, pPrintData, 1);
   
   for (long i = 0; i < mBmpWidth; ++i)
   {
       delete[] DataBlock[i];
   }
   
   delete[] DataBlock;
 }
 
 pPrintData[myxPos + 0] = 0x0c;
 ++myxPos;
  
  return myxPos;
}
Exemplo n.º 7
0
int main(int argc, const char *argv[]) {
  WebPPicture pic1, pic2;
  int ret = 1;
  float disto[5];
  size_t size1 = 0, size2 = 0;
  int type = 0;
  int c;
  int help = 0;
  int keep_alpha = 0;
  int scale = 0;
  int use_gray = 0;
  const char* name1 = NULL;
  const char* name2 = NULL;
  const char* output = NULL;

  if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
    fprintf(stderr, "Can't init pictures\n");
    return 1;
  }

  for (c = 1; c < argc; ++c) {
    if (!strcmp(argv[c], "-ssim")) {
      type = 1;
    } else if (!strcmp(argv[c], "-psnr")) {
      type = 0;
    } else if (!strcmp(argv[c], "-alpha")) {
      keep_alpha = 1;
    } else if (!strcmp(argv[c], "-scale")) {
      scale = 1;
    } else if (!strcmp(argv[c], "-gray")) {
      use_gray = 1;
    } else if (!strcmp(argv[c], "-h")) {
      help = 1;
      ret = 0;
    } else if (!strcmp(argv[c], "-o")) {
      if (++c == argc) {
        fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
        goto End;
      }
      output = argv[c];
    } else if (name1 == NULL) {
      name1 = argv[c];
    } else {
      name2 = argv[c];
    }
  }
  if (help || name1 == NULL || name2 == NULL) {
    if (!help) {
      fprintf(stderr, "Error: missing arguments.\n");
    }
    Help();
    goto End;
  }
  if ((size1 = ReadPicture(name1, &pic1, 1)) == 0) {
    goto End;
  }
  if ((size2 = ReadPicture(name2, &pic2, 1)) == 0) {
    goto End;
  }
  if (!keep_alpha) {
    WebPBlendAlpha(&pic1, 0x00000000);
    WebPBlendAlpha(&pic2, 0x00000000);
  }

  if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) {
    fprintf(stderr, "Error while computing the distortion.\n");
    goto End;
  }
  printf("%u %.2f    %.2f %.2f %.2f %.2f\n",
         (unsigned int)size1, disto[4],
         disto[0], disto[1], disto[2], disto[3]);

  if (output != NULL) {
    uint8_t* data = NULL;
    size_t data_size = 0;
    if (pic1.use_argb != pic2.use_argb) {
      fprintf(stderr, "Pictures are not in the same argb format. "
                      "Can't save the difference map.\n");
      goto End;
    }
    if (pic1.use_argb) {
      int n;
      fprintf(stderr, "max differences per channel: ");
      for (n = 0; n < 3; ++n) {    // skip the alpha channel
        const int range = (type == 1) ?
          SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
                           (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
                           4, pic1.width, pic1.height, scale) :
          DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
                           (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
                           4, pic1.width, pic1.height, scale);
        if (range < 0) fprintf(stderr, "\nError computing diff map\n");
        fprintf(stderr, "[%d]", range);
      }
      fprintf(stderr, "\n");
      if (use_gray) ConvertToGray(&pic1);
    } else {
      fprintf(stderr, "Can only compute the difference map in ARGB format.\n");
      goto End;
    }
    data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
                                       pic1.width, pic1.height,
                                       pic1.argb_stride * 4,
                                       &data);
    if (data_size == 0) {
      fprintf(stderr, "Error during lossless encoding.\n");
      goto End;
    }
    ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
    WebPFree(data);
    if (ret) goto End;
  }
  ret = 0;

 End:
  WebPPictureFree(&pic1);
  WebPPictureFree(&pic2);
  return ret;
}
Exemplo n.º 8
0
// Compute MOPs descriptors.
void ComputeMOPSDescriptors(CFloatImage &image, FeatureSet &features)
{
	int w = image.Shape().width;  // image width
	int h = image.Shape().height; // image height

	// Create grayscale image used for Harris detection
	CFloatImage grayImage=ConvertToGray(image);

	// Apply a 7x7 gaussian blur to the grayscale image
	CFloatImage blurImage(w,h,1);
	Convolve(grayImage, blurImage, ConvolveKernel_7x7);

	// Transform matrices
	CTransform3x3 xform;
	CTransform3x3 trans1;
	CTransform3x3 rotate;
	CTransform3x3 scale;
	CTransform3x3 trans2;

	// Declare additional variables
	float pxl;					// pixel value
	double mean, sq_sum, stdev; // variables for normailizing data set

	// This image represents the window around the feature you need to compute to store as the feature descriptor
	const int windowSize = 8;
	CFloatImage destImage(windowSize, windowSize, 1);

	for (vector<Feature>::iterator i = features.begin(); i != features.end(); i++) {
		Feature &f = *i;

		// Compute the transform from each pixel in the 8x8 image to sample from the appropriate 
		// pixels in the 40x40 rotated window surrounding the feature
		trans1 = CTransform3x3::Translation(f.x, f.y);						// translate window to feature point
		rotate = CTransform3x3::Rotation(f.angleRadians * 180.0 / PI);		// rotate window by angle
		scale = CTransform3x3::Scale(5.0);									// scale window by 5
		trans2 = CTransform3x3::Translation(-windowSize/2, -windowSize/2);	// translate window to origin

		// transform resulting from combining above transforms
		xform = trans1*scale*rotate*trans2;

		//Call the Warp Global function to do the mapping
		WarpGlobal(blurImage, destImage, xform, eWarpInterpLinear);

		// Resize data field for a 8x8 square window
		f.data.resize(windowSize * windowSize);	

		// Find mean of window
		mean = 0;
		for (int y = 0; y < windowSize; y++) {
			for (int x = 0; x < windowSize; x++) {
				pxl = destImage.Pixel(x, y, 0);
				f.data[y*windowSize + x] = pxl;
				mean += pxl/(windowSize*windowSize);
			}
		}

		// Find standard deviation of window
		sq_sum = 0;
		for (int k = 0; k < windowSize*windowSize; k++) {
			sq_sum += (mean - f.data[k]) * (mean - f.data[k]);
		}
		stdev = sqrt(sq_sum/(windowSize*windowSize));

		// Normalize window to have 0 mean and unit variance by subtracting
		// by mean and dividing by standard deviation
		for (int k = 0; k < windowSize*windowSize; k++) {
			f.data[k] = (f.data[k]-mean)/stdev;
		}
	}
}
Exemplo n.º 9
0
// Compute Simple descriptors.
void ComputeSimpleDescriptors(CFloatImage &image, FeatureSet &features)
{
	// Create grayscale image used for Harris detection
	CFloatImage grayImage=ConvertToGray(image);

	int w = grayImage.Shape().width;  // image width
	int h = grayImage.Shape().height; // image height

	// Declare additional variables
	int newX, newY;  // (x,y) coordinate for pixel in 5x5 sample window
	int padType = 0; // select variable for what type of padding to use: , 0->zero, 1->edge, 2->reflect

	// Iterate through feature set and store simple descriptors for each feature into
	// corresponding feature
	for (vector<Feature>::iterator i = features.begin(); i != features.end(); i++) {
		Feature &f = *i;

		// Set angle to 0 since simple descriptors do not include orientation 
		f.angleRadians = 0;

		// Resize data field for a 5x5 square window
		f.data.resize(5 * 5);

		// The descriptor is a 5x5 window of intensities sampled centered on the feature point
		for (int j = 0; j < 25; j++) {
			find5x5Index(f.x,f.y,j,&newX,&newY);
			if(grayImage.Shape().InBounds(newX, newY)) {
				f.data[j] = grayImage.Pixel(newX, newY, 0);
			} else {
				// Depending on value of padType, perform different types of border padding
				switch (padType) {
					case 1:
						// 1 -> replicate border values
						if (newX < 0) {
							newX = 0;
						} else if (newX >= w) {
							newX = w-1;
						}
				
						if (newY < 0) {
							newY = 0;
						} else if (newY >= h) {
							newY = h-1;
						}

						f.data[j] = grayImage.Pixel(newX, newY, 0);
						break;
					case 2:
						// 2 -> reflect border pixels
						if (newX < 0) {
							newX = -newX;
						} else if (newX >= w) {
							newX = w-(newX%w)-1;
						}
						if (newY < 0) {
							newY = -newY;
						} else if (newY >= h) {
							newY = h-(newY%h)-1;
						}
						
						f.data[j] = grayImage.Pixel(newX, newY, 0);
						break;
					default:
						// 0 -> zero padding
						f.data[j] = 0;
						break;
				}
			}
		}
	}
}