This commit is contained in:
Kovid Goyal 2016-05-08 13:03:57 +05:30
parent 019eaad9d7
commit b7b01b08be

View File

@ -8,6 +8,7 @@
#include <stdexcept> #include <stdexcept>
#include "imageops.h" #include "imageops.h"
#include <QColor> #include <QColor>
#include <QVector>
#include <cmath> #include <cmath>
// Macros {{{ // Macros {{{
@ -92,9 +93,11 @@ QImage remove_borders(const QImage &image, double fuzz) {
unsigned int width = img.width(), height = img.height(); unsigned int width = img.width(), height = img.height();
unsigned int top_border = 0, bottom_border = 0, left_border = 0, right_border = 0; unsigned int top_border = 0, bottom_border = 0, left_border = 0, right_border = 0;
bool bad_alloc = false; bool bad_alloc = false;
QVector<int> vbuf = QVector<int>();
ENSURE32(img) ENSURE32(img)
buf = new int[3*(MAX(width, height)+1)]; vbuf.resize(3*(MAX(width, height)+1));
buf = vbuf.data();
fuzz /= 255; fuzz /= 255;
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
@ -122,7 +125,6 @@ QImage remove_borders(const QImage &image, double fuzz) {
} }
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
delete[] buf;
if (bad_alloc) throw std::bad_alloc(); if (bad_alloc) throw std::bad_alloc();
return img; return img;
} }
@ -158,6 +160,8 @@ static QImage convolve(const QImage &image, int matrix_size, float *matrix) {
const QRgb *src = NULL; const QRgb *src = NULL;
float *m, *normalize_matrix, normalize, r, g, b; float *m, *normalize_matrix, normalize, r, g, b;
QImage img(image); QImage img(image);
QVector<QRgb*> buf1 = QVector<QRgb*>(matrix_size);
QVector<float> buf2 = QVector<float>(matrix_size * matrix_size);
if(!(matrix_size % 2)) if(!(matrix_size % 2))
throw std::out_of_range("Convolution kernel width must be an odd number"); throw std::out_of_range("Convolution kernel width must be an odd number");
@ -170,8 +174,10 @@ static QImage convolve(const QImage &image, int matrix_size, float *matrix) {
QImage buffer = QImage(w, h, img.format()); QImage buffer = QImage(w, h, img.format());
if (buffer.isNull()) throw std::bad_alloc(); if (buffer.isNull()) throw std::bad_alloc();
scanblock = new QRgb* [matrix_size]; buf1.resize(matrix_size);
normalize_matrix = new float[matrix_size*matrix_size]; scanblock = buf1.data();
buf2.resize(matrix_size * matrix_size);
normalize_matrix = buf2.data();
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
// create normalized matrix // create normalized matrix
@ -263,8 +269,6 @@ static QImage convolve(const QImage &image, int matrix_size, float *matrix) {
} }
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
delete[] scanblock;
delete[] normalize_matrix;
return buffer; return buffer;
} }
@ -301,7 +305,8 @@ static int default_convolve_matrix_size(const float radius, const float sigma, c
QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma, const bool high_quality) { // {{{ QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma, const bool high_quality) { // {{{
int matrix_size = default_convolve_matrix_size(radius, sigma, high_quality); int matrix_size = default_convolve_matrix_size(radius, sigma, high_quality);
int len = matrix_size*matrix_size; int len = matrix_size*matrix_size;
float alpha, *matrix = new float[len]; QVector<float> buf = QVector<float>(len);
float alpha, *matrix = buf.data();
float sigma2 = sigma*sigma*2.0; float sigma2 = sigma*sigma*2.0;
float sigmaPI2 = 2.0*M_PI*sigma*sigma; float sigmaPI2 = 2.0*M_PI*sigma*sigma;
@ -320,25 +325,24 @@ QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma
matrix[i/2]=(-2.0)*normalize; matrix[i/2]=(-2.0)*normalize;
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
QImage result(convolve(img, matrix_size, matrix)); QImage result(convolve(img, matrix_size, matrix));
delete[] matrix;
return(result); return(result);
} // }}} } // }}}
// gaussian_blur() {{{ // gaussian_blur() {{{
static float* get_blur_kernel(int &kernel_width, const float sigma) static void get_blur_kernel(int &kernel_width, const float sigma, QVector<float> &kernel)
{ {
#define KernelRank 3 #define KernelRank 3
float alpha, normalize, *kernel; float alpha, normalize;
int bias; int bias;
long i; long i;
if(sigma == 0.0) throw std::out_of_range("Zero sigma value is invalid for gaussian_blur"); if(sigma == 0.0) throw std::out_of_range("Zero sigma value is invalid for gaussian_blur");
if(kernel_width == 0) kernel_width = 3; if(kernel_width == 0) kernel_width = 3;
kernel.resize(kernel_width + 1);
kernel = new float[kernel_width+1];
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
memset(kernel, 0, (kernel_width+1)*sizeof(float)); kernel.fill(0);
bias = KernelRank*kernel_width/2; bias = KernelRank*kernel_width/2;
for(i=(-bias); i <= bias; ++i){ for(i=(-bias); i <= bias; ++i){
alpha = std::exp(-((float) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma)); alpha = std::exp(-((float) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
@ -351,10 +355,9 @@ static float* get_blur_kernel(int &kernel_width, const float sigma)
for(i=0; i < kernel_width; ++i) for(i=0; i < kernel_width; ++i)
kernel[i] /= normalize; kernel[i] /= normalize;
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
return(kernel);
} }
static void blur_scan_line(const float *kernel, const int kern_width, const QRgb *source, QRgb *destination, const int columns, const int offset) { static void blur_scan_line(const float* kernel, const int kern_width, const QRgb *source, QRgb *destination, const int columns, const int offset) {
FloatPixel aggregate, zero; FloatPixel aggregate, zero;
float scale; float scale;
const float *k; const float *k;
@ -451,30 +454,25 @@ QImage gaussian_blur(const QImage &image, const float radius, const float sigma)
int kern_width, x, y, w, h; int kern_width, x, y, w, h;
QRgb *src; QRgb *src;
QImage img(image); QImage img(image);
float *k = NULL; QVector<float> kernel;
if(sigma == 0.0) throw std::out_of_range("Zero sigma is invalid for convolution"); if(sigma == 0.0) throw std::out_of_range("Zero sigma is invalid for convolution");
// figure out optimal kernel width // figure out optimal kernel width
if(radius > 0){ if(radius > 0){
kern_width = (int)(2*std::ceil(radius)+1); kern_width = (int)(2*std::ceil(radius)+1);
k = get_blur_kernel(kern_width, sigma); get_blur_kernel(kern_width, sigma, kernel);
} }
else{ else{
float *last_kernel = NULL; float *last_kernel = NULL;
kern_width = 3; kern_width = 3;
k = get_blur_kernel(kern_width, sigma); get_blur_kernel(kern_width, sigma, kernel);
while((long)(255*k[0]) > 0){ while((long)(255*kernel[0]) > 0){
if(last_kernel != NULL)
delete[] last_kernel;
last_kernel = k;
kern_width += 2; kern_width += 2;
k = get_blur_kernel(kern_width, sigma); get_blur_kernel(kern_width, sigma, kernel);
} }
if(last_kernel != NULL){ if(last_kernel != NULL){
delete[] k;
kern_width -= 2; kern_width -= 2;
k = last_kernel;
} }
} }
@ -489,16 +487,15 @@ QImage gaussian_blur(const QImage &image, const float radius, const float sigma)
//blur image rows //blur image rows
for(y=0; y < h; ++y) for(y=0; y < h; ++y)
blur_scan_line(k, kern_width, reinterpret_cast<const QRgb *>(img.constScanLine(y)), blur_scan_line(kernel.data(), kern_width, reinterpret_cast<const QRgb *>(img.constScanLine(y)),
reinterpret_cast<QRgb *>(buffer.scanLine(y)), img.width(), 1); reinterpret_cast<QRgb *>(buffer.scanLine(y)), img.width(), 1);
// blur image columns // blur image columns
src = reinterpret_cast<QRgb *>(buffer.scanLine(0)); src = reinterpret_cast<QRgb *>(buffer.scanLine(0));
for(x=0; x < w; ++x) for(x=0; x < w; ++x)
blur_scan_line(k, kern_width, src+x, src+x, img.height(), blur_scan_line(kernel.data(), kern_width, src+x, src+x, img.height(),
img.width()); img.width());
// finish up // finish up
delete[] k;
return(buffer); return(buffer);
} }
// }}} // }}}
@ -549,7 +546,7 @@ static inline void hull(const int x_offset, const int y_offset, const int w, con
} }
#define DESPECKLE_CHANNEL(c, e) \ #define DESPECKLE_CHANNEL(c, e) \
(void)memset(pixels, 0, length); \ pixels.fill(0); \
j = w+2; \ j = w+2; \
for(y=0; y < h; ++y, ++j){ \ for(y=0; y < h; ++y, ++j){ \
src = reinterpret_cast<const QRgb *>(img.constScanLine(y)); \ src = reinterpret_cast<const QRgb *>(img.constScanLine(y)); \
@ -557,12 +554,12 @@ static inline void hull(const int x_offset, const int y_offset, const int w, con
for(x=w-1; x >= 0; --x, ++src, ++j) \ for(x=w-1; x >= 0; --x, ++src, ++j) \
pixels[j] = c(*src); \ pixels[j] = c(*src); \
} \ } \
(void)memset(buffer, 0, length); \ buffer.fill(0); \
for(i=0; i < 4; ++i){ \ for(i=0; i < 4; ++i){ \
hull(X[i], Y[i], w, h, pixels, buffer, 1); \ hull(X[i], Y[i], w, h, pixels.data(), buffer.data(), 1); \
hull(-X[i], -Y[i], w, h, pixels, buffer, 1); \ hull(-X[i], -Y[i], w, h, pixels.data(), buffer.data(), 1); \
hull(-X[i], -Y[i], w, h, pixels, buffer, -1); \ hull(-X[i], -Y[i], w, h, pixels.data(), buffer.data(), -1); \
hull(X[i], Y[i], w, h, pixels, buffer, -1); \ hull(X[i], Y[i], w, h, pixels.data(), buffer.data(), -1); \
} \ } \
j = w+2; \ j = w+2; \
for(y=0; y < h; ++y, ++j){ \ for(y=0; y < h; ++y, ++j){ \
@ -577,7 +574,6 @@ QImage despeckle(const QImage &image) {
QRgb *dest; QRgb *dest;
const QRgb *src; const QRgb *src;
QImage img(image); QImage img(image);
unsigned char *buffer, *pixels;
int w = img.width(); int w = img.width();
int h = img.height(); int h = img.height();
@ -587,8 +583,7 @@ QImage despeckle(const QImage &image) {
ENSURE32(img); ENSURE32(img);
length = (img.width()+2)*(img.height()+2); length = (img.width()+2)*(img.height()+2);
pixels = new unsigned char[length]; QVector<unsigned char> pixels(length), buffer(length);
buffer = new unsigned char[length];
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
DESPECKLE_CHANNEL(qRed, qRgba(pixels[j], qGreen(*dest), qBlue(*dest), qAlpha(*dest))) DESPECKLE_CHANNEL(qRed, qRgba(pixels[j], qGreen(*dest), qBlue(*dest), qAlpha(*dest)))
@ -596,8 +591,6 @@ QImage despeckle(const QImage &image) {
DESPECKLE_CHANNEL(qBlue, qRgba(qRed(*dest), qGreen(*dest), pixels[j], qAlpha(*dest))) DESPECKLE_CHANNEL(qBlue, qRgba(qRed(*dest), qGreen(*dest), pixels[j], qAlpha(*dest)))
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
delete[] pixels;
delete[] buffer;
return(img); return(img);
} }
// }}} // }}}
@ -662,8 +655,8 @@ void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned in
QImage normalize(const QImage &image) { // {{{ QImage normalize(const QImage &image) { // {{{
IntegerPixel intensity; IntegerPixel intensity;
HistogramListItem *histogram; HistogramListItem histogram[256] = {{0, 0, 0, 0}};
CharPixel *normalize_map; CharPixel normalize_map[256] = {{0, 0, 0, 0}};
ShortPixel high, low; ShortPixel high, low;
uint threshold_intensity; uint threshold_intensity;
int i, count; int i, count;
@ -674,12 +667,9 @@ QImage normalize(const QImage &image) { // {{{
ENSURE32(img); ENSURE32(img);
count = img.width()*img.height(); count = img.width()*img.height();
histogram = new HistogramListItem[256];
normalize_map = new CharPixel[256];
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
// form histogram // form histogram
memset(histogram, 0, 256*sizeof(HistogramListItem));
dest = (QRgb *)img.bits(); dest = (QRgb *)img.bits();
for(i=0; i < count; ++i){ for(i=0; i < count; ++i){
@ -730,8 +720,6 @@ QImage normalize(const QImage &image) { // {{{
break; break;
} }
delete[] histogram;
// stretch the histogram to create the normalized image mapping. // stretch the histogram to create the normalized image mapping.
for(i=0; i < 256; i++){ for(i=0; i < 256; i++){
if(i < low.red) if(i < low.red)
@ -778,7 +766,6 @@ QImage normalize(const QImage &image) { // {{{
*dest++ = qRgba(r, g, b, qAlpha(pixel)); *dest++ = qRgba(r, g, b, qAlpha(pixel));
} }
delete[] normalize_map;
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
return img; return img;
} // }}} } // }}}
@ -788,8 +775,10 @@ QImage oil_paint(const QImage &image, const float radius, const bool high_qualit
int i, x, y, w, h, matrix_x, matrix_y; int i, x, y, w, h, matrix_x, matrix_y;
int edge = matrix_size/2; int edge = matrix_size/2;
unsigned int max, value; unsigned int max, value;
unsigned int histogram[256] = {0};
QRgb *dest, *s, **scanblock; QRgb *dest, *s, **scanblock;
QImage img(image); QImage img(image);
QVector<QRgb*> buf = QVector<QRgb*>(matrix_size);
w = img.width(); w = img.width();
h = img.height(); h = img.height();
@ -798,8 +787,8 @@ QImage oil_paint(const QImage &image, const float radius, const bool high_qualit
ENSURE32(img); ENSURE32(img);
QImage buffer(w, h, img.format()); QImage buffer(w, h, img.format());
scanblock = new QRgb* [matrix_size]; buf.resize(matrix_size);
unsigned int *histogram = new unsigned int[256]; scanblock = buf.data();
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
for(y=0; y < h; ++y){ for(y=0; y < h; ++y){
@ -891,8 +880,6 @@ QImage oil_paint(const QImage &image, const float radius, const bool high_qualit
} }
} }
delete[] histogram;
delete[] scanblock;
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
return(buffer); return(buffer);
} // }}} } // }}}