diff --git a/src/calibre/gui2/tweak_book/editor/canvas.py b/src/calibre/gui2/tweak_book/editor/canvas.py index df29bcbcc4..a010be4c98 100644 --- a/src/calibre/gui2/tweak_book/editor/canvas.py +++ b/src/calibre/gui2/tweak_book/editor/canvas.py @@ -177,6 +177,36 @@ class Scale(Command): img = canvas.current_image return img.scaled(self.width, self.height, transformMode=Qt.SmoothTransformation) +class Sharpen(Command): + + TEXT = _('Sharpen image') + FUNC = 'sharpen' + + def __init__(self, sigma, canvas): + self.sigma = sigma + Command.__init__(self, canvas) + + def __call__(self, canvas): + img = canvas.current_image + i = qimage_to_magick(img) + getattr(i, self.FUNC)(0.0, self.sigma) + return magick_to_qimage(i) + +class Blur(Sharpen): + + TEXT = _('Blur image') + FUNC = 'blur' + +class Despeckle(Command): + + TEXT = _('De-speckle image') + + def __call__(self, canvas): + img = canvas.current_image + i = qimage_to_magick(img) + i.despeckle() + return magick_to_qimage(i) + class Replace(Command): ''' Replace the current image with another image. If there is a selection, @@ -204,7 +234,11 @@ def imageop(func): return error_dialog(self, _('No image'), _('No image loaded'), show=True) if not self.is_valid: return error_dialog(self, _('Invalid image'), _('The current image is not valid'), show=True) - return func(self, *args, **kwargs) + QApplication.setOverrideCursor(Qt.BusyCursor) + try: + return func(self, *args, **kwargs) + finally: + QApplication.restoreOverrideCursor() return ans class Canvas(QWidget): @@ -329,6 +363,21 @@ class Canvas(QWidget): self.undo_stack.push(Scale(width, height, self)) return True + @imageop + def sharpen_image(self, sigma=3.0): + self.undo_stack.push(Sharpen(sigma, self)) + return True + + @imageop + def blur_image(self, sigma=3.0): + self.undo_stack.push(Blur(sigma, self)) + return True + + @imageop + def despeckle_image(self): + self.undo_stack.push(Despeckle(self)) + return True + # The selection rectangle {{{ @property def dc_size(self): diff --git a/src/calibre/gui2/tweak_book/editor/image.py b/src/calibre/gui2/tweak_book/editor/image.py index 9f3eee1146..3bd1efe616 100644 --- a/src/calibre/gui2/tweak_book/editor/image.py +++ b/src/calibre/gui2/tweak_book/editor/image.py @@ -11,7 +11,7 @@ from functools import partial from PyQt4.Qt import ( QMainWindow, Qt, QApplication, pyqtSignal, QLabel, QIcon, QFormLayout, - QDialog, QSpinBox, QCheckBox, QDialogButtonBox, QToolButton, QMenu) + QDialog, QSpinBox, QCheckBox, QDialogButtonBox, QToolButton, QMenu, QInputDialog) from calibre.gui2 import error_dialog from calibre.gui2.tweak_book import actions @@ -244,6 +244,9 @@ class Editor(QMainWindow): self.filters_menu = m = QMenu() ac.setMenu(m) m.addAction(_('Auto-trim image'), self.canvas.autotrim_image) + m.addAction(_('Sharpen image'), self.sharpen_image) + m.addAction(_('Blur image'), self.blur_image) + m.addAction(_('De-speckle image'), self.canvas.despeckle_image) self.info_bar = b = self.addToolBar(_('Image information bar')) self.fmt_label = QLabel('') @@ -266,6 +269,18 @@ class Editor(QMainWindow): if d.exec_() == d.Accepted: self.canvas.resize_image(d.width, d.height) + def sharpen_image(self): + val, ok = QInputDialog.getInt(self, _('Sharpen image'), _( + 'The standard deviation for the Gaussian sharpen operation (higher means more sharpening)'), value=3, min=1, max=20) + if ok: + self.canvas.sharpen_image(sigma=val) + + def blur_image(self): + val, ok = QInputDialog.getInt(self, _('Blur image'), _( + 'The standard deviation for the Gaussian blur operation (higher means more blurring)'), value=3, min=1, max=20) + if ok: + self.canvas.blur_image(sigma=val) + def launch_editor(path_to_edit, path_is_raw=False): app = QApplication([]) if path_is_raw: diff --git a/src/calibre/utils/magick/magick.c b/src/calibre/utils/magick/magick.c index 7dd46d42ea..9aa04b4c96 100644 --- a/src/calibre/utils/magick/magick.c +++ b/src/calibre/utils/magick/magick.c @@ -1154,6 +1154,22 @@ magick_Image_sharpen(magick_Image *self, PyObject *args) { } // }}} +// Image.blur {{{ + +static PyObject * +magick_Image_blur(magick_Image *self, PyObject *args) { + double radius, sigma; + + NULL_CHECK(NULL) + + if (!PyArg_ParseTuple(args, "dd", &radius, &sigma)) return NULL; + + if (!MagickBlurImage(self->wand, radius, sigma)) return magick_set_exception(self->wand); + + Py_RETURN_NONE; +} +// }}} + // Image.quantize {{{ static PyObject * @@ -1431,8 +1447,12 @@ static PyMethodDef magick_Image_methods[] = { "sharpen(radius, sigma) \n\n sharpens an image. We convolve the image with a Gaussian operator of the given radius and standard deviation (sigma). For reasonable results, the radius should be larger than sigma. Use a radius of 0 and MagickSharpenImage() selects a suitable radius for you." }, + {"blur", (PyCFunction)magick_Image_blur, METH_VARARGS, + "blur(radius, sigma) \n\n blurs an image. We convolve the image with a Gaussian operator of the given radius and standard deviation (sigma). For reasonable results, the radius should be larger than sigma. Use a radius of 0 and MagickBlurImage() selects a suitable radius for you." + }, + {"despeckle", (PyCFunction)magick_Image_despeckle, METH_VARARGS, - "despeckle() \n\n reduces the speckle noise in an image while perserving the edges of the original image." + "despeckle() \n\n reduces the speckle noise in an image while preserving the edges of the original image." }, {"quantize", (PyCFunction)magick_Image_quantize, METH_VARARGS,