Пример #1
0
DMZ_INTERNAL void save_image_groups(IplImage *image, GroupedRectsList &groups) {
  if (!groups.size()) {
    return;
  }
  
  IplImage *rects_image = cvCreateImage(cvGetSize(image), image->depth, image->nChannels);
  cvCopy(image, rects_image);
  
  int min_top = SHRT_MAX;
  int max_top = 0;
  for (GroupedRectsListIterator group = groups.begin(); group != groups.end(); ++group) {
    add_rects_to_image(rects_image, group->character_rects, group->character_width);
    
    cvRectangleR(rects_image, cvRect(group->left - 1, group->top - 1, group->width + 2, group->height + 2), cvScalar(200.0f));
    
    if (group->top < min_top) {
      min_top = group->top;
    }
    if (group->top > max_top) {
      max_top = group->top;
    }
  }
  
  cvSetImageROI(rects_image, cvRect(0,
                                    min_top - kSmallCharacterHeight,
                                    rects_image->width,
                                    MIN(max_top + 2 * kSmallCharacterHeight, image->height) - (min_top - kSmallCharacterHeight)));
  ios_save_file(image_filename_string, rects_image);
  cvReleaseImage(&rects_image);
}
// Draw graphs      -- todo, adjust to fit in window
void Painting::drawGraph()
{
    // Paint the canvas white
    cvZero(mImage);
    cvRectangleR(mImage,cvRect(0,0,500,500),cvScalar(255,255,255), -1);

    CvFont font;
    double hScale=0.5;
    double vScale=0.5;
    int    lineWidth=1;
    cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX|CV_FONT_ITALIC, hScale,vScale,0,lineWidth);

    // Add text to canvas
    cvPutText (mImage,"Left",cvPoint(20,50), &font, cvScalar(200,0,0));
    cvPutText (mImage,"Right",cvPoint(420,50), &font, cvScalar(0,200,0));

    // Iterate the vector of data and center the selected objects
    if(mData.size())
    {
        int xScale = 200;
        int yScale = 20;
        int xOffSet = 250 - mData[mSelect].timeStamp * xScale;
        int yOffSet1 = 240 + mData[mSelect].pulsefreq * yScale;
        int yOffSet2 = 250 + mData[mSelect].blinkingfreq * yScale;
        int yOffSet3 = 260 + mData[mSelect].breathingfreq * yScale;
        int t = 1; //thick

        if(mSelect == 0)
            t = -1;

        drawCircle(mImage,cvPoint(mData[0].timeStamp * xScale + xOffSet,yOffSet1 - mData[0].pulsefreq * yScale),0,t);
        drawCircle(mImage,cvPoint(mData[0].timeStamp * xScale + xOffSet,yOffSet2 - mData[0].blinkingfreq * yScale),1,t);
        drawCircle(mImage,cvPoint(mData[0].timeStamp * xScale + xOffSet,yOffSet3 - mData[0].breathingfreq * yScale),2,t);

        for(unsigned int i = 1; i<mData.size(); i++)
        {
            int thick = 1;
            if((unsigned)mSelect == i)
                thick = -1;
            // Pulse
            drawCircle(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet1 - mData[i].pulsefreq * yScale),0,thick);
            cvLine(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet1 - mData[i].pulsefreq * yScale),
                   cvPoint(mData[i-1].timeStamp * xScale + xOffSet, yOffSet1 - mData[i-1].pulsefreq * yScale),cvScalar(0,0,255));
            // Blinking
            drawCircle(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet2 - mData[i].blinkingfreq * yScale),1,thick);
            cvLine(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet2 - mData[i].blinkingfreq * yScale),
                   cvPoint(mData[i-1].timeStamp * xScale + xOffSet, yOffSet2 - mData[i-1].blinkingfreq * yScale),cvScalar(0,255,0));
            // Breathing
            drawCircle(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet3 - mData[i].breathingfreq * yScale),2,thick);
            cvLine(mImage,cvPoint(mData[i].timeStamp * xScale + xOffSet, yOffSet3 - mData[i].breathingfreq * yScale),
                   cvPoint(mData[i-1].timeStamp * xScale + xOffSet, yOffSet3 - mData[i-1].breathingfreq * yScale),cvScalar(255,0,0));
        }
    }

    cvShowImage(WindowName, mImage);

}
void FkPaperKeyboard_TypeA::setKeyButtonImage(IplImage* srcImage, IplImage* dstImage){
	cvCopy(srcImage, dstImage);
	for(int i =0 ; i < 16 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(255,0,0), 1);
	for(int i =16 ; i < 30 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(255,255,0), 1);
	for(int i =30 ; i < 44 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(0, 255, 255), 1);
	for(int i =44 ; i < 57 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(0,0,0), 1);
	for(int i =57 ; i < 69 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(170,10,120), 1);
	for(int i =69 ; i < 76 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(255,0,255), 1);
	for(int i =76 ; i < 80 ; i++)
		cvRectangleR(dstImage, keyButton[i].getKeyLocation(), CV_RGB(255,255,255), 1);
}
// Initialize window and variables
void Painting::drawInit(Painting* p)
{
    paint = p;  // Save pointer to class object

    // Create a window
    namedWindow(WindowName);
    cvSetMouseCallback(WindowName, mouse, NULL);
    cvMoveWindow(WindowName, 50, 50);
    IplImage* image = cvCreateImage(cvSize(500,500),IPL_DEPTH_8U,3);
    cvZero( image );
    cvRectangleR(image,cvRect(0,0,500,500),cvScalar(255,255,255), -1);
    cvResizeWindow(WindowName, 500, 500);

    mImage = image;
    mSelect = 0;

    mLeftButton = cvRect(10, 10, 100, 100);
    mRightButton = cvRect(490, 10, 100, 100);
}
// Draw rectangle
IplImage* Painting::drawRect(IplImage * img, CvRect rect){
    cvRectangleR(img,rect,cvScalar(255,0,0), 5);

    return img;
}
int catcierge_haar_matcher_find_prey_adaptive(catcierge_haar_matcher_t *ctx,
											IplImage *img, IplImage *inv_thr_img,
											match_result_t *result, int save_steps)
{
	IplImage *inv_adpthr_img = NULL;
	IplImage *inv_combined = NULL;
	IplImage *open_combined = NULL;
	IplImage *dilate_combined = NULL;
	CvSeq *contours = NULL;
	size_t contour_count = 0;
	CvSize img_size;
	assert(ctx);
	assert(img);
	assert(ctx->args);

	img_size = cvGetSize(img);

	// We expect to be given an inverted global thresholded image (inv_thr_img)
	// that contains the rough cat profile.

	// Do an inverted adaptive threshold of the original image as well.
	// This brings out small details such as a mouse tail that fades
	// into the background during a global threshold.
	inv_adpthr_img = cvCreateImage(img_size, 8, 1);
	cvAdaptiveThreshold(img, inv_adpthr_img, 255,
		CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 11, 5);
	catcierge_haar_matcher_save_step_image(ctx,
		inv_adpthr_img, result, "adp_thresh", "Inverted adaptive threshold", save_steps);

	// Now we can combine the two thresholded images into one.
	inv_combined = cvCreateImage(img_size, 8, 1);
	cvAdd(inv_thr_img, inv_adpthr_img, inv_combined, NULL);
	catcierge_haar_matcher_save_step_image(ctx,
		inv_combined, result, "inv_combined", "Combined global and adaptive threshold", save_steps);

	// Get rid of noise from the adaptive threshold.
	open_combined = cvCreateImage(img_size, 8, 1);
	cvMorphologyEx(inv_combined, open_combined, NULL, ctx->kernel2x2, CV_MOP_OPEN, 2);
	catcierge_haar_matcher_save_step_image(ctx,
		open_combined, result, "opened", "Opened image", save_steps);

	dilate_combined = cvCreateImage(img_size, 8, 1);
	cvDilate(open_combined, dilate_combined, ctx->kernel3x3, 3);
	catcierge_haar_matcher_save_step_image(ctx,
		dilate_combined, result, "dilated", "Dilated image", save_steps);

	// Invert back the result so the background is white again.
	cvNot(dilate_combined, dilate_combined);
	catcierge_haar_matcher_save_step_image(ctx,
		dilate_combined, result, "combined", "Combined binary image", save_steps);

	cvFindContours(dilate_combined, ctx->storage, &contours,
		sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));

	// If we get more than 1 contour we count it as a prey.
	contour_count = catcierge_haar_matcher_count_contours(ctx, contours);

	if (save_steps)
	{
		IplImage *img_contour = cvCloneImage(img);
		IplImage *img_final_color = NULL;
		CvScalar color;

		cvDrawContours(img_contour, contours, cvScalarAll(255), cvScalarAll(0), 1, 1, 8, cvPoint(0, 0));
		catcierge_haar_matcher_save_step_image(ctx,
			img_contour, result, "contours", "Background contours", save_steps);

		// Draw a final color combined image with the Haar detection + contour.
		cvResetImageROI(img_contour);

		img_final_color =  cvCreateImage(cvGetSize(img_contour), 8, 3);

		cvCvtColor(img_contour, img_final_color, CV_GRAY2BGR);
		color = (contour_count > 1) ? CV_RGB(255, 0, 0) : CV_RGB(0, 255, 0);
		cvRectangleR(img_final_color, result->match_rects[0], color, 2, 8, 0);

		catcierge_haar_matcher_save_step_image(ctx,
			img_final_color, result, "final", "Final image", save_steps);

		cvReleaseImage(&img_contour);
		cvReleaseImage(&img_final_color);
	}

	cvReleaseImage(&inv_adpthr_img);
	cvReleaseImage(&inv_combined);
	cvReleaseImage(&open_combined);
	cvReleaseImage(&dilate_combined);

	return (contour_count > 1);
}
Пример #7
0
DMZ_INTERNAL void add_rects_to_image(IplImage *image, CharacterRectList &rect_list, int character_width) {
  for (CharacterRectListIterator rect = rect_list.begin(); rect != rect_list.end(); ++rect) {
    cvRectangleR(image, cvRect(rect->left, rect->top, character_width, kSmallCharacterHeight), cvScalar(SHRT_MAX));
  }
}
Пример #8
0
int main(int argc, char **argv)
{
	int ret = 0;
	catcierge_matcher_t *matcher = NULL;
	char *img_paths[4096];
	IplImage *imgs[4096];
	size_t img_count = 0;
	IplImage *img = NULL;
	CvSize img_size;
	CvScalar match_color;
	int match_success = 0;
	double match_res = 0;
	int debug = 0;
	int i;
	int j;
	int show = 0;
	int save = 0;
	char *output_path = "output";
	double match_threshold = 0.8;
	int success_count = 0;
	int preload = 0;
	int test_matchable = 0;
	const char *matcher_str = NULL;
	match_result_t result;

	clock_t start;
	clock_t end;
	catcierge_template_matcher_args_t args;
	catcierge_haar_matcher_args_t hargs;
	char *key = NULL;
	char *values[4096];
	size_t value_count = 0;
	memset(&args, 0, sizeof(args));
	memset(&result, 0, sizeof(result));

	fprintf(stderr, "Catcierge Image match Tester (C) Joakim Soderberg 2013-2014\n");

	if (argc < 4)
	{
		fprintf(stderr, "Usage: %s\n"
						"          [--output [path]]\n"
						"          [--debug]\n"
						"          [--show]\n"
						"          [--match_flipped <0|1>]\n"
						"          [--threshold]\n"
						"          [--preload]\n"
						"          [--test_matchable]\n"
						"          [--snout <snout images for template matching>]\n"
						"          [--cascade <haar cascade xml>]\n"
						"           --images <input images>\n"
						"           --matcher <template|haar>\n", argv[0]);
		return -1;
	}

	catcierge_haar_matcher_args_init(&hargs);
	catcierge_template_matcher_args_init(&args);

	for (i = 1; i < argc; i++)
	{
		if (!strcmp(argv[i], "--show"))
		{
			show = 1;
			continue;
		}
		else if (!strcmp(argv[i], "--output"))
		{
			save = 1;

			if ((i + 1) < argc)
			{
				if (strncmp(argv[i+1], "--", 2))
				{
					i++;
					output_path = argv[i];
				}
			}
			continue;
		}
		else if (!strcmp(argv[i], "--test_matchable")
				|| !strcmp(argv[i], "--test_obstructed"))
		{
			test_matchable = 1;
			preload = 1;
		}
		else if (!strcmp(argv[i], "--debug"))
		{
			debug = 1;
		}
		else if (!strcmp(argv[i], "--images"))
		{
			while (((i + 1) < argc) 
				&& strncmp(argv[i+1], "--", 2))
			{
				i++;
				img_paths[img_count] = argv[i];
				img_count++;
			}
		}
		else if (!strcmp(argv[i], "--preload"))
		{
			if ((i + 1) < argc)
			{
				i++;
				preload = 1;
				continue;
			}
		}
		else if (!strcmp(argv[i], "--matcher"))
		{
			if ((i + 1) < argc)
			{
				if (strncmp(argv[i+1], "--", 2))
				{
					i++;
					matcher_str = argv[i];

					if (strcmp(matcher_str, "template") && strcmp(matcher_str, "haar"))
					{
						fprintf(stderr, "Invalid matcher type \"%s\"\n", matcher_str);
						return -1;
					}
				}
			}
			continue;
		}
		else if (!strncmp(argv[i], "--", 2))
		{
			int j = i + 1;
			key = &argv[i][2];
			memset(values, 0, value_count * sizeof(char *));
			value_count = 0;

			// Look for values for the option.
			// Continue fetching values until we get another option
			// or there are no more options.
			while ((j < argc) && strncmp(argv[j], "--", 2))
			{
				values[value_count] = argv[j];
				value_count++;
				i++;
				j = i + 1;
			}

			if ((ret = parse_arg(&args, &hargs, key, values, value_count)) < 0)
			{
				fprintf(stderr, "Failed to parse command line arguments for \"%s\"\n", key);
				return ret;
			}
		}
		else
		{
			fprintf(stderr, "Unknown command line argument \"%s\"\n", argv[i]);
			return -1;
		}
	}

	if (!matcher_str)
	{
		fprintf(stderr, "You must specify a matcher type\n");
		return -1;
	}

	if (!strcmp(matcher_str, "template") && (args.snout_count == 0))
	{
		fprintf(stderr, "No snout image specified\n");
		return -1;
	}

	if (!strcmp(matcher_str, "haar") && !hargs.cascade)
	{
		fprintf(stderr, "No haar cascade specified\n");
		return -1;
	}

	if (img_count == 0)
	{
		fprintf(stderr, "No input image specified\n");
		return -1;
	}

	// Create output directory.
	if (save)
	{
		catcierge_make_path("%s", output_path);
	}

	args.super.type = MATCHER_TEMPLATE;
	hargs.super.type = MATCHER_HAAR;

	if (catcierge_matcher_init(&matcher,
		(!strcmp(matcher_str, "template")
		? (catcierge_matcher_args_t *)&args
		: (catcierge_matcher_args_t *)&hargs)))
	{
		fprintf(stderr, "Failed to init %s matcher.\n", matcher_str);
			return -1;
	}

	matcher->debug = debug;
	if (!matcher->is_obstructed)
		matcher->is_obstructed = catcierge_is_frame_obstructed;
	//catcierge_set_binary_thresholds(&ctx, 90, 200);

	// If we should preload the images or not
	// (Don't let file IO screw with benchmark)
	if (preload)
	{
		for (i = 0; i < (int)img_count; i++)
		{
			printf("Preload image %s\n", img_paths[i]);

			if (!(imgs[i] = cvLoadImage(img_paths[i], 1)))
			{
				fprintf(stderr, "Failed to load match image: %s\n", img_paths[i]);
				ret = -1;
				goto fail;
			}
		}
	}

	start = clock();

	if (test_matchable)
	{
		for (i = 0; i < (int)img_count; i++)
		{
			// This tests if an image frame is clear or not (matchable).
			int frame_obstructed;

			if ((frame_obstructed = matcher->is_obstructed(matcher, imgs[i])) < 0)
			{
				fprintf(stderr, "Failed to detect check for matchability frame\n");
				return -1;
			}

			printf("%s: Frame obstructed = %d\n",
				img_paths[i], frame_obstructed);

			if (show)
			{
				cvShowImage("image", imgs[i]);
				cvWaitKey(0);
			}
		}
	}
	else
	{
		for (i = 0; i < (int)img_count; i++)
		{
			match_success = 0;

			printf("---------------------------------------------------\n");
			printf("%s:\n", img_paths[i]);

			if (preload)
			{
				img = imgs[i];
			}
			else
			{
				if (!(img = cvLoadImage(img_paths[i], 1)))
				{
					fprintf(stderr, "Failed to load match image: %s\n", img_paths[i]);
					goto fail;
				}
			}

			img_size = cvGetSize(img);

			printf("  Image size: %dx%d\n", img_size.width, img_size.height);


			if ((match_res = matcher->match(matcher, img, &result, 0)) < 0)
			{
				fprintf(stderr, "Something went wrong when matching image: %s\n", img_paths[i]);
				catcierge_matcher_destroy(&matcher);
				return -1;
			}

			match_success = (match_res >= match_threshold);

			if (match_success)
			{
				printf("  Match (%s)! %f\n", catcierge_get_direction_str(result.direction), match_res);
				match_color = CV_RGB(0, 255, 0);
				success_count++;
			}
			else
			{
				printf("  No match! %f\n", match_res);
				match_color = CV_RGB(255, 0, 0);
			}

			if (show || save)
			{
				for (j = 0; j < (int)result.rect_count; j++)
				{
					printf("x: %d\n", result.match_rects[j].x);
					printf("y: %d\n", result.match_rects[j].y);
					printf("w: %d\n", result.match_rects[j].width);
					printf("h: %d\n", result.match_rects[j].height);
					cvRectangleR(img, result.match_rects[j], match_color, 1, 8, 0);
				}

				if (show)
				{
					cvShowImage("image", img);
					cvWaitKey(0);
				}

				if (save)
				{
					char out_file[PATH_MAX]; 
					char tmp[PATH_MAX];
					char *filename = tmp;
					char *ext;
					char *start;

					// Get the extension.
					strncpy(tmp, img_paths[i], sizeof(tmp));
					ext = strrchr(tmp, '.');
					*ext = '\0';
					ext++;

					// And filename.
					filename = strrchr(tmp, '/');
					start = strrchr(tmp, '\\');
					if (start> filename)
						filename = start;
					filename++;

					snprintf(out_file, sizeof(out_file) - 1, "%s/match_%s__%s.%s", 
							output_path, match_success ? "ok" : "fail", filename, ext);

					printf("Saving image \"%s\"\n", out_file);

					cvSaveImage(out_file, img, 0);
				}
			}

			cvReleaseImage(&img);
		}
	}

	end = clock();

	if (!test_matchable)
	{
		printf("Note that this time isn't useful with --show\n");
		printf("%d of %d successful! (%f seconds)\n",
			success_count, (int)img_count, (float)(end - start) / CLOCKS_PER_SEC);
	}

fail:
	catcierge_matcher_destroy(&matcher);
	cvDestroyAllWindows();

	return ret;
}
Пример #9
0
int main(int argc, char **argv)
{
	int ret = 0;
	catcierge_matcher_t *matcher = NULL;
	IplImage *img = NULL;
	CvSize img_size;
	CvScalar match_color;
	int match_success = 0;
	double match_res = 0;
	int i;
	int j;
	int success_count = 0;
	match_result_t result;

	clock_t start;
	clock_t end;
	catcierge_args_t args;
	memset(&args, 0, sizeof(args));
	memset(&result, 0, sizeof(result));

	fprintf(stderr, "Catcierge Image match Tester (C) Joakim Soderberg 2013-2016\n");

	if (catcierge_args_init(&args, argv[0]))
	{
		fprintf(stderr, "Failed to init args\n");
		return -1;
	}

	if (add_options(&args))
	{
		fprintf(stderr, "Failed to init tester args\n");
		ret = -1; goto fail;
	}

	if (catcierge_args_parse(&args, argc, argv))
	{
		ret = -1; goto fail;
	}

	// Create output directory.
	if (ctx.save)
	{
		catcierge_make_path("%s", ctx.output_path);
	}

	if (catcierge_matcher_init(&matcher, catcierge_get_matcher_args(&args)))
	{
		fprintf(stderr, "\n\nFailed to %s init matcher\n\n", matcher->name);
		return -1;
	}

	matcher->debug = ctx.debug;

	if (!(ctx.imgs = calloc(ctx.img_count, sizeof(IplImage *))))
	{
		fprintf(stderr, "Out of memory!\n");
		ret = -1;
		goto fail;
	}

	// TODO: Move to function
	// If we should preload the images or not
	// (Don't let file IO screw with benchmark)
	if (ctx.preload)
	{
		for (i = 0; i < (int)ctx.img_count; i++)
		{
			printf("Preload image %s\n", ctx.img_paths[i]);

			if (!(ctx.imgs[i] = cvLoadImage(ctx.img_paths[i], 1)))
			{
				fprintf(stderr, "Failed to load match image: %s\n", ctx.img_paths[i]);
				ret = -1;
				goto fail;
			}
		}
	}

	start = clock();

	if (ctx.test_matchable)
	{
		for (i = 0; i < (int)ctx.img_count; i++)
		{
			// This tests if an image frame is clear or not (matchable).
			int frame_obstructed;

			if ((frame_obstructed = matcher->is_obstructed(matcher, ctx.imgs[i])) < 0)
			{
				fprintf(stderr, "Failed to detect check for matchability frame\n");
				return -1;
			}

			printf("%s: Frame obstructed = %d\n",
				ctx.img_paths[i], frame_obstructed);

			if (ctx.show)
			{
				cvShowImage("image", ctx.imgs[i]);
				cvWaitKey(0);
			}
		}
	}
	else
	{
		for (i = 0; i < (int)ctx.img_count; i++)
		{
			match_success = 0;

			printf("---------------------------------------------------\n");
			printf("%s:\n", ctx.img_paths[i]);

			if (ctx.preload)
			{
				img = ctx.imgs[i];
			}
			else
			{
				if (!(img = cvLoadImage(ctx.img_paths[i], 1)))
				{
					fprintf(stderr, "Failed to load match image: %s\n", ctx.img_paths[i]);
					goto fail;
				}
			}

			img_size = cvGetSize(img);

			printf("  Image size: %dx%d\n", img_size.width, img_size.height);


			if ((match_res = matcher->match(matcher, img, &result, 0)) < 0)
			{
				fprintf(stderr, "Something went wrong when matching image: %s\n", ctx.img_paths[i]);
				catcierge_matcher_destroy(&matcher);
				return -1;
			}

			match_success = (match_res >= args.templ.match_threshold);

			if (match_success)
			{
				printf("  Match (%s)! %f\n", catcierge_get_direction_str(result.direction), match_res);
				match_color = CV_RGB(0, 255, 0);
				success_count++;
			}
			else
			{
				printf("  No match! %f\n", match_res);
				match_color = CV_RGB(255, 0, 0);
			}

			if (ctx.show || ctx.save)
			{
				for (j = 0; j < (int)result.rect_count; j++)
				{
					printf("x: %d\n", result.match_rects[j].x);
					printf("y: %d\n", result.match_rects[j].y);
					printf("w: %d\n", result.match_rects[j].width);
					printf("h: %d\n", result.match_rects[j].height);
					cvRectangleR(img, result.match_rects[j], match_color, 1, 8, 0);
				}

				if (ctx.show)
				{
					cvShowImage("image", img);
					cvWaitKey(0);
				}

				if (ctx.save)
				{
					char out_file[PATH_MAX]; 
					char tmp[PATH_MAX];
					char *filename = tmp;
					char *ext;
					char *start;

					// Get the extension.
					strncpy(tmp, ctx.img_paths[i], sizeof(tmp));
					ext = strrchr(tmp, '.');
					*ext = '\0';
					ext++;

					// And filename.
					filename = strrchr(tmp, '/');
					start = strrchr(tmp, '\\');
					if (start> filename)
						filename = start;
					filename++;

					snprintf(out_file, sizeof(out_file) - 1, "%s/match_%s__%s.%s", 
							ctx.output_path, match_success ? "ok" : "fail", filename, ext);

					printf("Saving image \"%s\"\n", out_file);

					cvSaveImage(out_file, img, 0);
				}
			}

			cvReleaseImage(&img);
		}
	}

	end = clock();

	if (!ctx.test_matchable)
	{
		if (ctx.show)
		{
			printf("Note that the duration is affected by using --show\n");
		}
		printf("%d of %d successful! (%f seconds)\n",
			success_count, (int)ctx.img_count, (float)(end - start) / CLOCKS_PER_SEC);
	}

fail:
	catcierge_matcher_destroy(&matcher);
	cvDestroyAllWindows();

	return ret;
}