diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c
index 1c60aca..e475787 100644
--- a/libfprint/drivers/aes2501.c
+++ b/libfprint/drivers/aes2501.c
@@ -630,7 +630,7 @@ static int capture(struct fp_img_dev *dev, gboolean unconditional,
 	*ret = img;
 	return 0;
 err:
-	g_free(img);
+	fp_img_free(img);
 	return r;
 }
 
diff --git a/libfprint/drivers/aes4000.c b/libfprint/drivers/aes4000.c
index 8198132..4b43346 100644
--- a/libfprint/drivers/aes4000.c
+++ b/libfprint/drivers/aes4000.c
@@ -181,7 +181,7 @@ retry:
 	return 0;
 err:
 	g_free(data);
-	g_free(img);
+	fp_img_free(img);
 	return r;
 }
 
diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c
index 56ac398..51b57c3 100644
--- a/libfprint/drivers/uru4000.c
+++ b/libfprint/drivers/uru4000.c
@@ -360,7 +360,7 @@ static int capture(struct fp_img_dev *dev, gboolean unconditional,
 	*ret = img;
 	return 0;
 err:
-	g_free(img);
+	fp_img_free(img);
 	return r;
 }
 
diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h
index 1e6b684..2fa7dad 100644
--- a/libfprint/fp_internal.h
+++ b/libfprint/fp_internal.h
@@ -191,16 +191,43 @@ gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
 	enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
 	enum fp_print_data_type type2);
 
+struct fp_minutia {
+	int x;
+	int y;
+	int ex;
+	int ey;
+	int direction;
+	double reliability;
+	int type;
+	int appearing;
+	int feature_id;
+	int *nbrs;
+	int *ridge_counts;
+	int num_nbrs;
+};
+
+struct fp_minutiae {
+	int alloc;
+	int num;
+	struct fp_minutia **list;
+};
+
 /* bit values for fp_img.flags */
 #define FP_IMG_V_FLIPPED 		(1<<0)
 #define FP_IMG_H_FLIPPED 		(1<<1)
 #define FP_IMG_COLORS_INVERTED	(1<<2)
+#define FP_IMG_BINARIZED_FORM	(1<<3)
+
+#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
+	| FP_IMG_COLORS_INVERTED)
 
 struct fp_img {
 	int width;
 	int height;
 	size_t length;
 	uint16_t flags;
+	struct fp_minutiae *minutiae;
+	unsigned char *binarized;
 	unsigned char data[0];
 };
 
@@ -208,7 +235,8 @@ struct fp_img *fpi_img_new(size_t length);
 struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *dev);
 struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize);
 gboolean fpi_img_is_sane(struct fp_img *img);
-int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img,
+int fpi_img_detect_minutiae(struct fp_img *img);
+int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
 	struct fp_print_data **ret);
 int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
 	struct fp_print_data *new_print);
diff --git a/libfprint/fprint.h b/libfprint/fprint.h
index 334fe99..8eccd03 100644
--- a/libfprint/fprint.h
+++ b/libfprint/fprint.h
@@ -212,6 +212,7 @@ int fp_img_get_width(struct fp_img *img);
 unsigned char *fp_img_get_data(struct fp_img *img);
 int fp_img_save_to_file(struct fp_img *img, char *path);
 void fp_img_standardize(struct fp_img *img);
+struct fp_img *fp_img_binarize(struct fp_img *img);
 void fp_img_free(struct fp_img *img);
 
 /* Library */
diff --git a/libfprint/img.c b/libfprint/img.c
index 3db9248..d579a63 100644
--- a/libfprint/img.c
+++ b/libfprint/img.c
@@ -23,7 +23,6 @@
 #include <string.h>
 
 #include <glib.h>
-#include <magick/ImageMagick.h>
 
 #include "fp_internal.h"
 #include "nbis/include/bozorth.h"
@@ -90,6 +89,13 @@ struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize)
  */
 API_EXPORTED void fp_img_free(struct fp_img *img)
 {
+	if (!img)
+		return;
+
+	if (img->minutiae)
+		free_minutiae(img->minutiae);
+	if (img->binarized)
+		free(img->binarized);
 	g_free(img);
 }
 
@@ -227,55 +233,12 @@ API_EXPORTED void fp_img_standardize(struct fp_img *img)
 	}
 }
 
-static struct fp_img *im_resize(struct fp_img *img, unsigned int factor)
-{
-	Image *mimg;
-	Image *resized;
-	ExceptionInfo exception;
-	MagickBooleanType ret;
-	int new_width = img->width * factor;
-	int new_height = img->height * factor;
-	struct fp_img *newimg;
-
-	/* It is possible to implement resizing using a simple algorithm, however
-	 * we use ImageMagick because it applies some kind of smoothing to the
-	 * result, which improves matching performances in my experiments. */
-
-	if (!IsMagickInstantiated())
-		InitializeMagick(NULL);
-	
-	GetExceptionInfo(&exception);
-	mimg = ConstituteImage(img->width, img->height, "I", CharPixel, img->data,
-		&exception);
-
-	GetExceptionInfo(&exception);
-	resized = ResizeImage(mimg, new_width, new_height, 0, 1.0, &exception);
-
-	newimg = fpi_img_new(new_width * new_height);
-	newimg->width = new_width;
-	newimg->height = new_height;
-	newimg->flags = img->flags;
-
-	GetExceptionInfo(&exception);
-	ret = ExportImagePixels(resized, 0, 0, new_width, new_height, "I",
-		CharPixel, newimg->data, &exception);
-	if (ret != MagickTrue) {
-		fp_err("export failed");
-		return NULL;
-	}
-
-	DestroyImage(mimg);
-	DestroyImage(resized);
-
-	return newimg;
-}
-
 /* Based on write_minutiae_XYTQ and bz_load */
-static void minutiae_to_xyt(MINUTIAE *minutiae, int bwidth,
+static void minutiae_to_xyt(struct fp_minutiae *minutiae, int bwidth,
 	int bheight, unsigned char *buf)
 {
 	int i;
-	MINUTIA *minutia;
+	struct fp_minutia *minutia;
 	struct minutiae_struct c[MAX_FILE_MINUTIAE];
 	struct xyt_struct *xyt = (struct xyt_struct *) buf;
 
@@ -305,29 +268,20 @@ static void minutiae_to_xyt(MINUTIAE *minutiae, int bwidth,
 	xyt->nrows = nmin;
 }
 
-int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *_img,
-	struct fp_print_data **ret)
+int fpi_img_detect_minutiae(struct fp_img *img)
 {
-	MINUTIAE *minutiae;
+	struct fp_minutiae *minutiae;
 	int r;
 	int *direction_map, *low_contrast_map, *low_flow_map;
 	int *high_curve_map, *quality_map;
 	int map_w, map_h;
 	unsigned char *bdata;
 	int bw, bh, bd;
-	struct fp_print_data *print;
-	struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(imgdev->dev->drv);
-	struct fp_img *img = _img;
-	int free_img = 0;
 	GTimer *timer;
 
-	if (imgdrv->enlarge_factor) {
-		/* FIXME: enlarge_factor should not exist! instead, MINDTCT should
-		 * actually look at the value of the pixels-per-mm parameter and
-		 * figure out itself when the image needs to be treated as if it
-		 * were bigger. */
-		img = im_resize(_img, imgdrv->enlarge_factor);
-		free_img = 1;
+	if (img->flags & FP_IMG_STANDARDIZATION_FLAGS) {
+		fp_err("cant detect minutiae for non-standardized image");
+		return -EINVAL;
 	}
 
 	/* 25.4 mm per inch */
@@ -340,34 +294,50 @@ int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *_img,
 	g_timer_stop(timer);
 	fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
 	g_timer_destroy(timer);
-	if (free_img)
-		g_free(img);
 	if (r) {
 		fp_err("get minutiae failed, code %d", r);
 		return r;
 	}
 	fp_dbg("detected %d minutiae", minutiae->num);
-	r = minutiae->num;
+	img->minutiae = minutiae;
+	img->binarized = bdata;
 
-	/* FIXME: space is wasted if we dont hit the max minutiae count. would
-	 * be good to make this dynamic. */
-	print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
-	print->type = PRINT_DATA_NBIS_MINUTIAE;
-	minutiae_to_xyt(minutiae, bw, bh, print->data);
-	/* FIXME: the print buffer at this point is endian-specific, and will
-	 * only work when loaded onto machines with identical endianness. not good!
-	 * data format should be platform-independant. */
-	*ret = print;
-
-	free_minutiae(minutiae);
 	free(quality_map);
 	free(direction_map);
 	free(low_contrast_map);
 	free(low_flow_map);
 	free(high_curve_map);
-	free(bdata);
+	return minutiae->num;
+}
 
-	return r;
+int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
+	struct fp_print_data **ret)
+{
+	struct fp_print_data *print;
+	int r;
+
+	if (!img->minutiae) {
+		r = fpi_img_detect_minutiae(img);
+		if (r < 0)
+			return r;
+		if (!img->minutiae) {
+			fp_err("no minutiae after successful detection?");
+			return -ENOENT;
+		}
+	}
+
+	/* FIXME: space is wasted if we dont hit the max minutiae count. would
+	 * be good to make this dynamic. */
+	print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
+	print->type = PRINT_DATA_NBIS_MINUTIAE;
+	minutiae_to_xyt(img->minutiae, img->width, img->height, print->data);
+
+	/* FIXME: the print buffer at this point is endian-specific, and will
+	 * only work when loaded onto machines with identical endianness. not good!
+	 * data format should be platform-independant. */
+	*ret = print;
+
+	return 0;
 }
 
 int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
@@ -393,3 +363,50 @@ int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
 
 	return r;
 }
+
+/** \ingroup img
+ * Get a binarized form of a standardized scanned image. This is where the
+ * fingerprint image has been "enhanced" and is a set of pure black ridges
+ * on a pure white background. Internally, image processing happens on top
+ * of the binarized image.
+ *
+ * The image must have been \ref img_std "standardized" otherwise this function
+ * will fail.
+ *
+ * It is safe to binarize an image and free the original while continuing
+ * to use the binarized version.
+ *
+ * You cannot binarize an image twice.
+ *
+ * \param img a standardized image
+ * \returns a new image representing the binarized form of the original, or
+ * NULL on error. Must be freed with fp_img_free() after use.
+ */
+API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
+{
+	struct fp_img *ret;
+	int height = img->height;
+	int width = img->width;
+	int imgsize = height * width;
+
+	if (img->flags & FP_IMG_BINARIZED_FORM) {
+		fp_err("image already binarized");
+		return NULL;
+	}
+
+	if (!img->binarized) {
+		int r = fpi_img_detect_minutiae(img);
+		if (r < 0)
+			return NULL;
+		if (!img->binarized)
+			fp_err("no minutiae after successful detection?");
+	}
+
+	ret = fpi_img_new(imgsize);
+	ret->flags |= FP_IMG_BINARIZED_FORM;
+	ret->width = width;
+	ret->height = height;
+	memcpy(ret->data, img->binarized, imgsize);
+	return ret;
+}
+
diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c
index 1e99998..f072959 100644
--- a/libfprint/imgdev.c
+++ b/libfprint/imgdev.c
@@ -18,7 +18,9 @@
  */
 
 #include <errno.h>
+
 #include <glib.h>
+#include <magick/ImageMagick.h>
 
 #include "fp_internal.h"
 
@@ -62,14 +64,65 @@ int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev)
 {
 	struct fp_driver *drv = imgdev->dev->drv;
 	struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
-	return imgdrv->img_width;
+	int width = imgdrv->img_width;
+
+	if (width > 0 && imgdrv->enlarge_factor > 1)
+		width *= imgdrv->enlarge_factor;
+	return width;
 }
 
 int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev)
 {
 	struct fp_driver *drv = imgdev->dev->drv;
 	struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
-	return imgdrv->img_height;
+	int height = imgdrv->img_height;
+
+    if (height > 0 && imgdrv->enlarge_factor > 1)
+		height *= imgdrv->enlarge_factor;
+	return height;
+}
+
+static struct fp_img *im_resize(struct fp_img *img, unsigned int factor)
+{
+	Image *mimg;
+	Image *resized;
+	ExceptionInfo exception;
+	MagickBooleanType ret;
+	int new_width = img->width * factor;
+	int new_height = img->height * factor;
+	struct fp_img *newimg;
+
+	/* It is possible to implement resizing using a simple algorithm, however
+	 * we use ImageMagick because it applies some kind of smoothing to the
+	 * result, which improves matching performances in my experiments. */
+
+	if (!IsMagickInstantiated())
+		InitializeMagick(NULL);
+	
+	GetExceptionInfo(&exception);
+	mimg = ConstituteImage(img->width, img->height, "I", CharPixel, img->data,
+		&exception);
+
+	GetExceptionInfo(&exception);
+	resized = ResizeImage(mimg, new_width, new_height, 0, 1.0, &exception);
+
+	newimg = fpi_img_new(new_width * new_height);
+	newimg->width = new_width;
+	newimg->height = new_height;
+	newimg->flags = img->flags;
+
+	GetExceptionInfo(&exception);
+	ret = ExportImagePixels(resized, 0, 0, new_width, new_height, "I",
+		CharPixel, newimg->data, &exception);
+	if (ret != MagickTrue) {
+		fp_err("export failed");
+		return NULL;
+	}
+
+	DestroyImage(mimg);
+	DestroyImage(resized);
+
+	return newimg;
 }
 
 int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
@@ -147,6 +200,16 @@ int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
 		goto err;
 	}
 
+	if (imgdrv->enlarge_factor > 1) {
+		/* FIXME: enlarge_factor should not exist! instead, MINDTCT should
+		 * actually look at the value of the pixels-per-mm parameter and
+		 * figure out itself when the image needs to be treated as if it
+		 * were bigger. */
+		struct fp_img *tmp = im_resize(img, imgdrv->enlarge_factor);
+		fp_img_free(img);
+		img = tmp;
+	}
+
 	*_img = img;
 	return 0;
 err:
@@ -178,10 +241,10 @@ int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage,
 	if (r)
 		return r;
 
-	r = fpi_img_detect_minutiae(imgdev, img, &print);
+	r = fpi_img_to_print_data(imgdev, img, &print);
 	if (r < 0)
 		return r;
-	if (r < MIN_ACCEPTABLE_MINUTIAE) {
+	if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
 		fp_dbg("not enough minutiae, %d/%d", r, MIN_ACCEPTABLE_MINUTIAE);
 		fp_print_data_free(print);
 		return FP_ENROLL_RETRY;
@@ -214,10 +277,10 @@ static int img_dev_verify(struct fp_dev *dev,
 	if (r)
 		return r;
 
-	r = fpi_img_detect_minutiae(imgdev, img, &print);
+	r = fpi_img_to_print_data(imgdev, img, &print);
 	if (r < 0)
 		return r;
-	if (r < MIN_ACCEPTABLE_MINUTIAE) {
+	if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
 		fp_dbg("not enough minutiae, %d/%d", r, MIN_ACCEPTABLE_MINUTIAE);
 		fp_print_data_free(print);
 		return FP_VERIFY_RETRY;
diff --git a/libfprint/nbis/include/lfs.h b/libfprint/nbis/include/lfs.h
index c184425..2da5d25 100644
--- a/libfprint/nbis/include/lfs.h
+++ b/libfprint/nbis/include/lfs.h
@@ -43,6 +43,7 @@ identified are necessarily the best available for the purpose.
 
 #include <math.h>
 #include <stdio.h>
+#include <fp_internal.h>
 
 /*************************************************************************/
 /*        OUTPUT FILE EXTENSIONS                                         */
@@ -130,26 +131,8 @@ typedef struct rotgrids{
 #define DISAPPEARING    0
 #define APPEARING       1
 
-typedef struct minutia{
-   int x;
-   int y;
-   int ex;
-   int ey;
-   int direction;
-   double reliability;
-   int type;
-   int appearing;
-   int feature_id;
-   int *nbrs;
-   int *ridge_counts;
-   int num_nbrs;
-} MINUTIA;
-
-typedef struct minutiae{
-   int alloc;
-   int num;
-   MINUTIA **list;
-} MINUTIAE;
+typedef struct fp_minutia MINUTIA;
+typedef struct fp_minutiae MINUTIAE;
 
 typedef struct feature_pattern{
    int type;