From 85b5fab4ab1d5742a5be7fd85522d045e61d8920 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Feb 2013 17:31:36 +0530 Subject: [PATCH] Add image comparison API to imagemagick bindings --- src/calibre/utils/magick/__init__.py | 3 + src/calibre/utils/magick/generate.py | 2 +- src/calibre/utils/magick/magick.c | 25 +++++++ src/calibre/utils/magick/magick_constants.h | 72 ++++++++++++++------- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/calibre/utils/magick/__init__.py b/src/calibre/utils/magick/__init__.py index 6be5580d17..01e0a7bdd8 100644 --- a/src/calibre/utils/magick/__init__.py +++ b/src/calibre/utils/magick/__init__.py @@ -200,6 +200,9 @@ class Image(_magick.Image): # {{{ raise ValueError('left and/or top out of bounds') _magick.Image.compose(self, img, int(left), int(top), op) + def compare(self, img, metric='RootMeanSquaredErrorMetric'): + return _magick.Image.compare(self, img, getattr(_magick, metric)) + def font_metrics(self, drawing_wand, text): if isinstance(text, unicode): text = text.encode('UTF-8') diff --git a/src/calibre/utils/magick/generate.py b/src/calibre/utils/magick/generate.py index 9dd225ee82..3547741426 100644 --- a/src/calibre/utils/magick/generate.py +++ b/src/calibre/utils/magick/generate.py @@ -50,7 +50,7 @@ def get_value(const): def main(): constants = [] for x in ('resample', 'image', 'draw', 'distort', 'composite', 'geometry', - 'colorspace'): + 'colorspace', 'compare'): constants += list(parse_enums('magick/%s.h'%x)) base = os.path.dirname(__file__) constants = [ diff --git a/src/calibre/utils/magick/magick.c b/src/calibre/utils/magick/magick.c index 6fbee2c77d..0a4c3bd866 100644 --- a/src/calibre/utils/magick/magick.c +++ b/src/calibre/utils/magick/magick.c @@ -494,6 +494,7 @@ typedef struct { // Method declarations {{{ static PyObject* magick_Image_compose(magick_Image *self, PyObject *args); +static PyObject* magick_Image_compare(magick_Image *self, PyObject *args); static PyObject* magick_Image_copy(magick_Image *self, PyObject *args); static PyObject* magick_Image_texture(magick_Image *self, PyObject *args); // }}} @@ -1196,6 +1197,10 @@ static PyMethodDef magick_Image_methods[] = { "compose(img, left, top, op) \n\n Compose img using operation op at (left, top)" }, + {"compare", (PyCFunction)magick_Image_compare, METH_VARARGS, + "compose(img, metric) \n\n Compare images using the specified metric. (One of AbsoluteErrorMetric, MeanAbsoluteErrorMetric, MeanErrorPerPixelMetric, MeanSquaredErrorMetric, PeakAbsoluteErrorMetric, PeakSignalToNoiseRatioMetric, RootMeanSquaredErrorMetric, NormalizedCrossCorrelationErrorMetric, FuzzErrorMetric)" + }, + {"texture", (PyCFunction)magick_Image_texture, METH_VARARGS, "texture(img)) \n\n Repeatedly tile img across and down the canvas." }, @@ -1380,6 +1385,26 @@ magick_Image_compose(magick_Image *self, PyObject *args) } // }}} +// Image.compose {{{ +static PyObject * +magick_Image_compare(magick_Image *self, PyObject *args) +{ + PyObject *img; + MetricType metric; + magick_Image *src; + double distortion = 0; + + NULL_CHECK(NULL) + + if (!PyArg_ParseTuple(args, "O!i", &magick_ImageType, &img, &metric)) return NULL; + src = (magick_Image*)img; + if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;} + + MagickCompareImages(self->wand, src->wand, metric, &distortion); + return Py_BuildValue("d", distortion); +} +// }}} + // Image.clone {{{ static PyObject * magick_Image_copy(magick_Image *self, PyObject *args) diff --git a/src/calibre/utils/magick/magick_constants.h b/src/calibre/utils/magick/magick_constants.h index 2967b921b7..f1f86ca206 100644 --- a/src/calibre/utils/magick/magick_constants.h +++ b/src/calibre/utils/magick/magick_constants.h @@ -14,25 +14,24 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "CubicFilter", 10); PyModule_AddIntConstant(m, "CatromFilter", 11); PyModule_AddIntConstant(m, "MitchellFilter", 12); - PyModule_AddIntConstant(m, "LanczosFilter", 13); - PyModule_AddIntConstant(m, "BesselFilter", 14); - PyModule_AddIntConstant(m, "SincFilter", 15); + PyModule_AddIntConstant(m, "JincFilter", 13); + PyModule_AddIntConstant(m, "SincFilter", 14); + PyModule_AddIntConstant(m, "SincFastFilter", 15); PyModule_AddIntConstant(m, "KaiserFilter", 16); PyModule_AddIntConstant(m, "WelshFilter", 17); PyModule_AddIntConstant(m, "ParzenFilter", 18); - PyModule_AddIntConstant(m, "LagrangeFilter", 19); - PyModule_AddIntConstant(m, "BohmanFilter", 20); - PyModule_AddIntConstant(m, "BartlettFilter", 21); - PyModule_AddIntConstant(m, "SentinelFilter", 22); - PyModule_AddIntConstant(m, "UndefinedInterpolatePixel", 0); - PyModule_AddIntConstant(m, "AverageInterpolatePixel", 1); - PyModule_AddIntConstant(m, "BicubicInterpolatePixel", 2); - PyModule_AddIntConstant(m, "BilinearInterpolatePixel", 3); - PyModule_AddIntConstant(m, "FilterInterpolatePixel", 4); - PyModule_AddIntConstant(m, "IntegerInterpolatePixel", 5); - PyModule_AddIntConstant(m, "MeshInterpolatePixel", 6); - PyModule_AddIntConstant(m, "NearestNeighborInterpolatePixel", 7); - PyModule_AddIntConstant(m, "SplineInterpolatePixel", 8); + PyModule_AddIntConstant(m, "BohmanFilter", 19); + PyModule_AddIntConstant(m, "BartlettFilter", 20); + PyModule_AddIntConstant(m, "LagrangeFilter", 21); + PyModule_AddIntConstant(m, "LanczosFilter", 22); + PyModule_AddIntConstant(m, "LanczosSharpFilter", 23); + PyModule_AddIntConstant(m, "Lanczos2Filter", 24); + PyModule_AddIntConstant(m, "Lanczos2SharpFilter", 25); + PyModule_AddIntConstant(m, "RobidouxFilter", 26); + PyModule_AddIntConstant(m, "RobidouxSharpFilter", 27); + PyModule_AddIntConstant(m, "CosineFilter", 28); + PyModule_AddIntConstant(m, "SplineFilter", 29); + PyModule_AddIntConstant(m, "SentinelFilter", 30); PyModule_AddIntConstant(m, "UndefinedAlphaChannel", 0); PyModule_AddIntConstant(m, "ActivateAlphaChannel", 1); PyModule_AddIntConstant(m, "BackgroundAlphaChannel", 2); @@ -44,6 +43,8 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "SetAlphaChannel", 8); PyModule_AddIntConstant(m, "ShapeAlphaChannel", 9); PyModule_AddIntConstant(m, "TransparentAlphaChannel", 10); + PyModule_AddIntConstant(m, "FlattenAlphaChannel", 11); + PyModule_AddIntConstant(m, "RemoveAlphaChannel", 12); PyModule_AddIntConstant(m, "UndefinedType", 0); PyModule_AddIntConstant(m, "BilevelType", 1); PyModule_AddIntConstant(m, "GrayscaleType", 2); @@ -152,16 +153,20 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "ArcDistortion", 9); PyModule_AddIntConstant(m, "PolarDistortion", 10); PyModule_AddIntConstant(m, "DePolarDistortion", 11); - PyModule_AddIntConstant(m, "BarrelDistortion", 12); - PyModule_AddIntConstant(m, "BarrelInverseDistortion", 13); - PyModule_AddIntConstant(m, "ShepardsDistortion", 14); - PyModule_AddIntConstant(m, "SentinelDistortion", 15); + PyModule_AddIntConstant(m, "Cylinder2PlaneDistortion", 12); + PyModule_AddIntConstant(m, "Plane2CylinderDistortion", 13); + PyModule_AddIntConstant(m, "BarrelDistortion", 14); + PyModule_AddIntConstant(m, "BarrelInverseDistortion", 15); + PyModule_AddIntConstant(m, "ShepardsDistortion", 16); + PyModule_AddIntConstant(m, "ResizeDistortion", 17); + PyModule_AddIntConstant(m, "SentinelDistortion", 18); PyModule_AddIntConstant(m, "UndefinedColorInterpolate", 0); PyModule_AddIntConstant(m, "BarycentricColorInterpolate", 1); PyModule_AddIntConstant(m, "BilinearColorInterpolate", 7); PyModule_AddIntConstant(m, "PolynomialColorInterpolate", 8); - PyModule_AddIntConstant(m, "ShepardsColorInterpolate", 14); - PyModule_AddIntConstant(m, "VoronoiColorInterpolate", 15); + PyModule_AddIntConstant(m, "ShepardsColorInterpolate", 16); + PyModule_AddIntConstant(m, "VoronoiColorInterpolate", 18); + PyModule_AddIntConstant(m, "InverseColorInterpolate", 19); PyModule_AddIntConstant(m, "UndefinedCompositeOp", 0); PyModule_AddIntConstant(m, "NoCompositeOp", 1); PyModule_AddIntConstant(m, "ModulusAddCompositeOp", 2); @@ -198,7 +203,7 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "LightenCompositeOp", 33); PyModule_AddIntConstant(m, "LinearLightCompositeOp", 34); PyModule_AddIntConstant(m, "LuminizeCompositeOp", 35); - PyModule_AddIntConstant(m, "MinusCompositeOp", 36); + PyModule_AddIntConstant(m, "MinusDstCompositeOp", 36); PyModule_AddIntConstant(m, "ModulateCompositeOp", 37); PyModule_AddIntConstant(m, "MultiplyCompositeOp", 38); PyModule_AddIntConstant(m, "OutCompositeOp", 39); @@ -217,7 +222,7 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "ModulusSubtractCompositeOp", 52); PyModule_AddIntConstant(m, "ThresholdCompositeOp", 53); PyModule_AddIntConstant(m, "XorCompositeOp", 54); - PyModule_AddIntConstant(m, "DivideCompositeOp", 55); + PyModule_AddIntConstant(m, "DivideDstCompositeOp", 55); PyModule_AddIntConstant(m, "DistortCompositeOp", 56); PyModule_AddIntConstant(m, "BlurCompositeOp", 57); PyModule_AddIntConstant(m, "PegtopLightCompositeOp", 58); @@ -226,6 +231,10 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "LinearDodgeCompositeOp", 61); PyModule_AddIntConstant(m, "LinearBurnCompositeOp", 62); PyModule_AddIntConstant(m, "MathematicsCompositeOp", 63); + PyModule_AddIntConstant(m, "DivideSrcCompositeOp", 64); + PyModule_AddIntConstant(m, "MinusSrcCompositeOp", 65); + PyModule_AddIntConstant(m, "DarkenIntensityCompositeOp", 66); + PyModule_AddIntConstant(m, "LightenIntensityCompositeOp", 67); PyModule_AddIntConstant(m, "NoValue", 0); PyModule_AddIntConstant(m, "XValue", 1); PyModule_AddIntConstant(m, "XiValue", 1); @@ -250,6 +259,7 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "CorrelateNormalizeValue", 65536); PyModule_AddIntConstant(m, "AreaValue", 131072); PyModule_AddIntConstant(m, "DecimalValue", 262144); + PyModule_AddIntConstant(m, "SeparatorValue", 524288); PyModule_AddIntConstant(m, "AllValues", 2147483647); PyModule_AddIntConstant(m, "UndefinedGravity", 0); PyModule_AddIntConstant(m, "ForgetGravity", 0); @@ -286,4 +296,18 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "Rec709YCbCrColorspace", 20); PyModule_AddIntConstant(m, "LogColorspace", 21); PyModule_AddIntConstant(m, "CMYColorspace", 22); + PyModule_AddIntConstant(m, "LuvColorspace", 23); + PyModule_AddIntConstant(m, "HCLColorspace", 24); + PyModule_AddIntConstant(m, "LCHColorspace", 25); + PyModule_AddIntConstant(m, "LMSColorspace", 26); + PyModule_AddIntConstant(m, "UndefinedMetric", 0); + PyModule_AddIntConstant(m, "AbsoluteErrorMetric", 1); + PyModule_AddIntConstant(m, "MeanAbsoluteErrorMetric", 2); + PyModule_AddIntConstant(m, "MeanErrorPerPixelMetric", 3); + PyModule_AddIntConstant(m, "MeanSquaredErrorMetric", 4); + PyModule_AddIntConstant(m, "PeakAbsoluteErrorMetric", 5); + PyModule_AddIntConstant(m, "PeakSignalToNoiseRatioMetric", 6); + PyModule_AddIntConstant(m, "RootMeanSquaredErrorMetric", 7); + PyModule_AddIntConstant(m, "NormalizedCrossCorrelationErrorMetric", 8); + PyModule_AddIntConstant(m, "FuzzErrorMetric", 9); }