From 072239c3eb49b6b2b2980a388bcaa261fd4b9da0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 6 May 2016 11:04:35 +0530 Subject: [PATCH] Implement despeckle() --- src/calibre/utils/imageops/imageops.cpp | 99 +++++++++++++++++++++++++ src/calibre/utils/imageops/imageops.h | 1 + src/calibre/utils/imageops/imageops.sip | 7 ++ src/calibre/utils/img.py | 5 ++ 4 files changed, 112 insertions(+) diff --git a/src/calibre/utils/imageops/imageops.cpp b/src/calibre/utils/imageops/imageops.cpp index bf72c37a75..9b9a0e3968 100644 --- a/src/calibre/utils/imageops/imageops.cpp +++ b/src/calibre/utils/imageops/imageops.cpp @@ -477,3 +477,102 @@ QImage gaussian_blur(const QImage &image, const float radius, const float sigma) return(buffer); } // }}} + +// despeckle() {{{ + +inline void hull(const int x_offset, const int y_offset, const int w, const int h, unsigned char *f, unsigned char *g, const int polarity) { + int x, y, v; + unsigned char *p, *q, *r, *s; + p = f+(w+2); q = g+(w+2); + r = p+(y_offset*(w+2)+x_offset); + for(y=0; y < h; ++y, ++p, ++q, ++r){ + ++p; ++q; ++r; + if(polarity > 0){ + for(x=w; x > 0; --x, ++p, ++q, ++r){ + v = (*p); + if((int)*r >= (v+2)) v += 1; + *q = (unsigned char)v; + } + } + else{ + for(x=w; x > 0; --x, ++p, ++q, ++r){ + v = (*p); + if((int)*r <= (v-2)) v -= 1; + *q = (unsigned char)v; + } + } + } + p = f+(w+2); q = g+(w+2); + r = q+(y_offset*(w+2)+x_offset); s = q-(y_offset*(w+2)+x_offset); + for(y=0; y < h; ++y, ++p, ++q, ++r, ++s){ + ++p; ++q; ++r; ++s; + if(polarity > 0){ + for(x=w; x > 0; --x, ++p, ++q, ++r, ++s){ + v = (*q); + if(((int)*s >= (v+2)) && ((int)*r > v)) v+=1; + *p = (unsigned char)v; + } + } + else{ + for(x=w; x > 0; --x, ++p, ++q, ++r, ++s){ + v = (int)(*q); + if (((int)*s <= (v-2)) && ((int)*r < v)) v -= 1; + *p = (unsigned char)v; + } + } + } +} + +#define DESPECKLE_CHANNEL(c, e) \ + (void)memset(pixels, 0, length); \ + j = w+2; \ + for(y=0; y < h; ++y, ++j){ \ + src = reinterpret_cast(img.constScanLine(y)); \ + ++j; \ + for(x=w-1; x >= 0; --x, ++src, ++j) \ + pixels[j] = c(*src); \ + } \ + (void)memset(buffer, 0, length); \ + for(i=0; i < 4; ++i){ \ + hull(X[i], Y[i], w, h, pixels, buffer, 1); \ + hull(-X[i], -Y[i], w, h, pixels, buffer, 1); \ + hull(-X[i], -Y[i], w, h, pixels, buffer, -1); \ + hull(X[i], Y[i], w, h, pixels, buffer, -1); \ + } \ + j = w+2; \ + for(y=0; y < h; ++y, ++j){ \ + dest = reinterpret_cast(img.scanLine(y)); \ + ++j; \ + for(x=w-1; x >= 0; --x, ++dest, ++j) \ + *dest = e; \ + } + +QImage despeckle(const QImage &image) { + int length, x, y, j, i; + QRgb *dest; + const QRgb *src; + QImage img(image); + unsigned char *buffer, *pixels; + int w = img.width(); + int h = img.height(); + + static const int + X[4]= {0, 1, 1,-1}, + Y[4]= {1, 0, 1, 1}; + + ENSURE32(img); + length = (img.width()+2)*(img.height()+2); + pixels = new unsigned char[length]; + buffer = new unsigned char[length]; + + Py_BEGIN_ALLOW_THREADS; + DESPECKLE_CHANNEL(qRed, qRgba(pixels[j], qGreen(*dest), qBlue(*dest), qAlpha(*dest))) + DESPECKLE_CHANNEL(qGreen, qRgba(qRed(*dest), pixels[j], qBlue(*dest), qAlpha(*dest))) + DESPECKLE_CHANNEL(qBlue, qRgba(qRed(*dest), qGreen(*dest), pixels[j], qAlpha(*dest))) + Py_END_ALLOW_THREADS; + + delete[] pixels; + delete[] buffer; + return(img); +} +// }}} diff --git a/src/calibre/utils/imageops/imageops.h b/src/calibre/utils/imageops/imageops.h index 660ffcc4f9..d9691ce3a6 100644 --- a/src/calibre/utils/imageops/imageops.h +++ b/src/calibre/utils/imageops/imageops.h @@ -14,4 +14,5 @@ QImage remove_borders(const QImage &image, double fuzz); QImage grayscale(const QImage &image); QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma, const bool high_quality=true); QImage gaussian_blur(const QImage &img, const float radius, const float sigma); +QImage despeckle(const QImage &image); diff --git a/src/calibre/utils/imageops/imageops.sip b/src/calibre/utils/imageops/imageops.sip index 00d871566e..70781bfc5c 100644 --- a/src/calibre/utils/imageops/imageops.sip +++ b/src/calibre/utils/imageops/imageops.sip @@ -43,3 +43,10 @@ QImage gaussian_blur(const QImage &img, const float radius, const float sigma); sipRes = new QImage(gaussian_blur(*a0, a1, a2)); IMAGEOPS_SUFFIX %End + +QImage despeckle(const QImage &image); +%MethodCode + IMAGEOPS_PREFIX + sipRes = new QImage(despeckle(*a0)); + IMAGEOPS_SUFFIX +%End diff --git a/src/calibre/utils/img.py b/src/calibre/utils/img.py index 667c69558d..d18429faa8 100644 --- a/src/calibre/utils/img.py +++ b/src/calibre/utils/img.py @@ -227,6 +227,11 @@ def gaussian_blur(img, radius=-1, sigma=3): raise RuntimeError(imageops_err) return imageops.gaussian_blur(image_from_data(img), max(0, radius), sigma) +def despeckle(img): + if imageops is None: + raise RuntimeError(imageops_err) + return imageops.despeckle(image_from_data(img)) + def run_optimizer(file_path, cmd, as_filter=False, input_data=None): file_path = os.path.abspath(file_path) cwd = os.path.dirname(file_path)