Ejemplo n.º 1
0
void camVolbergFwd(CamImage *source, CamImage *dest, CamVolbergFwdParams *params)
{
    int x,y;
    CamImage intermediate;
    CAM_PIXEL *scanlinesrc,*scanlinedst,*intptr,*dstptr;
    double *mapping;

    camAllocateImage(&intermediate,dest->width,source->height,source->depth);
    
    // First resampling : horizontal
    mapping=(double*)malloc((source->width+1)*sizeof(double));
    for (y=0;y<intermediate.height;y++) {
	for (x=0;x<=source->width;x++) {
	    params->hfwd(x,y,&mapping[x]);
	}
	// Call Volberg's algorithm
	camVolbergFwdScanline((CAM_PIXEL*)(source->imageData+y*source->widthStep), source->width,
	   (CAM_PIXEL*)(intermediate.imageData+y*intermediate.widthStep), intermediate.width,
	   mapping);
    }
    free(mapping);

    // Second resampling : vertical
    mapping=(double*)malloc((intermediate.height+1)*sizeof(double));
    scanlinesrc=(CAM_PIXEL*)malloc(intermediate.height*sizeof(CAM_PIXEL));
    scanlinedst=(CAM_PIXEL*)malloc(dest->height*sizeof(CAM_PIXEL));
    for (x=0;x<dest->width;x++) {
	intptr=((CAM_PIXEL*)intermediate.imageData)+x;
	for (y=0;y<intermediate.height;y++) {
	    params->vfwd(x,y,&mapping[y]);
	    scanlinesrc[y]=*intptr;
	    intptr=((CAM_PIXEL*)(((char*)intptr)+intermediate.widthStep));
	}
	params->vfwd(x,y,&mapping[y]);
	// Call Volberg's algorithm
	camVolbergFwdScanline(scanlinesrc, intermediate.height,
	   scanlinedst, dest->height,
	   mapping);	
	dstptr=((CAM_PIXEL*)dest->imageData)+x;
	for (y=0;y<dest->height;y++) {
	    *dstptr=scanlinedst[y];
	    dstptr=((CAM_PIXEL*)(((char*)dstptr)+dest->widthStep));
	}
    }
    free(scanlinesrc);
    free(scanlinedst);
    free(mapping);

    camDeallocateImage(&intermediate);
}
Ejemplo n.º 2
0
int camFreeBitmapFont(CamBitmapFont *font)
{
    int i;
    if (font->nb_chars) {
        for (i=0;i<font->nb_chars;i++) {
            camRLEDeallocate(&font->masks[i]);
            camDeallocateImage(&font->letters[i]);
        }
        free(font->masks);
        free(font->letters);
        font->masks=NULL;
        font->letters=NULL;
        font->nb_chars=0;
    }
    return 1;
}
Ejemplo n.º 3
0
int main()
{
    CamImage imodel[3], image, color_image;
    CamKeypoints points[3], points2;
    int i, j;
    CamKeypointsMatches matches;
    char filename[256];
    char *model_images[] = {"edog5", "dvd", "mrpotato4"};
#define DISPLAYED_MODEL 2
#define NB_SCENES 5
    char *test_images[NB_SCENES] = {"scene1", "scene3", "scene4", "scene5", "scene7"};

    const int cx[3] = {450, 390, 550};
    const int cy[3] = {340, 510, 450};
    const int width[3] = {720, 690, 500};
    const int height[3] = {640, 910, 500};
    CamAffineTransform t;
    int error, c, x1, y1, x2, y2, score, nbFeatures;
    CamPoint xy[7], uv[7];
    CamImage dimage;
    CamROI roi;

    const int nb_keypoints = 500;

    for (i = 0; i < 3; i++) {
        sprintf(filename, "resources/photos/%s.bmp", model_images[i]);
        printf("Feature point detection on %s ...\n", model_images[i]);
        imodel[i].imageData = NULL;
        camLoadBMP(&imodel[i], filename);
        /*
        camAllocateImage(&image, imodel[i].width, imodel[i].height, CAM_DEPTH_8U);
        camRGB2Y(&imodel[i], &image);
        */
        camAllocateYUVImage(&image, imodel[i].width, imodel[i].height);
        camRGB2YUV(&imodel[i], &image);
        camAllocateKeypoints(&points[i], 0);
        points[i].id = i;
        camFastHessianDetector(&image, &points[i], 100, CAM_UPRIGHT);
        /*
        camDrawKeypoints(&points[i], &image, 128);
        sprintf(filename, "output/%s.pgm", model_images[i]);
        camSavePGM(&image, filename);
        	*/
        camDeallocateImage(&image);
    }

    camAllocateRGBImage(&dimage, imodel[0].width, imodel[0].height * 2);
    camSetROI(&roi, 0, 0, imodel[0].height, imodel[0].width, imodel[0].height);

    camAllocateKeypointsMatches(&matches, 2048);
    nbFeatures = 0;
    score = 0;

    for (i = 0; i < NB_SCENES; i++) {
        sprintf(filename, "resources/photos/%s.bmp", test_images[i]);
        printf("Feature point detection on %s ...\n", test_images[i]);
        color_image.imageData = NULL;
        camLoadBMP(&color_image, filename);
        /*
        camAllocateImage(&image, color_image.width, color_image.height, CAM_DEPTH_8U);
        camRGB2Y(&color_image, &image);
        */
        camAllocateYUVImage(&image, color_image.width, color_image.height);
        camRGB2YUV(&color_image, &image);
        camAllocateKeypoints(&points2, 0);
        camFastHessianDetector(&image, &points2, nb_keypoints, CAM_UPRIGHT);
        printf("# features = %d\n", points2.nbPoints);
        nbFeatures += points2.nbPoints;

        // Create result image
        dimage.roi = NULL;
        camCopy(&imodel[DISPLAYED_MODEL], &dimage);
        dimage.roi = &roi;
        camCopy(&color_image, &dimage);
        dimage.roi = NULL;
        camDrawKeypoints(&points[DISPLAYED_MODEL], &dimage, 128);

        for (j = 0; j < 3; j++) {
            camKeypointsMatching2(&points[j], &points2, &matches);
            // Find affine parameters
            if (camFindAffineTransform2(&matches, &t, &error)) {
                score += matches.nbMatches - matches.nbOutliers;
                if (j == DISPLAYED_MODEL) {
                    // Draw lines between model and target
                    for (c = 0; c < matches.nbMatches; c++) {
                        if (matches.pairs[c].mark != -1) {
                            camDrawKeypoint(matches.pairs[c].p1, &dimage, CAM_RGB(255, 0, 0));
                            x1 = matches.pairs[c].p1->x;
                            y1 = matches.pairs[c].p1->y;
                            x2 = matches.pairs[c].p2->x;
                            y2 = matches.pairs[c].p2->y;
                            y2 += image.height;
                            camDrawLine(&dimage, x1, y1, x2, y2, CAM_RGB(0, 255, 0));
                        }
                    }
                    for (c = 0; c < matches.nbMatches; c++) {
                        matches.pairs[c].p2->y += image.height;
                        camDrawKeypoint(matches.pairs[c].p2, &dimage, 128);
                    }
                }

                // Draw box on model and target
                xy[0].x = xy[3].x = cx[j] - width[j] / 2;
                xy[0].y = xy[1].y = cy[j] - height[j] / 2;
                xy[1].x = xy[2].x = cx[j] + width[j] / 2;
                xy[2].y = xy[3].y = cy[j] + height[j] / 2;
                xy[4].x = xy[5].x = cx[j];
                xy[4].y = xy[6].y = cy[j];
                xy[5].y = cy[j] - height[j] / 4;
                xy[6].x = cx[j] + width[j] / 4;
                for (c = 0; c < 7; c++) {
                    camApplyAffineTransform(&xy[c], &uv[c], &t);
                    uv[c].y += image.height;
                }
                if (j == DISPLAYED_MODEL) {
                    camDrawLine(&dimage, xy[0].x, xy[0].y, xy[1].x, xy[1].y, 0);
                    camDrawLine(&dimage, xy[2].x, xy[2].y, xy[1].x, xy[1].y, 0);
                    camDrawLine(&dimage, xy[2].x, xy[2].y, xy[3].x, xy[3].y, 0);
                    camDrawLine(&dimage, xy[0].x, xy[0].y, xy[3].x, xy[3].y, 0);
                    camDrawLine(&dimage, xy[4].x, xy[4].y, xy[5].x, xy[5].y, 0);
                    camDrawLine(&dimage, xy[6].x, xy[6].y, xy[4].x, xy[4].y, 0);
                }
                camDrawLine(&dimage, uv[0].x, uv[0].y, uv[1].x, uv[1].y, 0);
                camDrawLine(&dimage, uv[2].x, uv[2].y, uv[1].x, uv[1].y, 0);
                camDrawLine(&dimage, uv[2].x, uv[2].y, uv[3].x, uv[3].y, 0);
                camDrawLine(&dimage, uv[0].x, uv[0].y, uv[3].x, uv[3].y, 0);
                camDrawLine(&dimage, uv[4].x, uv[4].y, uv[5].x, uv[5].y, 0);
                camDrawLine(&dimage, uv[6].x, uv[6].y, uv[4].x, uv[4].y, 0);
            }
        }

        sprintf(filename, "output/%s.bmp", test_images[i]);
        camSaveBMP(&dimage, filename);

        camDeallocateImage(&image);
        camDeallocateImage(&color_image);
        camFreeKeypoints(&points2);
    }

    for (i = 0; i < 3; i++) {
        camDeallocateImage(&imodel[i]);
        camFreeKeypoints(&points[i]);
    }
    camDeallocateImage(&dimage);
    camFreeKeypointsMatches(&matches);

    printf("# features in scenes = %d\n", nbFeatures);
    printf("# matching features = %d (%lg%%)\n", score, score*100.0/nbFeatures);

}
Ejemplo n.º 4
0
int camLoadBitmapFont(CamBitmapFont *font, char *filename)
{
    CamImage bitmap;
    CamROI roi;
    int delimiter_color,x,i,px;
    unsigned char *ptr;
    CamTable clusters,LUT;
    CamRLEImage temp;
    char s[256];

    if (!camLoadBMP(&bitmap,filename)) {
        sprintf(s,"Couldn't load bitmap file %s.",filename);
        camError("camBitmapFontLoad",s);
        return 0;
    }
    font->height=bitmap.height-1;

    // Try to figure out how many letters are there in this font image
    font->nb_chars=0;
    font->first_char=33;
#define GET_COLOR_PIXEL(ptr) (((int)*(ptr))+(((int)*((ptr)+1))<<8)+(((int)*((ptr)+2))<<16))   
    ptr=bitmap.imageData;
    delimiter_color=GET_COLOR_PIXEL(ptr);
    for (x=0;x<bitmap.width;x++,ptr+=3) {
        if (GET_COLOR_PIXEL(ptr)==delimiter_color) font->nb_chars++;
    }

    // OK, now we can allocate memory for these
    font->masks=(CamRLEImage*)malloc(sizeof(CamRLEImage)*font->nb_chars);
    font->letters=(CamImage*)malloc(sizeof(CamImage)*font->nb_chars);
    
    // Let's prepare the data for font loading
    ptr=bitmap.imageData;
    px=0;
    roi.coi=0;
    bitmap.roi=&roi;
    clusters.size=12;
    for (i=0;i<6;i++) clusters.t[i*2]=clusters.t[i*2+1]=*(ptr+i);
    i=0;
    LUT.size=3;
    LUT.t[0]=1; LUT.t[1]=0; LUT.t[2]=0;
    camRLEAllocate(&temp,10000);

    // Ready. Let's go for all the characters
    for (x=1,ptr+=3;x<bitmap.width;x++,ptr+=3) {
        if (GET_COLOR_PIXEL(ptr)==delimiter_color) {
            // We've found the next character
            camRLEAllocate(&font->masks[i],(x-px)*font->height+2);
            camAllocateRGBImage(&font->letters[i],x-px,font->height);
            roi.xOffset=px; roi.yOffset=1;
            roi.width=x-px;
            roi.height=font->height;
            camCopy(&bitmap,&font->letters[i]);
            camRLEEncodeColor(&bitmap,&temp,&clusters);
            camRLEApplyLUT(&temp,&font->masks[i],&LUT);
            px=x;
            i++;
        }
    }
    // We've found the last character
    camRLEAllocate(&font->masks[i],(x-px)*font->height+2);
    camAllocateRGBImage(&font->letters[i],x-px,font->height);
    roi.xOffset=px; roi.yOffset=1;
    roi.width=x-px;
    roi.height=font->height;
    camCopy(&bitmap,&font->letters[i]);
    camRLEEncodeColor(&bitmap,&temp,&clusters);
    camRLEApplyLUT(&temp,&font->masks[i],&LUT);

    // Set the masks to all letters
    for (i=0;i<font->nb_chars;i++) {
        font->letters[i].mask=&font->masks[i];
    }
    camDeallocateImage(&bitmap);
    camRLEDeallocate(&temp);
    return 1;
}
Ejemplo n.º 5
0
void test_camRecursiveKeypoints()
{
	CamImage image, dest;
	CamKeypoints points;
	int i;
	const int x = 8;
	int angle;
	double costheta;
	double sintheta;

	const int xp[4] = {-1, 1, 1, -1};
	const int yp[4] = {-1, -1, 1, 1};
	CamWarpingParams params;

	printf("Recursive keypoints detection :\n");
	camAllocateImage(&image, 256, 256, CAM_DEPTH_8U);
	camAllocateKeypoints(&points, 100);

	camSet(&image, 0);
	//     camDrawRectangle(&image, 102, 120, 156, 152, 128);
	//     camFillColor(&image, 103, 121, 128, -1);
	camDrawRectangle(&image, 120, 50, 150, 70, 28);
	camDrawRectangle(&image, 100, 35, 180, 90, 180);
	//     camFillColor(&image, 123, 36, 128, -1);
	//
	//     camDrawCircle(&image, 50, 50, 10, 128);
	//     camFillColor(&image, 50, 50, 128, -1);
	//     camDrawCircle(&image, 80, 50, 5, 128);
	//     camFillColor(&image, 80, 50, 128, -1);
	//     camDrawCircle(&image, 100, 50, 3, 128);
	//     camFillColor(&image, 100, 50, 128, -1);
	#if 1
	angle = 20;
	costheta = cos(angle * 2 * M_PI / 360);
	sintheta = sin(angle * 2 * M_PI / 360);
	for (i = 0; i < 4; i++) {
		params.p[i].x = (int)floor((costheta * xp[i] - sintheta * yp[i]) * 15 + 0.5);
		params.p[i].y = (int)floor((sintheta * xp[i] + costheta * yp[i]) * 15 + 0.5);
		params.p[i].x += 192;
		params.p[i].y += 192;
	}
	for (i = 0; i < 4; i++) {
		camDrawLine(&image, params.p[i].x, params.p[i].y, params.p[(i+1)%4].x, params.p[(i+1)%4].y, 128);
	}
	camFillColor(&image, 192, 192, 128, -1);

	angle = 30;
	costheta = cos(angle * 2 * M_PI / 360);
	sintheta = sin(angle * 2 * M_PI / 360);
	for (i = 0; i < 4; i++) {
		params.p[i].x = (int)floor((costheta * xp[i] - sintheta * yp[i]) * 10 + 0.5);
		params.p[i].y = (int)floor((sintheta * xp[i] + costheta * yp[i]) * 10 + 0.5);
		params.p[i].x += 50;
		params.p[i].y += 192;
	}

	for (i = 0; i < 4; i++) {
		camDrawLine(&image, params.p[i].x, params.p[i].y, params.p[(i+1)%4].x, params.p[(i+1)%4].y, 128);
	}
	camFillColor(&image, 50, 192, 128, -1);

	#endif
	dest.imageData = NULL;

	camKeypointsRecursiveDetector(&image, NULL, &points, 20, 0);
	for (i = 0; i < points.nbPoints; i++) {
		printf("x=%d y=%d value=%d scale=%d size=%d angle=%d\n", points.keypoint[i]->x, points.keypoint[i]->y, points.keypoint[i]->value, points.keypoint[i]->scale, points.keypoint[i]->size, points.keypoint[i]->angle);
	}
	camDrawKeypoints(&points, &image, 192);
	camSavePGM(&image, "output/keypoints_recursive.pgm");

	camDeallocateImage(&image);
	camDeallocateImage(&dest);
	camFreeKeypoints(&points);
}
Ejemplo n.º 6
0
int
camKeypointsRecursiveDetector(CamImage *source, CamImage *integral, CamKeypoints *points, int nb_max_keypoints, int options)
{
	CamImage filter;
	CamInternalROIPolicyStruct iROI;
	CamROI *roi, roix;
	int i, width, height;
	CamKeypointShort *keypoints;
	int pnb_keypoints;
	int nb_keypoints = 0;
	unsigned int *abs_value_lines[2];
	int *value_lines[2];
	unsigned char *scale_lines[2];
	unsigned char *lmax_lines[2];
	unsigned int *lmax[2], nblmax[2];
	int widthStep;

	camPatchSizeParam = 16; //32 * 2 / 3; // Equivalent optimal value wrt SURF (the descriptor can partially lie outside screen)

	// Parameters checking
	CAM_CHECK(camKeypointsRecursiveDetector, camInternalROIPolicy(source, NULL, &iROI, 1));
	CAM_CHECK_ARGS(camKeypointsRecursiveDetector, (source->depth & CAM_DEPTH_MASK) >= 8);
	CAM_CHECK_ARGS(camKeypointsRecursiveDetector, points->allocated >= 0);
	CAM_CHECK_ARGS(camKeypointsRecursiveDetector, source->nChannels == 1 || ((source->nChannels == 3) && (source->dataOrder == CAM_DATA_ORDER_PLANE)));
	width = iROI.srcroi.width;
	height = iROI.srcroi.height;

	if (!integral)
	{
		// Compute integral image
		integral = (CamImage*)malloc(sizeof(CamImage));
		integral->imageData = NULL;
		roi = source->roi;
		camSetMaxROI(&roix, source);
		roix.coi = 1;
		source->roi = &roix;
		camIntegralImage(source, integral); // Computed on the whole frame, not only on ROI
		integral->roi = &iROI.srcroi;
		source->roi = roi;
	}
	widthStep = integral->widthStep / 4;

	// Allocate temp memory for keypoints
	keypoints = (CamKeypointShort*)malloc(CAM_MAX_KEYPOINTS * sizeof(CamKeypointShort));
	points->nbPoints = 0;
	// Allocate value and scale lines
	for (i = 0; i < 2; i++) {
		scale_lines[i] = (unsigned char*)malloc(width * sizeof(unsigned char));
		value_lines[i] = (int*)malloc(width * sizeof(int));
		abs_value_lines[i] = (unsigned int*)malloc(width * sizeof(unsigned int));
		lmax_lines[i] = (unsigned char*)malloc(width * sizeof(unsigned char));
		lmax[i] = (unsigned int*)malloc(width * sizeof(unsigned int));
		nblmax[i] = 0;
	}

	// Go !
	{
		int y;
		int max_scale_y;
		unsigned int *abs_current_value_line, *abs_previous_value_line;
		int *current_value_line, *previous_value_line;
		unsigned char *current_scale_line, *previous_scale_line;
		unsigned char *current_lmax_line, *previous_lmax_line;
		unsigned int *current_lmax, *previous_lmax;
		unsigned int current_nblmax, previous_nblmax;

		current_value_line = value_lines[0];
		previous_value_line = value_lines[1];
		abs_current_value_line = abs_value_lines[0];
		abs_previous_value_line = abs_value_lines[1];
		current_scale_line = scale_lines[0];
		previous_scale_line = scale_lines[1];
		current_lmax_line = lmax_lines[0];
		previous_lmax_line = lmax_lines[1];
		current_lmax = lmax[0];
		previous_lmax = lmax[1];
		// Fill the previous value line with a dummy value
		for (i = 0; i < width; i++) {
			abs_previous_value_line[i] = 0;
			previous_value_line[i] = 0;
			previous_scale_line[i] = 1;
			previous_lmax_line[i] = 0;
		}
		current_nblmax = 0;
		previous_nblmax = 0;

		for (y = 0; y < height; y++) {
			int x, v;
			// max_scale is the biggest scale we may apply. It must be more than 0, otherwise we can't do any image processing
			max_scale_y = (y + iROI.srcroi.yOffset) >> 1;
			// The preceding formula comes from :
			// - When iROI.yOffset is 0, max_scale should be 1 when y is 2 (1 pixel margin due to integral image)
			// - When iROI.yOffset is 0, max_scale should be 1 when y is 3 (1 pixel margin due to integral image)
			// - When iROI.yOffset is 0, max_scale should be 2 when y is 4 (1 pixel margin due to integral image)
			// Check max_scale with the bottom line
			v = (source->height - 1 - (y + iROI.srcroi.yOffset)) >> 1;
			// The preceding formula comes from "When iROI.yOffset is 0, max_scale should be 1 when y is equal to source->height - 3
			if (v < max_scale_y) max_scale_y = v;

			if (max_scale_y < 1) {
				// We can't compute anything for that line
				for (i = 0; i < width; i++) {
					current_value_line[i] = 0;
					current_scale_line[i] = 1;
				}
			} else {
				int scale_up = 1;
				int current_value = 0;
				unsigned int abs_current_value = 0;
				int current_scale = 1;
				unsigned int *ptr = ((unsigned int*)(integral->imageData + (y + iROI.srcroi.yOffset) * integral->widthStep)) + iROI.srcroi.xOffset;
				int s1, s2;

				for (x = 0; x < width; x++, ptr++) {
					int first_scale = current_scale;
					int max_scale = max_scale_y;
					// Check max_scale with the left side of the frame
					v = (x + iROI.srcroi.xOffset) >> 1;
					if (v < max_scale) max_scale = v;
					// Check max_scale with the right side of the frame
					v = (source->width -1 - (x + iROI.srcroi.xOffset)) >> 1;
					if (v < max_scale) max_scale = v;
					// We need a scale margin in order to compute the descriptor
					max_scale -= CAM_SCALE_MARGIN;

					// Choose the first scale to evaluate, depending on upper and left results
					if (abs_previous_value_line[x] > abs_current_value)
						first_scale = previous_scale_line[x];

					if (first_scale <= max_scale) {
						// Evaluate at first scale
						int scale = first_scale; // alias
						int yoffset = scale * widthStep;
						int value;
						#define CAM_RECURSIVE_PATTERN \
						{ \
						unsigned int *ptrAi = ptr - (yoffset + scale); \
						unsigned int *ptrDi = ptr + (yoffset + scale); \
						unsigned int *ptrBi = ptr - (yoffset - scale); \
						unsigned int *ptrCi = ptr + (yoffset - scale); \
						unsigned int valin = *ptrDi - *ptrBi - *ptrCi + *ptrAi; \
						unsigned int *ptrAo = ptr - ((yoffset + scale) << 1); \
						unsigned int *ptrDo = ptr + ((yoffset + scale) << 1); \
						unsigned int *ptrBo = ptr - ((yoffset - scale) << 1); \
						unsigned int *ptrCo = ptr + ((yoffset - scale) << 1); \
						unsigned int valout = *ptrDo - *ptrBo - *ptrCo + *ptrAo; \
						value = valout - (valin << 2); \
					}
					CAM_RECURSIVE_PATTERN;
					current_scale = scale;
					current_value = value;
					abs_current_value = abs(value);

					// Let's see if we go upscale or not...
					if (scale_up && scale < max_scale) {
						// OK. Let's test the upscale
						scale++;
						yoffset += widthStep;
						CAM_RECURSIVE_PATTERN;
						s1 = current_scale * current_scale; s2 = scale * scale;
						if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
							// Yes. That was a good hint
							current_scale = scale;
							current_value = value;
							abs_current_value = abs(value);
							// Let's test again at higher scale
							if (scale < max_scale) {
								scale++;
								yoffset += widthStep;
								CAM_RECURSIVE_PATTERN;
								s1 = current_scale * current_scale; s2 = scale * scale;
								if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
									// Even better
									current_scale = scale;
									current_value = value;
									abs_current_value = abs(value);
									// And we stop there, satisfied...
								}
							}
						} else {
							// Oh, oh... Apparently, that was not a good hint to go upscale...
							// Let's try to downscale then...
							scale -= 2;
							if (scale > 0 && scale <= max_scale) {
								yoffset -= widthStep << 1;
								CAM_RECURSIVE_PATTERN;
								s1 = current_scale * current_scale; s2 = scale * scale;
								if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
									// Yes. It's better
									scale_up = 0; // This is the hint for next time
									current_scale = scale;
									current_value = value;
									abs_current_value = abs(value);
								}
								// And we stop there, satisfied...
							}
						}
					} else {
						// OK. Let's test the downscale
						scale--;
						if (scale > 0 && scale <= max_scale) {
							yoffset -= widthStep;
							CAM_RECURSIVE_PATTERN;
							if (((CAM_INT64)abs(value)) * current_scale * current_scale > (CAM_INT64)abs_current_value * scale * scale) {
								// Yes. That was a good hint.
								current_scale = scale;
								current_value = value;
								abs_current_value = abs(value);
								// Let's test again at lower scale
								scale--;
								if (scale > 0 && scale <= max_scale) {
									yoffset -= widthStep;
									CAM_RECURSIVE_PATTERN;
									s1 = current_scale * current_scale; s2 = scale * scale;
									if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
										// Even better
										current_scale = scale;
										current_value = value;
										abs_current_value = abs(value);
										// And we stop there, satisfied...
									}
								}
							} else {
								// Oh, oh... Apparently, that was not a good hint to go downscale...
								// Let's try to upscale then...
								scale += 2;
								if (scale > 0 && scale <= max_scale) {
									yoffset += widthStep << 1;
									CAM_RECURSIVE_PATTERN;
									s1 = current_scale * current_scale; s2 = scale * scale;
									if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
										// Yes. It's better
										scale_up = 1; // This is the hint for next time
										current_scale = scale;
										current_value = value;
										abs_current_value = abs(value);
									}
									// And we stop there, satisfied...
								}
							}
						} else {
							// Oh, oh... Apparently, that was not a good hint to go downscale...
							// Let's try to upscale then...
							scale += 2;
							if (scale > 0 && scale <= max_scale) {
								yoffset += widthStep;
								CAM_RECURSIVE_PATTERN;
								s1 = current_scale * current_scale; s2 = scale * scale;
								if (((CAM_INT64)abs(value)) * s1 > (CAM_INT64)abs_current_value * s2) {
									// Yes. It's better
									scale_up = 1; // This is the hint for next time
									current_scale = scale;
									current_value = value;
									abs_current_value = abs(value);
								}
								// And we stop there, satisfied...
							}
						}
					}
					} else {
						// Skip this pixel and go back to small scale
						current_value = 0;
						abs_current_value = 0;
						current_scale = 1;
						scale_up = 1;
					}

					current_value = (current_value << 4) / (current_scale * current_scale);
					abs_current_value = abs(current_value);

					// Now, we do have the current_value, current_scale and abs_current_value
					// Let's check whether it is a local maximum or not
					{
						// If the current value is strictly greater than all the other values, then this is a local maximum and
						// the other pixels are marked as NOT being local maxima
						// If the current value is greater or equal to all neighbours, then this ia marked to be a local maximum
						// but the other pixels are kept as local maxima
						// If the current value is greater than any neighbour, this neighbour is marked as not being a local maximum
						int local_maximum = 1;
						if (x > 0) {
							// Compare to the left and upper-left pixel
							if (abs_current_value_line[x - 1] > abs_current_value)
								local_maximum = 0;
							else current_lmax_line[x - 1] = 0;
							if (abs_previous_value_line[x - 1] > abs_current_value)
								local_maximum = 0;
							else previous_lmax_line[x - 1] = 0;
						}
						// Compare to pixel above
						if (abs_previous_value_line[x] > abs_current_value)
							local_maximum = 0;
						else previous_lmax_line[x] = 0;
						if (x < width - 1) {
							// Compare to the upper right pixel
							if (abs_previous_value_line[x + 1] > abs_current_value)
								local_maximum = 0;
							else previous_lmax_line[x + 1] = 0;
						}
						if (local_maximum && abs_current_value > 0) {
							current_lmax[current_nblmax++] = x;
							current_lmax_line[x] = 1;
						}
					}

					// Record the data for next line evaluation
					current_value_line[x] = current_value;
					abs_current_value_line[x] = abs_current_value;
					current_scale_line[x] = current_scale;

					//if (y == 100) {
						//printf("y=%d x=%d value=%d abs=%d scale=%d\n", y, x, current_value, abs_current_value, current_scale);
						//}
				}
			}

			// Check the local maxima on the previous line, and record the keypoints IF they are local maxima
			for (i = 0; i != previous_nblmax; i++) {
				if (previous_lmax_line[previous_lmax[i]]) {
					int scale = previous_scale_line[previous_lmax[i]];
					if (scale >= CAM_MIN_SCALE) {
						// Yes, we have definitely a local maximum
						// Record the keypoint
						keypoints[nb_keypoints].x = previous_lmax[i];
						keypoints[nb_keypoints].y = y - 1;
						keypoints[nb_keypoints].scale = scale;
						keypoints[nb_keypoints].value = previous_value_line[previous_lmax[i]];
						nb_keypoints++;
					}
				}
			}

			// Switch previous and current lines
			{
				unsigned char *tmp;
				tmp = current_scale_line;
				current_scale_line = previous_scale_line;
				previous_scale_line = tmp;
			}{
				unsigned int *tmp;
				tmp = abs_current_value_line;
				abs_current_value_line = abs_previous_value_line;
				abs_previous_value_line = tmp;
			}{
				int *tmp;
				tmp = current_value_line;
				current_value_line = previous_value_line;
				previous_value_line = tmp;
			}{
				unsigned char *tmp;
				tmp = current_lmax_line;
				current_lmax_line = previous_lmax_line;
				previous_lmax_line = tmp;
			}{
				unsigned int *tmp;
				tmp = current_lmax;
				current_lmax = previous_lmax;
				previous_lmax = tmp;
			}
			previous_nblmax = current_nblmax;
			current_nblmax = 0;
		}
	}

	for (i = 0; i < 2; i++) {
		free(value_lines[i]);
		free(abs_value_lines[i]);
		free(scale_lines[i]);
		free(lmax_lines[i]);
		free(lmax[i]);
	}

	// Post-processing : remove keypoints in a given neighborhood (proportionnal to scale)
	for (i = 1; i < nb_keypoints; i++) {
		CamKeypointShort *keypoint = &keypoints[i];
		int j = i - 1;
		int neighborhood = keypoint->scale >> 2;
		while (j >= 0 && keypoints[j].y >= keypoint->y - neighborhood) {
			CamKeypointShort *keypoint2 = &keypoints[j];
			if (keypoint2->x >= keypoint->x - neighborhood && keypoint2->x <= keypoint->x + neighborhood) {
				// They are next to each other...
				if (abs(keypoint2->value) > abs(keypoint->value))
					keypoint->scale = 0; // Let's get rid of this one... It has a lower value
					else
						keypoint2->scale = 0;
			}
			j--;
		}
	}
	for (i = 0; i < nb_keypoints; i++)
		if (!keypoints[i].scale) keypoints[i].value = 0;

		if (options & CAM_UPRIGHT) {
			// Remove keypoints the descriptor of which would be outside the frame boundaries
			for (i = 0; i < nb_keypoints; i++) {
				if (keypoints[i].scale) {
					int scale = keypoints[i].scale; // Save the scale
					keypoints[i].scale = (scale << 2) + 1;
					if (!camKeypointDescriptorCheckBounds(&keypoints[i], integral))
						keypoints[i].value = 0;
					keypoints[i].scale = scale;
				}
			}
		}

		// Sort the features according to value
		qsort(keypoints, nb_keypoints, sizeof(CamKeypointShort), camSortKeypointsShort);
		for (i = 0; i < nb_keypoints; i++)
			if (keypoints[i].value == 0) break;
			nb_keypoints = i;

		if (nb_keypoints > nb_max_keypoints) nb_keypoints = nb_max_keypoints;

		/* Interpolation :
		* Maxima : solve([y1=a*(x1-p)^2+b,y2=a*(-p)^2+b,y3=a*(x3-p)^2+b],[a,b,p])
		* Maxima yields the following formula for parabolic interpolation
		2         2     2         2
		x1  y3 + (x3  - x1 ) y2 - x3  y1
		p = ------------------------------------
		2 x1 y3 + (2 x3 - 2 x1) y2 - 2 x3 y1

		*/
		// Scale super-resolution
		for (i = 0; i < nb_keypoints; i++) {
			CamKeypointShort *keypoint = &keypoints[i];
			#if 1
			int x = keypoint->x, y = keypoint->y;
			int v, max_scale;

			max_scale = (y + iROI.srcroi.yOffset) >> 1;
			v = (source->height - 1 - (y + iROI.srcroi.yOffset)) >> 1;
			if (v < max_scale) max_scale = v;
			v = (x + iROI.srcroi.xOffset) >> 1;
			if (v < max_scale) max_scale = v;
			v = (source->width -1 - (x + iROI.srcroi.xOffset)) >> 1;
			if (v < max_scale) max_scale = v;
			max_scale -= CAM_SCALE_MARGIN;

			if (keypoint->scale < max_scale) {
				unsigned int *ptr = ((unsigned int*)(integral->imageData + (y + iROI.srcroi.yOffset) * integral->widthStep)) + iROI.srcroi.xOffset + x;
				int scale = keypoint->scale - 1;
				int yoffset = scale * widthStep;
				int value, num, den, x1, x3, y1, y2, y3;
				x1 = -1;
				x3 = 1;
				y2 = keypoint->value;
				CAM_RECURSIVE_PATTERN;
				y1 = (value << 4) / (scale * scale);
				if (y1 > y2) keypoint->scale <<= 2; // This is a problem : this is not a local maximum in scale...
					else {
						int found = 0;
						scale = keypoint->scale + 1;
						do {
							yoffset = scale * widthStep;
							CAM_RECURSIVE_PATTERN;
							y3 = (value << 4) / (scale * scale);
							if (y3 <= y2) {
								num = (x1 * x1 * y3 + (x3 * x3 - x1 * x1) * y2 - x3 * x3 * y1);
								den = x1 * y3 + (x3 - x1) * y2 - x3 * y1;
								if (den == 0)
									keypoint->scale <<= 2;
								else {
									int p = (num << 1) / den; // shift only by 1, because den is shifted by 2
									keypoint->scale = p + ((scale - 1) << 2);
									found = 1;
								}
								break;
							}
							y1 = y2;
							y2 = y3;
							scale++;
						} while (scale <= max_scale);
						if (!found) keypoint->scale <<= 2;
					}
			}
			else
				#endif
				keypoint->scale <<= 2;
		}

		// Angle
		if (options & CAM_UPRIGHT) {
			for (i = 0; i < nb_keypoints; i++) {
				keypoints[i].angle = 0;
			}
		} else {
			camAllocateImage(&filter, CAM_ORIENTATION_STAMP_SIZE, CAM_ORIENTATION_STAMP_SIZE, CAM_DEPTH_16S);
			camBuildGaussianFilter(&filter, camSigmaParam);
			pnb_keypoints = nb_keypoints;
			if (pnb_keypoints > nb_max_keypoints) pnb_keypoints = nb_max_keypoints;
			for (i = 0; i < pnb_keypoints; i++) {
				nb_keypoints += camKeypointOrientation(source, &keypoints[i], &filter, &keypoints[nb_keypoints]);
			}
			camDeallocateImage(&filter);
		}

		// Sort again the features according to value
		qsort(keypoints, nb_keypoints, sizeof(CamKeypointShort), camSortKeypointsShort);

		// Keypoints allocation
		pnb_keypoints = nb_keypoints;
		if (pnb_keypoints > nb_max_keypoints) pnb_keypoints = nb_max_keypoints;
		if (points->allocated == 0) {
			camAllocateKeypoints(points, pnb_keypoints);
		} else if (points->allocated < pnb_keypoints) {
			camFreeKeypoints(points);
			camAllocateKeypoints(points, pnb_keypoints);
		}
		if (points->bag == NULL) {
			#ifdef __SSE2__
			points->bag = (CamKeypoint*)_mm_malloc(sizeof(CamKeypoint) * pnb_keypoints, 16);
			#else
			points->bag = (CamKeypoint*)malloc(sizeof(CamKeypoint) * pnb_keypoints);
			#endif
		}
		for (i = 0; i < pnb_keypoints; i++) {
			points->keypoint[i] = &points->bag[i];
			points->keypoint[i]->x = keypoints[i].x + iROI.srcroi.xOffset;
			points->keypoint[i]->y = keypoints[i].y + iROI.srcroi.yOffset;
			points->keypoint[i]->scale = keypoints[i].scale;
			points->keypoint[i]->value = keypoints[i].value;
			points->keypoint[i]->angle = keypoints[i].angle;
		}
		points->nbPoints = pnb_keypoints;
		free(keypoints);

		if (options & CAM_UPRIGHT) {
			camKeypointsDescriptor(points, integral, options);
		} else {
			camKeypointsDescriptor(points, source, options);
		}

		// Finally, set the points' set
		for (i = 0; i < points->nbPoints; i++) {
			points->keypoint[i]->set = points;
		}

		// Integral Image is now useless
		camInternalROIPolicyExit(&iROI);
		return 1;
}