Skip to content
Browse files

New Version 1.27

Main changes:
- New deconvolution methods added (Tikhonov, Total Variation prior)
- Speed improved (about 2.5x)
- Memory consumption decreased (about 1.5x)
- Max image size increased
- Settings page added
- Updates checker added
- Drag and drop added
- Help screen with image example added
- Wiener deconvolution bug fixed
  • Loading branch information...
1 parent e730360 commit 568de6cf07dd61fe09571ef283d56a1819c3ecd6 @Y-Vladimir committed Sep 30, 2012
View
BIN dist/SmartDeblur-1.27-win.zip
Binary file not shown.
View
62 src/CheckUpdatesThread.cpp
@@ -0,0 +1,62 @@
+#include "CheckUpdatesThread.h"
+#include "MainWindow.h"
+
+CheckUpdatesThread::CheckUpdatesThread(QObject *parent) : QThread(parent) {
+ isUpdateAvailable = false;
+ this->parent = parent;manager = new QNetworkAccessManager();
+ connect(manager, SIGNAL(finished(QNetworkReply*)),
+ this, SLOT(replyFinished(QNetworkReply*)));
+ // Check two destinations to have more reliability
+ manager->get(QNetworkRequest(QUrl("http://cloud.github.com/downloads/Y-Vladimir/SmartDeblur/updates.xml")));
+ manager->get(QNetworkRequest(QUrl("http://yuzhikov.com/updates.xml")));
+
+}
+
+void CheckUpdatesThread::run() {
+ qDebug() << "CheckUpdatesThread started";
+}
+
+void CheckUpdatesThread::replyFinished(QNetworkReply * reply) {
+ try {
+ if (reply->error() != QNetworkReply::NoError) {
+ reply->deleteLater();
+ return;
+ }
+
+ QDomDocument updatesDoc("updatesXML");
+ updatesDoc.setContent(QString(reply->readAll()));
+ reply->deleteLater();
+
+ QDomElement appElem = updatesDoc.documentElement();
+ QString lastVersion = appElem.attributeNode("lastVersion").nodeValue();
+ QString description = appElem.elementsByTagName("description").at(0).toElement().text();
+ QString date = appElem.elementsByTagName("date").at(0).toElement().text();
+ QString url = appElem.elementsByTagName("url").at(0).toElement().text();
+ QString changes;
+ QDomNodeList nodeList = appElem.elementsByTagName("changes").at(0).toElement().elementsByTagName("item");
+ if (nodeList.count() > 0) {
+ for(int i=0; i<nodeList.count(); i++) {
+ changes += "<li>" + nodeList.at(i).toElement().text() + "</li>";
+ }
+ changes = "<ul>"+changes+"</ul>";
+ }
+
+ // If gathered vesrion not equal current, then show message
+ if (lastVersion.length() > 1 && lastVersion != MainWindow::appVersion) {
+ if (isUpdateAvailable) {
+ return;
+ }
+ isUpdateAvailable = true;
+ QString updateText = "Your installed version of SmartDeblur is %1<b><br>"
+ "An updated version (%2, from %3) is available!</b><br><br>"
+ "%4%5"
+ "Download a new version by the following link:<br>"
+ "<a href=\"%6\">%6</a>";
+ QMessageBox::information((QWidget*)parent, tr("New version of SmartDblur"),
+ updateText.arg(MainWindow::appVersion, lastVersion, date, description, changes, url));
+ }
+ } catch (...) {
+ qDebug() << "ERROR while checking updates!";
+ }
+
+}
View
36 src/CheckUpdatesThread.h
@@ -0,0 +1,36 @@
+#ifndef CHECKUPDATESTHREAD_H
+#define CHECKUPDATESTHREAD_H
+
+#include <QThread>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QDebug>
+#include <QString>
+#include <QMessageBox>
+#include <QFile>
+#include <QDomDocument>
+
+
+class CheckUpdatesThread : public QThread
+{
+ Q_OBJECT
+public:
+ explicit CheckUpdatesThread(QObject *parent = 0);
+
+private:
+ QNetworkAccessManager* manager;
+ QObject *parent;
+ volatile bool isUpdateAvailable;
+
+
+protected:
+ void run();
+
+signals:
+
+private slots:
+ void replyFinished(QNetworkReply* reply);
+
+};
+
+#endif // CHECKUPDATESTHREAD_H
View
721 src/DeconvolutionTool.cpp
@@ -1,212 +1,509 @@
-#include "DeconvolutionTool.h"
-
-
-
-DeconvolutionTool::DeconvolutionTool(QObject* parent):QObject(parent) {
- // Init MultiThreading
- threadsCount = QThread::idealThreadCount() > 0 ? QThread::idealThreadCount() : 2;
- qDebug("Init Multi-Threading with threads count: %d", threadsCount);
- fftw_plan_with_nthreads(threadsCount);
-
- inputMatrix = NULL;
- inputMatrix = NULL;
- outputMatrix = NULL;
- kernelMatrix = NULL;
- inputMatrixFFT = NULL;
- kernelMatrixFFT = NULL;
-
- forwardImagePlan = NULL;
- forwardKernelPlan = NULL;
- backwardImagePlan = NULL;
-}
-
-void DeconvolutionTool::initFFT(const QImage *inputImage) {
- QTime time;
- time.start();
-
- // Read image size
- width = inputImage->width();
- height = inputImage->height();
-
- // Destroy FFTW objects
- fftw_destroy_plan(forwardImagePlan);
- fftw_destroy_plan(forwardKernelPlan);
- fftw_destroy_plan(backwardImagePlan);
- fftw_free(inputMatrix);
- fftw_free(outputMatrix);
- fftw_free(kernelMatrix);
- fftw_free(inputMatrixFFT);
- fftw_free(kernelMatrixFFT);
-
- // Init FFTW structures with given size
- inputMatrix = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height);
- outputMatrix = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height);
- kernelMatrix = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height);
- inputMatrixFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height);
- kernelMatrixFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height);
-
- // Init FFTW plan to optimize speed - init once and use many times
- // Column-major Format used. When creating the plan, simply pass
- // the dimensions of the array to the planner in reverse order (width, height..) -> (height, width...)
- forwardImagePlan = fftw_plan_dft_2d(height, width, inputMatrix, inputMatrixFFT, FFTW_FORWARD, FFTW_ESTIMATE);
- forwardKernelPlan = fftw_plan_dft_2d(height, width, kernelMatrix, kernelMatrixFFT, FFTW_FORWARD, FFTW_ESTIMATE);
- backwardImagePlan = fftw_plan_dft_2d(height, width, inputMatrixFFT, outputMatrix, FFTW_BACKWARD, FFTW_ESTIMATE);
-
-
- qDebug("initFFT: %d ms", time.elapsed());
-}
-
-void DeconvolutionTool::doDeconvolution(const QImage *inputImage, QImage *outputImage, const Blur* blur) {
- // Create kernel
- buildKernel(kernelMatrix, width, height, blur);
- fftw_execute(forwardKernelPlan);
-
- if (blur->previewMode) {
- doDeconvolutionForChannel(inputImage, outputImage, kernelMatrixFFT, width, height, blur->radius, blur->PSNR, GRAY);
- } else {
- emit progressEvent(10);
- doDeconvolutionForChannel(inputImage, outputImage, kernelMatrixFFT, width, height, blur->radius, blur->PSNR, RED);
- emit progressEvent(40);
- doDeconvolutionForChannel(inputImage, outputImage, kernelMatrixFFT, width, height, blur->radius, blur->PSNR, GREEN);
- emit progressEvent(70);
- doDeconvolutionForChannel(inputImage, outputImage, kernelMatrixFFT, width, height, blur->radius, blur->PSNR, BLUE);
- emit progressEvent(99);
- }
-}
-
-int DeconvolutionTool::getThreadsCount() {
- return threadsCount;
-}
-
-void DeconvolutionTool::doDeconvolutionForChannel(const QImage *inputImage, QImage *outputImage, const fftw_complex *kernelMatrixFFT,
- const int width, const int height, const double kernelRadius, const double PSNR, const CurrentChannel channel) {
- // Read given channel
- ImageUtils::fillInputMatrix(inputMatrix, inputImage, width, height, channel);
- fftw_execute(forwardImagePlan);
-
- if (channel != GRAY) {
- // Borders processing to prevent ring effect
- multiplayFFTs(inputMatrixFFT, kernelMatrixFFT, inputMatrixFFT, width, height);
- fftw_execute(backwardImagePlan);
- for (int y = 0; y<height; y++) {
- for (int x = 0; x<width; x++) {
- int index = y*width+x;
- if (x < kernelRadius || y < kernelRadius || x > width - kernelRadius ||y > height - kernelRadius) {
- inputMatrix[index][0] = outputMatrix[y*width + x][0] / (width * height);
- }
-
- inputMatrix[index][0] = centerFFTKoef(x, y)*fabs(inputMatrix[index][0]);
- inputMatrix[index][1] = 0;
- }
- }
- fftw_execute(forwardImagePlan);
- }
-
- // Deconvolution in the Frequency domain
- deconvolutionByWiener(kernelMatrixFFT, inputMatrixFFT, inputMatrixFFT, width, height, PSNR);
-
- // Inverse FFT to return to the Time domain
- fftw_execute(backwardImagePlan);
- ImageUtils::fillOutputImage(inputImage, outputMatrix, outputImage, width, height, channel);
-}
-
-void DeconvolutionTool::multiplayFFTs(const fftw_complex *firstFFT, const fftw_complex *secondFFT, fftw_complex *outFFT, const int width, const int height) {
- for (int y = 0; y<height; y++) {
- for (int x = 0; x<width; x++) {
- int index = y*width + x;
- double value = centerFFTKoef(x, y) * secondFFT[index][0];
- outFFT[index][0] *= value;
- outFFT[index][1] *= value;
- }
- }
-}
-
-void DeconvolutionTool::deconvolutionByWiener(const fftw_complex *kernelFFT, const fftw_complex *inImageFFT, fftw_complex *outImageFFT, const int WIDTH, const int HEIGHT, const double K) {
- for(int y=0; y<HEIGHT; y++) {
- for(int x=0; x<WIDTH; x++) {
- int index = y*WIDTH+x;
- double energyValue = pow(kernelFFT[index][0], 2) + pow(kernelFFT[index][1], 2);
- double wienerValue = energyValue / (kernelFFT[index][0] * (energyValue + K));
- wienerValue *= centerFFTKoef(x, y);
- outImageFFT[index][0] = wienerValue * inImageFFT[index][0];
- outImageFFT[index][1] = wienerValue * inImageFFT[index][1];
- }
- }
-}
-
-void DeconvolutionTool::buildKernel(fftw_complex* outKernelFFT, const int WIDTH, const int HEIGHT, const Blur* blur) {
- QImage* kernelImage;
-
- if (blur->getName() == "FocusBlur") {
- kernelImage = ImageUtils::buildKernelImage((FocusBlur*)blur);
- } else if (blur->getName() == "MotionBlur") {
- kernelImage = ImageUtils::buildKernelImage((MotionBlur*)blur);
- }
- int size = kernelImage->width();
-
- //kernelImage->save("C:/Projects/KernelImage.png");
-
- // Fill kernel
- double sumKernelElements = 0;
- for (int y = 0; y<HEIGHT; y++) {
- for (int x = 0; x<WIDTH; x++) {
- int index = y*WIDTH + x;
- int value = 0;
- // if we are in the kernel area (of small kernelImage), then take pixel values. Otherwise keep 0
- if (abs(x-WIDTH/2)<(size-2)/2 && abs(y-HEIGHT/2)<(size-2)/2) {
- int xLocal = x-(WIDTH-size)/2;
- int yLocal = y-(HEIGHT-size)/2;
- value = qRed(kernelImage->pixel(xLocal,yLocal))* centerFFTKoef(x, y);;
- }
-
- outKernelFFT[index][0] = value;
- sumKernelElements += abs(value);
- outKernelFFT[index][1] = 0;
- }
- }
-
- delete(kernelImage);
-
- // Zero-protection
- if (sumKernelElements==0) {
- sumKernelElements = 1;
- }
-
- // Normalize
- double k = 1/sumKernelElements;
- for (int i=0; i<WIDTH*HEIGHT; i++) {
- outKernelFFT[i][0] *= k;
- }
-}
-
-void DeconvolutionTool::visualizeFFT(fftw_complex* fft, const int WIDTH, const int HEIGHT, QString path) {
- QImage visualImage(WIDTH, HEIGHT, QImage::Format_RGB32);
-
- // Find maximum
- float maxAmpl = 0;
- float curAmpl = 0;
- for (int i=0; i<WIDTH*HEIGHT; i++) {
- // Extract Amplitude
- curAmpl = sqrt(pow(fft[i][0], 2) + pow(fft[i][1], 2));
- curAmpl = log(1 + curAmpl);
- if (curAmpl > maxAmpl) {
- maxAmpl = curAmpl;
- }
- }
-
- // Build image
- for(int y=0; y<HEIGHT; y++) {
- for(int x=0; x<WIDTH; x++) {
- // Normalize
- curAmpl = sqrt(pow(fft[y*WIDTH+x][0], 2) + pow(fft[y*WIDTH+x][1], 2));
- // curAmpl = fft[i*width+j].r;
- // Log scale
- curAmpl = 255 * log(1 + curAmpl) / maxAmpl;
- visualImage.setPixel(x, y, qRgb(curAmpl, curAmpl, curAmpl));
- }
- }
-
- visualImage.save(path);
-}
-
-
+#include "DeconvolutionTool.h"
+
+DeconvolutionTool::DeconvolutionTool(QObject* parent):QObject(parent) {
+ // Init MultiThreading
+ threadsCount = QThread::idealThreadCount() > 0 ? QThread::idealThreadCount() : 2;
+ qDebug("Init Multi-Threading with threads count: %d", threadsCount);
+ fftw_plan_with_nthreads(threadsCount);
+ tvIterationsCount = 500;
+ previewMethod = 0;
+
+ inputImageMatrix = NULL;
+ outputImageMatrix = NULL;
+ kernelMatrix = NULL;
+ laplacianMatrix = NULL;
+ outLaplacianMatrix = NULL;
+
+ inputImageFFT = NULL;
+ outputImageFFT = NULL;
+ kernelFFT = NULL;
+ kernelTempFFT = NULL;
+ laplacianMatrixFFT = NULL;
+
+ realForwardPlan = NULL;
+ realForwardKernelPlan = NULL;
+ realBackwardPlan = NULL;
+ forwardLaplacianPlan = NULL;
+ backwardLaplacianPlan = NULL;
+}
+
+DeconvolutionTool::~DeconvolutionTool() {
+ removeFFTObjects();
+}
+
+void DeconvolutionTool::removeFFTObjects() {
+ fftw_free(inputImageMatrix);
+ fftw_free(outputImageMatrix);
+ fftw_free(kernelMatrix);
+ fftw_free(laplacianMatrix);
+ fftw_free(outLaplacianMatrix);
+
+ fftw_free(inputImageFFT);
+ fftw_free(outputImageFFT);
+ fftw_free(kernelFFT);
+ fftw_free(kernelTempFFT);
+ fftw_free(laplacianMatrixFFT);
+
+ fftw_destroy_plan(realForwardPlan);
+ fftw_destroy_plan(realForwardKernelPlan);
+ fftw_destroy_plan(realBackwardPlan);
+ fftw_destroy_plan(forwardLaplacianPlan);
+ fftw_destroy_plan(backwardLaplacianPlan);
+
+ isProcessingCancelled = false;
+}
+
+void DeconvolutionTool::initFFT(const QImage *inputImage) {
+ removeFFTObjects();
+ QTime time;
+ time.start();
+ QString progressText = "Loading image";
+ setProgressInterval(1,100, progressText);
+
+ // Read image size
+ width = inputImage->width();
+ height = inputImage->height();
+
+ // Init FFTW structures with given size
+ inputImageMatrix = (double*) fftw_malloc(sizeof(double) * width * height);
+ outputImageMatrix = (double*) fftw_malloc(sizeof(double) * width * height);
+ kernelMatrix = (double*) fftw_malloc(sizeof(double) * width * height);
+ // kernelTempMatrix = (double*)fftw_malloc(sizeof(double)*width*height);
+ laplacianMatrix = (double*) fftw_malloc(sizeof(double) * width * height);
+ outLaplacianMatrix = (double*) fftw_malloc(sizeof(double) * width * height);
+
+ inputImageFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (width/2+1) * height);
+ outputImageFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (width/2+1) * height);
+ kernelFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (width/2+1) * height);
+ kernelTempFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (width/2+1) * height);
+ laplacianMatrixFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (width/2+1) * height);
+ setProgressSubValue(10);
+
+ // Init FFTW plan to optimize speed - init once and use many times
+ // Column-major Format used. When creating the plan, simply pass
+ // the dimensions of the array to the planner in reverse order (width, height..) -> (height, width...)
+ realForwardPlan = fftw_plan_dft_r2c_2d(height, width, inputImageMatrix, inputImageFFT, FFTW_MEASURE);
+ setProgressSubValue(30);
+ realForwardKernelPlan = fftw_plan_dft_r2c_2d(height, width, kernelMatrix, kernelFFT, FFTW_MEASURE);
+ setProgressSubValue(50);
+ realBackwardPlan = fftw_plan_dft_c2r_2d(height, width, inputImageFFT, outputImageMatrix, FFTW_MEASURE);
+ setProgressSubValue(70);
+ QApplication::processEvents();
+ forwardLaplacianPlan = fftw_plan_dft_r2c_2d(height, width, laplacianMatrix, laplacianMatrixFFT, FFTW_MEASURE);
+ setProgressSubValue(90);
+ backwardLaplacianPlan = fftw_plan_dft_c2r_2d(height, width, laplacianMatrixFFT, outLaplacianMatrix, FFTW_MEASURE);
+ setProgressSubValue(100);
+
+ qDebug("initFFT: %d ms", time.elapsed());
+}
+
+bool DeconvolutionTool::doDeconvolution(QImage *inputImage, QImage *outputImage, Blur* blur) {
+ isProcessingCancelled = false;
+ // Create kernel
+ buildKernel(kernelMatrix, width, height, blur);
+ fftw_execute(realForwardKernelPlan);
+ // Fill processingContext
+ ProcessingContext* processingContext = new ProcessingContext();
+ processingContext->inputImage = inputImage;
+ processingContext->outputImage = outputImage;
+ processingContext->inputImageMatrix = inputImageMatrix;
+ processingContext->outputImageMatrix = outputImageMatrix;
+ processingContext->kernelFFT = kernelFFT;
+ processingContext->width = width;
+ processingContext->height = height;
+ processingContext->blur = blur;
+
+ if (blur->mode == PREVIEW_GRAY) {
+ doDeconvolutionForChannel(processingContext, GRAY);
+ } else {
+ QString progressText= "High-Quality";
+ if (blur->mode == PREVIEW_COLOR) {
+ progressText = "Color Preview";
+ }
+ setProgressInterval(10,40, progressText);
+ doDeconvolutionForChannel(processingContext, RED);
+ setProgressInterval(40,70, progressText);
+ doDeconvolutionForChannel(processingContext, GREEN);
+ setProgressInterval(70,100, progressText);
+ doDeconvolutionForChannel(processingContext, BLUE);
+
+ }
+
+ delete(processingContext);
+ return !isProcessingCancelled;
+}
+
+int DeconvolutionTool::getThreadsCount() {
+ return threadsCount;
+}
+
+void DeconvolutionTool::doDeconvolutionForChannel(ProcessingContext* processingContext, const CurrentChannel channel) {
+ if (isProcessingCancelled && processingContext->blur->mode != PREVIEW_GRAY) {
+ return;
+ }
+
+ double blurRadius = processingContext->blur->radius;
+ int width = processingContext->width;
+ int height = processingContext->height;
+ // Read given channel
+ ImageUtils::fillMatrixFromImage(processingContext, channel);
+ fftw_execute(realForwardPlan);
+ processingContext->inputImageFFT = inputImageFFT;
+
+ // Borders processing to prevent ring effect
+ multiplayRealFFTs(inputImageFFT, processingContext->kernelFFT, width/2+1, height);
+ fftw_execute(realBackwardPlan);
+ for (int y = 0; y<height; y++) {
+ for (int x = 0; x<width; x++) {
+ int index = y*width+x;
+ if (x < blurRadius || y < blurRadius || x > width - blurRadius ||y > height - blurRadius) {
+ processingContext->inputImageMatrix[index] = outputImageMatrix[y*width + x] / (width * height);
+ }
+
+ }
+ }
+
+ if (processingContext->blur->mode != HIGH_QUALITY) {
+ // Deconvolution in the Frequency domain
+ fftw_execute(realForwardPlan);
+ if (previewMethod == 0) {
+ deconvolutionByWiener(processingContext);
+ } else {
+ deconvolutionByTikhonov(processingContext);
+ }
+
+ // Inverse FFT to return to the Time domain
+ fftw_execute(realBackwardPlan);
+ } else {
+ deconvolutionByTotalVariationPrior(processingContext);
+ }
+
+ // Don't cancel in case of preview
+ if (!isProcessingCancelled || processingContext->blur->mode == (PREVIEW_GRAY)) {
+ ImageUtils::fillImageFromMatrix(processingContext, channel);
+ }
+}
+
+void DeconvolutionTool::multiplayRealFFTs(fftw_complex *outFFT, const fftw_complex *kernelFFT, const int width, const int height) {
+ for (int y = 0; y<height; y++) {
+ for (int x = 0; x<width; x++) {
+ int index = y*width + x;
+ double value = kernelFFT[index][0];
+ outFFT[index][0] *= value;
+ outFFT[index][1] *= value;
+ }
+ }
+}
+
+void DeconvolutionTool::cancelProcessing() {
+ isProcessingCancelled = true;
+}
+
+void DeconvolutionTool::setTVIterationsCount(int value) {
+ tvIterationsCount = value;
+}
+
+void DeconvolutionTool::setPreviewMethod(int value) {
+ previewMethod = value;
+}
+
+void DeconvolutionTool::deconvolutionByWiener(ProcessingContext* processingContext) {
+ double K = pow(1.07, processingContext->blur->smooth)/10000.0;
+ int N = (processingContext->width/2+1) * processingContext->height;
+ for(int i=0; i<N; i++) {
+ double energyValue = pow(processingContext->kernelFFT[i][0], 2) + pow(processingContext->kernelFFT[i][1], 2);
+ double wienerValue = processingContext->kernelFFT[i][0] / (energyValue + K);
+ processingContext->inputImageFFT[i][0] = wienerValue * processingContext->inputImageFFT[i][0];
+ processingContext->inputImageFFT[i][1] = wienerValue * processingContext->inputImageFFT[i][1];
+ }
+}
+
+void DeconvolutionTool::deconvolutionByTikhonov(ProcessingContext* processingContext) {
+ int width = processingContext->width;
+ int height = processingContext->height;
+
+ // Create laplacian
+ for(int y=0; y<height; y++) {
+ for(int x=0; x<width; x++) {
+ int index = y*width+x;
+ laplacianMatrix[index] = 0;
+ }
+ }
+ laplacianMatrix[0] = 4;
+ laplacianMatrix[1] = -1;
+ laplacianMatrix[1*width] = -1;
+ laplacianMatrix[width-1] = -1;
+ laplacianMatrix[(height-1)*width] = -1;
+
+ fftw_execute(forwardLaplacianPlan);
+ double K = pow(1.07, processingContext->blur->smooth)/1000.0;
+ int N = (width/2+1) * height;
+ for(int index=0; index<N; index++) {
+ double energyValue = pow(processingContext->kernelFFT[index][0], 2) + pow(processingContext->kernelFFT[index][1], 2);
+ double energyLaplacianValue = pow(laplacianMatrixFFT[index][0], 2) + pow(laplacianMatrixFFT[index][1], 2);
+ double tikhonovValue = processingContext->kernelFFT[index][0] / (energyValue + K*energyLaplacianValue);
+ processingContext->inputImageFFT[index][0] = tikhonovValue * processingContext->inputImageFFT[index][0];
+ processingContext->inputImageFFT[index][1] = tikhonovValue * processingContext->inputImageFFT[index][1];
+
+ }
+}
+
+void DeconvolutionTool::deconvolutionByTotalVariationPrior(ProcessingContext* processingContext) {
+ if (isProcessingCancelled) {
+ return;
+ }
+
+ int WIDTH = processingContext->width;
+ int HEIGHT = processingContext->height;
+ fftw_complex *GradientMatrix = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * WIDTH * HEIGHT);
+ double *fTV= (double*) fftw_malloc(sizeof(double) * WIDTH * HEIGHT);
+
+ double k = 1.0/(WIDTH * HEIGHT);
+ int k2 = WIDTH * HEIGHT;
+ int k3 = (WIDTH/2+1) * HEIGHT;
+
+ // copy blur fft
+ for(int i=0; i<k3; i++) {
+ kernelTempFFT[i][0] = processingContext->kernelFFT[i][0];
+ kernelTempFFT[i][1] = processingContext->kernelFFT[i][1];
+ }
+
+ fftw_execute(realForwardKernelPlan);
+
+ double epsilon = 0.004;
+ double lambda = pow(1.07, processingContext->blur->smooth)/100000.0;;
+ double tau = 1.9 / ( 1 + lambda * 8 / epsilon);
+
+ // Pre-multiply: h*(h*f-y) = h*h*f-y*h, so h*h and y*h we can pre-calculate
+ // 1. Calculation y*h convolution using FFT
+ for(int y=0; y<HEIGHT; y++) {
+ for(int x=0; x<WIDTH; x++) {
+ int index = y*WIDTH+x;
+ fTV[index] = processingContext->inputImageMatrix[index]/255.0;
+ laplacianMatrix[index] = fTV[index];
+ }
+ }
+ fftw_execute(forwardLaplacianPlan);
+ multiplayRealFFTs(laplacianMatrixFFT, kernelTempFFT, WIDTH/2+1, HEIGHT);
+ fftw_execute(backwardLaplacianPlan);
+ for(int y=0; y<HEIGHT; y++) {
+ for(int x=0; x<WIDTH; x++) {
+ int index = y*WIDTH+x;
+ double value = outLaplacianMatrix[index]*k;
+ processingContext->inputImageMatrix[index] = value;
+ }
+ }
+
+ // 2. Calculation h*h
+ multiplayRealFFTs(kernelTempFFT, kernelTempFFT, WIDTH/2+1, HEIGHT);
+
+ // Apply iterative gradient descent method
+ int niter = tvIterationsCount;
+ while (niter > 0) {
+ if (isProcessingCancelled) {
+ fftw_free(GradientMatrix);
+ fftw_free(fTV);
+ return;
+ }
+ if (niter%10==0) {
+ setProgressSubValue(100*(tvIterationsCount-niter)/tvIterationsCount);
+ }
+
+ QTime timeAll;
+ timeAll.start();
+
+ QTime time;
+ time.start();
+
+ // Calculation fTV*h convolution using FFT
+ for(int y=0; y<HEIGHT; y++) {
+ for(int x=0; x<WIDTH; x++) {
+ int index = y*WIDTH+x;
+ laplacianMatrix[index] = fTV[index];
+ }
+ }
+ fftw_execute(forwardLaplacianPlan);
+ multiplayRealFFTs(laplacianMatrixFFT, kernelTempFFT, WIDTH/2+1, HEIGHT);
+ fftw_execute(backwardLaplacianPlan);
+ int fftTime = time.elapsed();
+
+ double tv = 0;
+ double epsilonPow2 = epsilon*epsilon;
+ for(int y=0; y<HEIGHT; y++) {
+ for(int x=0; x<WIDTH; x++) {
+ int index = y*WIDTH+x;
+ // build gradient
+ double curValue = fTV[index];
+ if (y<HEIGHT-1) {
+ GradientMatrix[index][0] = fTV[index+WIDTH] - curValue;
+ } else {
+ GradientMatrix[index][0] = 0;
+ }
+
+ if (x<WIDTH-1) {
+ GradientMatrix[index][1] = fTV[index+1] - curValue;
+ } else {
+ GradientMatrix[index][1] = 0;
+ }
+
+ // Calculate d
+ double kValue = 1/sqrt(epsilonPow2 + GradientMatrix[index][0]*GradientMatrix[index][0] + GradientMatrix[index][1]*GradientMatrix[index][1]);
+
+ GradientMatrix[index][0] *= kValue;
+ GradientMatrix[index][1] *= kValue;
+
+ // Calculate divergence
+ double divergenceValue = 0;
+ if (y>0 && x>0) {
+ double fx = GradientMatrix[index][0] - GradientMatrix[index-WIDTH][0];
+ double fy = GradientMatrix[index][1] - GradientMatrix[index-1][1];
+ divergenceValue = -(fx+fy);
+ }
+
+ // update fTV
+ fTV[index] = fTV[index] - tau*(outLaplacianMatrix[index]*k - processingContext->inputImageMatrix[index] + lambda*divergenceValue);
+ if (niter==1) {
+ double fTVvalue = 255.0*fTV[index];
+ if (fTVvalue <0) {fTVvalue = 0;}
+ if (fTVvalue >255) {fTVvalue = 255;}
+ processingContext->outputImageMatrix[index] = k2 * fTVvalue;
+ }
+ }
+ }
+
+ // qDebug("Iteration:%d tv: %f convolution: %d ms all: %d ms", niter, tv, fftTime, timeAll.elapsed());
+ niter--;
+ }
+
+ fftw_free(GradientMatrix);
+ fftw_free(fTV);
+}
+
+void DeconvolutionTool::buildKernel(double* outKernel, const int WIDTH, const int HEIGHT, const Blur* blur) {
+ QImage* kernelImage;
+
+ double* kernelTempMatrix = (double*)fftw_malloc(sizeof(double)*WIDTH*HEIGHT);
+ if (blur->getName() == "FocusBlur") {
+ kernelImage = ImageUtils::buildKernelImage((FocusBlur*)blur);
+ } else if (blur->getName() == "MotionBlur") {
+ kernelImage = ImageUtils::buildKernelImage((MotionBlur*)blur);
+ } else if (blur->getName() == "GaussianBlur") {
+ kernelImage = ImageUtils::buildKernelImage((GaussianBlur*)blur);
+ }
+ int size = kernelImage->width();
+
+ // Fill kernel
+ double sumKernelElements = 0;
+ for (int y = 0; y<HEIGHT; y++) {
+ for (int x = 0; x<WIDTH; x++) {
+ int index = y*WIDTH + x;
+ int value = 0;
+ // if we are in the kernel area (of small kernelImage), then take pixel values. Otherwise keep 0
+ if (abs(x-WIDTH/2)<(size-2)/2 && abs(y-HEIGHT/2)<(size-2)/2) {
+ int xLocal = x-(WIDTH-size)/2;
+ int yLocal = y-(HEIGHT-size)/2;
+ value = qRed(kernelImage->pixel(xLocal,yLocal));
+ }
+
+ kernelTempMatrix[index] = value;
+ sumKernelElements += abs(value);
+ }
+ }
+
+ delete(kernelImage);
+
+ // Zero-protection
+ if (sumKernelElements==0) {
+ sumKernelElements = 1;
+ }
+
+ // Normalize
+ double k = 1/sumKernelElements;
+ for (int i=0; i<WIDTH*HEIGHT; i++) {
+ kernelTempMatrix[i] *= k;
+ }
+
+ // Translate kernel, because we don't use centered FFT (by multiply input image on pow(-1,x+y))
+ // so we need to translate kernel by width/2 to the left and by height/2 to the up
+ for (int y=0; y<HEIGHT; y++) {
+ for (int x=0; x<WIDTH; x++) {
+ int xTranslated = (x + WIDTH/2) % WIDTH;
+ int yTranslated = (y + HEIGHT/2) % HEIGHT;
+ outKernel[y*WIDTH + x] = kernelTempMatrix[yTranslated*WIDTH + xTranslated];
+ }
+ }
+ fftw_free(kernelTempMatrix);
+}
+
+// Build Laplacian matrix
+// [ 0 -1 0]
+// [-1 4 -1]
+// [ 0 -1 0]
+void DeconvolutionTool::buildLaplacian(fftw_complex *outLaplacianFFT, const int WIDTH, const int HEIGHT) {
+ for (int y = 0; y<HEIGHT; y++) {
+ for (int x = 0; x<WIDTH; x++) {
+ int index = y*WIDTH + x;
+ int value = 0;
+ int xCenter = WIDTH/2;
+ int yCenter = HEIGHT/2;
+ if (x==xCenter && y==yCenter) {
+ value = 4;
+ }
+ if (abs(xCenter-x)+abs(yCenter-y) == 1) {
+ value = -1;
+ }
+
+ outLaplacianFFT[index][0] = value;
+ outLaplacianFFT[index][1] = 0;
+ }
+ }
+}
+
+void DeconvolutionTool::visualizeFFT(fftw_complex* fft, const int WIDTH, const int HEIGHT, QString path) {
+ QImage visualImage(WIDTH, HEIGHT, QImage::Format_RGB32);
+
+ // Find maximum
+ float maxAmpl = 0;
+ float curAmpl = 0;
+ for (int i=0; i<WIDTH*HEIGHT; i++) {
+ // Extract Amplitude
+ curAmpl = sqrt(pow(fft[i][0], 2) + pow(fft[i][1], 2));
+ curAmpl = log(1 + curAmpl);
+ if (curAmpl > maxAmpl) {
+ maxAmpl = curAmpl;
+ }
+ }
+
+ // Build image
+ for(int y=0; y<HEIGHT; y++) {
+ for(int x=0; x<WIDTH; x++) {
+ // Normalize
+ curAmpl = sqrt(pow(fft[y*WIDTH+x][0], 2) + pow(fft[y*WIDTH+x][1], 2));
+ // Log scale
+ curAmpl = 255 * log(1 + curAmpl) / maxAmpl;
+ visualImage.setPixel(x, y, qRgb(curAmpl, curAmpl, curAmpl));
+ }
+ }
+
+ visualImage.save(path);
+}
+
+void DeconvolutionTool::setProgressInterval(int begin, int end, QString text) {
+ if (!isProcessingCancelled) {
+ beginCurrentProgress = begin;
+ endCurrentProgress = end;
+ currentProgressText = text;
+ emit progressEvent(begin, text);
+ } else {
+ emit progressEvent(0, text);
+ }
+}
+
+void DeconvolutionTool::setProgressSubValue(int percentValue) {
+ emit progressEvent(beginCurrentProgress + percentValue*(endCurrentProgress - beginCurrentProgress)/100, currentProgressText);
+ QApplication::processEvents();
+}
+
+
+
+
View
59 src/DeconvolutionTool.h
@@ -9,6 +9,7 @@
#include <QThread>
#include <QPixmap>
#include <QObject>
+#include <QApplication>
#include <time.h>
#ifdef _MSC_VER
@@ -19,6 +20,7 @@
#endif
#include "ImageUtils.h"
+#include "Models/ProcessingContext.h"
#if defined (Q_WS_WIN)
#include "fftw3.h"
#else
@@ -27,39 +29,66 @@
#include <typeinfo>
+
class DeconvolutionTool : public QObject
{
Q_OBJECT
public:
DeconvolutionTool(QObject* parent = 0);
+ ~DeconvolutionTool();
void initFFT(const QImage *inputImage);
- void doDeconvolution(const QImage *inputImage, QImage *outputImage, const Blur *blur);
+ bool doDeconvolution(QImage *inputImage, QImage *outputImage, Blur *blur);
int getThreadsCount();
+ static void visualizeFFT(fftw_complex *fft, const int WIDTH, const int HEIGHT, QString path);
+ static void buildKernel(double* outKernelFFT, const int WIDTH, const int HEIGHT, const Blur* blur);
+ static void multiplayRealFFTs(fftw_complex *outFFT, const fftw_complex *kernelFFT, const int width, const int height);
+ void cancelProcessing();
+ void setTVIterationsCount(int value);
+ void setPreviewMethod(int value);
signals:
- void progressEvent(int);
+ void progressEvent(int, QString);
private:
- void visualizeFFT(fftw_complex *fft, const int WIDTH, const int HEIGHT, QString path);
- void multiplayFFTs(const fftw_complex *firstFFT, const fftw_complex *secondFFT, fftw_complex *outFFT, const int width, const int height);
- void deconvolutionByWiener(const fftw_complex *kernelFFT, const fftw_complex *inImageFFT, fftw_complex *outImageFFT, const int WIDTH, const int HEIGHT, const double K);
+ void removeFFTObjects();
+ void setProgressInterval(int begin, int end, QString text);
+ void setProgressSubValue(int percentValue);
+ void deconvolutionByWiener(ProcessingContext* processingContext);
+ void deconvolutionByTikhonov(ProcessingContext *processingContext);
+ void deconvolutionByTotalVariationPrior(ProcessingContext *processingContext);
+
+ void buildLaplacian(fftw_complex* outLaplacianFFT, const int WIDTH, const int HEIGHT);
+ void doDeconvolutionForChannel(ProcessingContext* processingContext, const CurrentChannel channel);
+
+ volatile bool isProcessingCancelled;
- void buildKernel(fftw_complex* outKernelFFT, const int WIDTH, const int HEIGHT, const Blur *blur);
- void doDeconvolutionForChannel(const QImage *inputImage, QImage *outputImage, const fftw_complex *kernelMatrixFFT, const int width, const int height, const double kernelRadius, const double PSNR, const CurrentChannel channel);
+ int beginCurrentProgress, endCurrentProgress;
+ QString currentProgressText;
int width, height;
int threadsCount;
+ int tvIterationsCount;
+ int previewMethod;
+
+ double *inputImageMatrix;
+ double *outputImageMatrix;
+ double *kernelMatrix;
+
+ fftw_complex *inputImageFFT;
+ fftw_complex *outputImageFFT;
+ fftw_complex *kernelFFT;
+ fftw_complex *kernelTempFFT;
- fftw_complex *inputMatrix;
- fftw_complex *outputMatrix;
- fftw_complex *kernelMatrix;
- fftw_complex *inputMatrixFFT;
- fftw_complex *kernelMatrixFFT;
+ fftw_plan realForwardPlan;
+ fftw_plan realBackwardPlan;
+ fftw_plan realForwardKernelPlan;
+ fftw_plan forwardLaplacianPlan;
+ fftw_plan backwardLaplacianPlan;
- fftw_plan forwardImagePlan;
- fftw_plan forwardKernelPlan;
- fftw_plan backwardImagePlan;
+ double *laplacianMatrix;
+ double *outLaplacianMatrix;
+ fftw_complex *laplacianMatrixFFT;
};
View
BIN src/FFTW/fftw3.pdf
Binary file not shown.
View
36 src/HelpDialog.cpp
@@ -0,0 +1,36 @@
+#include "HelpDialog.h"
+#include "ui_HelpDialog.h"
+#include "MainWindow.h"
+
+
+
+HelpDialog::HelpDialog(QWidget *parent):QDialog(parent),ui(new Ui::HelpDialog) {
+ ui->setupUi(this);
+ ui->mainTextLabel->setText(ui->mainTextLabel->text().arg(MainWindow::appVersion));
+
+ // Load settings
+ settings = new QSettings("SmartDeblur", "SmartDeblur");
+ ui->showOnStartupCheckBox->setChecked(settings->value("ShowHelpOnStartup", true).toBool());
+ ui->openExampleCheckBox->setChecked(settings->value("OpenExampleAfterHelp", true).toBool());
+
+ connect(ui->showOnStartupCheckBox, SIGNAL(toggled(bool)), SLOT(saveSettings(bool)));
+ connect(ui->openExampleCheckBox, SIGNAL(toggled(bool)), SLOT(saveSettings(bool)));
+}
+
+HelpDialog::~HelpDialog() {
+ delete ui;
+}
+
+bool HelpDialog::isShowOnStartup() {
+ return ui->showOnStartupCheckBox->isChecked();
+}
+
+bool HelpDialog::isOpenExampleAfterClose() {
+ return ui->openExampleCheckBox->isChecked();
+}
+
+void HelpDialog::saveSettings(bool value) {
+ settings->setValue("ShowHelpOnStartup", ui->showOnStartupCheckBox->isChecked());
+ settings->setValue("OpenExampleAfterHelp", ui->openExampleCheckBox->isChecked());
+}
+
View
30 src/HelpDialog.h
@@ -0,0 +1,30 @@
+#ifndef HELPDIALOG_H
+#define HELPDIALOG_H
+
+#include <QDialog>
+#include <QSettings>
+
+
+namespace Ui {
+class HelpDialog;
+}
+
+class HelpDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit HelpDialog(QWidget *parent = 0);
+ ~HelpDialog();
+ bool isShowOnStartup();
+ bool isOpenExampleAfterClose();
+
+private slots:
+ void saveSettings(bool value);
+
+private:
+ Ui::HelpDialog *ui;
+ QSettings *settings;
+};
+
+#endif // HELPDIALOG_H
View
253 src/HelpDialog.ui
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HelpDialog</class>
+ <widget class="QDialog" name="HelpDialog">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>550</width>
+ <height>390</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>550</width>
+ <height>390</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>550</width>
+ <height>390</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>About SmartDeblur</string>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QDialog {background-color: rgb(255, 255, 255)}</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <widget class="QLabel" name="label">
+ <property name="geometry">
+ <rect>
+ <x>13</x>
+ <y>8</y>
+ <width>80</width>
+ <height>80</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="MainResources.qrc">:/SmartDeblur/Icons/Logo2.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" name="mainTextLabel">
+ <property name="geometry">
+ <rect>
+ <x>110</x>
+ <y>5</y>
+ <width>430</width>
+ <height>221</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>10000</width>
+ <height>10000</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The &lt;span style=&quot; font-weight:600;&quot;&gt;SmartDeblur&lt;/span&gt; is a tool for restoration of defocused and blurred images. Algorithm based on several deconvolution technics (Wiener, Tikhonov, Total Variation pior). Supported defect types:&lt;/p&gt;
+&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Out-of-Focus blur (with kernel deep tuning)&lt;/li&gt;
+&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Motion blur&lt;/li&gt;
+&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Gaussian blur&lt;/li&gt;&lt;/ul&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;SmartDeblur uses the FFTW library which provides fast Fourier tranformation implementation. See &lt;a href=&quot;www.fftw.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;www.fftw.org&lt;/span&gt;&lt;/a&gt; for details &lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Author: &lt;span style=&quot; font-weight:600;&quot;&gt;Vladimir Yuzhikov&lt;/span&gt; (yuvladimir@gmail.com), the latest sources and binaries are available on: &lt;a href=&quot;https://github.com/Y-Vladimir/SmartDeblur&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/Y-Vladimir/SmartDeblur&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Version: %1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QFrame" name="frame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>340</y>
+ <width>551</width>
+ <height>51</height>
+ </rect>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QFrame {background-color: rgb(240, 240, 240)}</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <widget class="QPushButton" name="okButton">
+ <property name="geometry">
+ <rect>
+ <x>430</x>
+ <y>10</y>
+ <width>111</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="showOnStartupCheckBox">
+ <property name="geometry">
+ <rect>
+ <x>13</x>
+ <y>5</y>
+ <width>161</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Show this dialog on startup</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" name="openExampleCheckBox">
+ <property name="geometry">
+ <rect>
+ <x>13</x>
+ <y>28</y>
+ <width>171</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Open the example after close</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QLabel" name="label_2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>240</y>
+ <width>561</width>
+ <height>101</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: rgb(189, 255, 169);</string>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="midLineWidth">
+ <number>0</number>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; After close the Example with real Out-of-Focus blur will be opened.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; Try that Example and read blurred text and car number! &lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;First of all set &lt;span style=&quot; font-weight:600;&quot;&gt;&amp;quot;Radius&amp;quot;&lt;/span&gt; slider between&lt;span style=&quot; font-weight:600;&quot;&gt; 27&lt;/span&gt; and&lt;span style=&quot; font-weight:600;&quot;&gt; 29&lt;/span&gt; (move slider carefully).&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Then choose desired quality level using &lt;span style=&quot; font-weight:600;&quot;&gt;&amp;quot;Smooth&amp;quot;&lt;/span&gt; slider (&lt;span style=&quot; font-weight:600;&quot;&gt;30&lt;/span&gt; for example).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>10</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_3">
+ <property name="geometry">
+ <rect>
+ <x>9</x>
+ <y>249</y>
+ <width>41</width>
+ <height>41</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="MainResources.qrc">:/SmartDeblur/Icons/Attention.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ <resources>
+ <include location="MainResources.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>okButton</sender>
+ <signal>clicked()</signal>
+ <receiver>HelpDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>285</x>
+ <y>274</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>265</x>
+ <y>149</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
View
BIN src/Icons/Attention.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN src/Icons/BlurExample1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
53 src/ImageUtils.cpp
@@ -93,36 +93,63 @@ QImage *ImageUtils::buildKernelImage(const MotionBlur* motionBlur) {
return kernelImage;
}
-void ImageUtils::fillInputMatrix(fftw_complex *inputMatrix, const QImage *inputImage, const int width, const int height, const CurrentChannel channel) {
- for (int y=0; y<height; y++) {
- QRgb *line = (QRgb*)inputImage->constScanLine(y);
- for (int x=0; x<width; x++) {
+QImage *ImageUtils::buildKernelImage(const GaussianBlur* gaussianBlur) {
+ // Double radius plus 2*3 pixels to ensure that generated kernel will be fitted inside the image
+ int size = 3.5 * gaussianBlur->radius + 6;
+ size += size%2;
+
+ QImage* kernelImage = new QImage(size, size, QImage::Format_RGB32);
+ kernelImage->fill(Qt::red);
+
+ // Prepare painter to have antialiasing and sub-pixel accuracy
+ QPainter kernelPainter(kernelImage);
+ kernelPainter.setRenderHint(QPainter::Antialiasing);
+
+ // Workarround to have high accuracy, otherwise drawLine method has some micro-mistakes in the rendering
+ QPen pen = kernelPainter.pen();
+ pen.setColor(Qt::white);
+ kernelPainter.setPen(pen);
+
+ for (int y=0; y<size; y++) {
+ for (int x=0; x<size; x++) {
+ int value = 255*(pow(M_E, -(pow(x-size/2,2)+pow(y-size/2,2))/(2*pow(gaussianBlur->radius,2))));
+ kernelImage->setPixel(x,y,qRgb(value,value,value));
+ }
+ }
+
+ kernelPainter.end();
+
+ return kernelImage;
+}
+
+void ImageUtils::fillMatrixFromImage(ProcessingContext *processingContext, const CurrentChannel channel) {
+ for (int y=0; y<processingContext->height; y++) {
+ QRgb *line = (QRgb*)processingContext->inputImage->constScanLine(y);
+ for (int x=0; x<processingContext->width; x++) {
int value = 0;
switch (channel) {
case RED: value = qRed(line[x]); break;
case GREEN: value = qGreen(line[x]); break;
case BLUE: value = qBlue(line[x]); break;
case GRAY: value = qGray(line[x]); break;
}
- inputMatrix[y*width + x][0] = centerFFTKoef(x, y) * value;
+ processingContext->inputImageMatrix[y*processingContext->width + x] = value;
}
}
}
-void ImageUtils::fillOutputImage(const QImage *inputImage, const fftw_complex *outputMatrix, QImage *outputImage, const int width, const int height, const CurrentChannel channel) {
- for (int y = 0; y < height; y++) {
- QRgb *line = (QRgb*) outputImage->scanLine(y);
- for (int x = 0; x < width; x++) {
- double value = outputMatrix[y*width + x][0] / (width * height);
- value *= centerFFTKoef(x, y);
- value = fabs(value);
+void ImageUtils::fillImageFromMatrix(ProcessingContext* processingContext, const CurrentChannel channel) {
+ double k = 1.0/(processingContext->width * processingContext->height);
+ for (int y = 0; y < processingContext->height; y++) {
+ QRgb *line = (QRgb*) processingContext->outputImage->scanLine(y);
+ for (int x = 0; x < processingContext->width; x++) {
+ double value = k * processingContext->outputImageMatrix[y*processingContext->width + x];
if (value < 0) {
value = 0;
}
if (value > 255) {
value = 255;
}
-
switch (channel) {
case RED: line[x] = qRgb(value, 0, 0); break;
case GREEN: line[x] = line[x] | qRgb(0, value, 0); break;
View
9 src/ImageUtils.h
@@ -17,10 +17,11 @@
#else
#include <fftw3.h>
#endif
-#include <math.h>
#include "Models/Blur.h"
#include "Models/FocusBlur.h"
#include "Models/MotionBlur.h"
+#include "Models/GaussianBlur.h"
+#include "Models/ProcessingContext.h"
enum CurrentChannel {
RED,
@@ -42,9 +43,11 @@ class ImageUtils {
public:
static QImage* buildKernelImage(const FocusBlur* focusBlur);
static QImage* buildKernelImage(const MotionBlur* motionBlur);
+ static QImage *buildKernelImage(const GaussianBlur* gaussianBlur);
- static void fillInputMatrix(fftw_complex *inputMatrix, const QImage *inputImage, const int width, const int height, const CurrentChannel channel);
- static void fillOutputImage(const QImage *inputImage, const fftw_complex *outputMatrix, QImage *outputImage, const int width, const int height, const CurrentChannel channel);
+ static void fillInputMatrix(ProcessingContext *processingContext, const CurrentChannel channel);
+ static void fillMatrixFromImage(ProcessingContext *processingContext, const CurrentChannel channel);
+ static void fillImageFromMatrix(ProcessingContext *processingContext, const CurrentChannel channel);
};
#endif // IMAGEUTILS_H
View
2 src/MainResources.qrc
@@ -9,5 +9,7 @@
<file>Icons/image.png</file>
<file>Icons/Logo1.png</file>
<file>Icons/Logo2.png</file>
+ <file>Icons/BlurExample1.jpg</file>
+ <file>Icons/Attention.png</file>
</qresource>
</RCC>
View
121 src/MainWindow.cpp
@@ -1,17 +1,21 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
-const QString MainWindow::appVersion = "0.48";
+
const double MainWindow::MAX_IMAGE_PIXELS = 3000000;
-const double MainWindow::MAX_IMAGE_DIMENSION = 2048;
+double MainWindow::MAX_IMAGE_DIMENSION = 3000;
+const QString MainWindow::appVersion = "1.27";
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
+ setWindowTitle("SmartDeblur v. " + appVersion);
resize(1000, 700);
+ helpDialog = new HelpDialog(this);
+
imageLabel = new QLabel;
imageLabel->setBackgroundRole(QPalette::Base);
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
@@ -21,7 +25,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->scrollArea->setWidget(imageLabel);
radius = 9;
- PSNR = 0.001;
+ quality = 30;
workerThread = new WorkerThread();
@@ -33,6 +37,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
updateZoomControls();
progressBar = new QProgressBar();
+ progressBar->setStyleSheet("QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; }");
progressBar->setValue(0);
progressBar->setVisible(false);
lblDeconvolutionTime = new QLabel();
@@ -44,6 +49,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->statusBar->addWidget(progressBar);
workerThread->start();
+
+ if (helpDialog->isShowOnStartup()) {
+ // Delay start because we need to wait while main window will be initialized
+ startupTimer = new QTimer();
+ startupTimer->setSingleShot(true);
+ connect(startupTimer, SIGNAL(timeout()), SLOT(help()));
+ startupTimer->start(500);
+ }
+
+ checkUpdatesThread = new CheckUpdatesThread();
+ //checkUpdatesThread->start();
}
@@ -61,11 +77,15 @@ void MainWindow::updatePreviewImage(int deconvolutionTime) {
lblDeconvolutionTime->setText(" Last operation time: " + QString::number(deconvolutionTime) + " ms ");
}
-void MainWindow::updateProgress(int value) {
+void MainWindow::updateProgress(int value, QString text) {
+ progressBar->setFormat(QString("%1: %p%").arg(text));
progressBar->setVisible(true);
progressBar->setValue(value);
-}
+ if (value==progressBar->maximum()) {
+ progressBar->setVisible(false);
+ }
+}
Blur *MainWindow::generateBlurInfo(bool previewMode) {
Blur* blur;
@@ -75,19 +95,23 @@ Blur *MainWindow::generateBlurInfo(bool previewMode) {
focusBlur->radius = radius;
focusBlur->edgeFeather = feather;
focusBlur->correctionStrength = strength;
- focusBlur->PSNR = PSNR;
- focusBlur->previewMode = previewMode;
kernelImage = ImageUtils::buildKernelImage(focusBlur);
blur = focusBlur;
- } else {
+ } else if (ui->comboBoxType->currentIndex() == 1) {
MotionBlur* motionBlur = new MotionBlur();
motionBlur->radius = motionLength;
motionBlur->angle = motionAngle;
- motionBlur->PSNR = PSNR;
- motionBlur->previewMode = previewMode;
kernelImage = ImageUtils::buildKernelImage(motionBlur);
blur = motionBlur;
+ } else if (ui->comboBoxType->currentIndex() == 2) {
+ GaussianBlur* gaussianBlur = new GaussianBlur();
+ gaussianBlur->radius = radius;
+ kernelImage = ImageUtils::buildKernelImage(gaussianBlur);
+ blur = gaussianBlur;
}
+
+ blur->smooth = quality;
+ blur->mode = previewMode ? PREVIEW_GRAY : HIGH_QUALITY;
// Update kernel preview
ui->labelKernelPreview->setPixmap(QPixmap::fromImage(kernelImage->scaled(ui->labelKernelPreview->size())));
delete(kernelImage);
@@ -120,8 +144,8 @@ void MainWindow::radiusChanged() {
void MainWindow::PSNRChanged() {
// Non-linear transformation
- PSNR = pow(1.07, ui->sliderPSNR->value())/10000.0;
- ui->labelPSNR->setText(QString::number(ui->sliderPSNR->value()) + "%");
+ quality = ui->sliderPSNR->value();
+ ui->labelPSNR->setText(QString::number(quality) + "%");
updatePreviewDeconvolution();
}
@@ -151,15 +175,17 @@ void MainWindow::motionAngleChanged() {
}
void MainWindow::defectTypeChanged(int type) {
- bool motionVisible = (type == 1);
+ bool motionVisible = type == 1;
int yMotion1 = 29;
int yMotion2 = 49;
int yMotion3 = 69;
- bool focusVisible = (type == 0);
+ bool focusVisible = type == 0;
int yFocus1 = 36;
int yFocus2 = 63;
+ bool gaussianVisible = type == 2;
+
// Set visibility
ui->labelMotionLengthCaption->setVisible(motionVisible);
ui->labelMotionLength->setVisible(motionVisible);
@@ -169,9 +195,9 @@ void MainWindow::defectTypeChanged(int type) {
ui->labelMotionAngle->setVisible(motionVisible);
ui->sliderMotionAngle->setVisible(motionVisible);
- ui->labelRadiusCaption->setVisible(focusVisible);
- ui->labelRadius->setVisible(focusVisible);
- ui->sliderRadius->setVisible(focusVisible);
+ ui->labelRadiusCaption->setVisible(focusVisible || gaussianVisible);
+ ui->labelRadius->setVisible(focusVisible || gaussianVisible);
+ ui->sliderRadius->setVisible(focusVisible || gaussianVisible);
ui->labelFeatherCaption->setVisible(focusVisible);
ui->labelFeather->setVisible(focusVisible);
@@ -210,14 +236,14 @@ void MainWindow::defectTypeChanged(int type) {
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
- {
- event->acceptProposedAction();
- }
+{
+ event->acceptProposedAction();
+}
- void MainWindow::dragMoveEvent(QDragMoveEvent *event)
- {
- event->acceptProposedAction();
- }
+void MainWindow::dragMoveEvent(QDragMoveEvent *event)
+{
+ event->acceptProposedAction();
+}
void MainWindow::dropEvent(QDropEvent *event)
@@ -283,7 +309,7 @@ void MainWindow::openFile(QString fileName) {
outputImage = new QImage(inputImage->width(), inputImage->height(), QImage::Format_RGB32);
lblThreadsCount->setText(tr(" Threads: %1 ").arg(workerThread->initFFT(inputImage)));
imageLabel->setPixmap(QPixmap::fromImage(*inputImage));
- updateFullDeconvolution();
+ // updateFullDeconvolution();
ui->checkBoxFitToWindow->setChecked(true);
fitToWindow();
}
@@ -334,18 +360,11 @@ void MainWindow::fitToWindow() {
scaleImage(factor);
}
-void MainWindow::about() {
- QMessageBox::about(this, tr("About SmartDeblur"),
- tr("<p>The <b>SmartDeblur</b> is a tool for restoration of defocused and blurred images. "
- "Algorithm based on Wiener deconvolution.<br>"
- "Supported defect types:<ul>"
- "<li>Out of Focus blur (with kernel deep tuning)</li>"
- "<li>Motion blur</li></ul></p>"
- "<p>SmartDeblur uses the FFTW library which provides fast fourier tranformation implementation. "
- "See <a href='www.fftw.org'>www.fftw.org</a> for details </p>"
- "<p>Author: <b>Vladimir Yuzhikov</b> (yuvladimir@gmail.com), the latest sources and binaries are available on: <a href='https://github.com/Y-Vladimir/SmartDeblur'>https://github.com/Y-Vladimir/SmartDeblur</a></p>"
- "<p><b>Version: %1</b></p>").arg(appVersion));
-
+void MainWindow::help() {
+ helpDialog->exec();
+ if (helpDialog->isOpenExampleAfterClose()) {
+ openFile(":/SmartDeblur/Icons/BlurExample1.jpg");
+ }
}
void MainWindow::showOriginalPressed() {
@@ -369,12 +388,12 @@ void MainWindow::scaleImage(double factor) {
adjustScrollBar(ui->scrollArea->verticalScrollBar(), factor);
ui->btnZoomIn->setEnabled(scaleFactor < 1.0);
- ui->btnZoomOut->setEnabled(scaleFactor > 0.3);
+ ui->btnZoomOut->setEnabled(scaleFactor > 0.3);
}
void MainWindow::initControls() {
ui->sliderRadius->setValue(1);
- ui->sliderPSNR->setValue(40);
+ ui->sliderPSNR->setValue(29);
ui->sliderKernelStrength->setValue(0);
ui->sliderKernelFeather->setValue(20);
@@ -387,6 +406,8 @@ void MainWindow::initControls() {
ui->btnSave->setEnabled(false);
ui->btnShowOriginal->setEnabled(false);
+ ui->imageSizeLimitSpinBox->setValue(MAX_IMAGE_DIMENSION);
+
this->setAcceptDrops(true);
}
@@ -398,7 +419,7 @@ void MainWindow::createActions() {
connect(ui->btnOpen, SIGNAL(clicked()), this, SLOT(open()));
connect(ui->btnSave, SIGNAL(clicked()), this, SLOT(save()));
- connect(ui->btnAbout, SIGNAL(clicked()), this, SLOT(about()));
+ connect(ui->btnAbout, SIGNAL(clicked()), this, SLOT(help()));
connect(ui->sliderRadius, SIGNAL(valueChanged(int)), this, SLOT(radiusChanged()));
connect(ui->sliderKernelFeather, SIGNAL(valueChanged(int)), this, SLOT(kernelFeatherChanged()));
@@ -418,9 +439,14 @@ void MainWindow::createActions() {
connect(ui->btnShowOriginal, SIGNAL(released()), this, SLOT(showOriginalReleased()));
connect(ui->comboBoxType, SIGNAL(currentIndexChanged(int)), this, SLOT(defectTypeChanged(int)));
+ //connect(ui->, SIGNAL())
connect(workerThread, SIGNAL(deconvolutionFinished(int)), SLOT(updatePreviewImage(int)));
- connect(workerThread->getDeconvolutionTool(), SIGNAL(progressEvent(int)), this, SLOT(updateProgress(int)));
+ connect(workerThread->getDeconvolutionTool(), SIGNAL(progressEvent(int, QString)), this, SLOT(updateProgress(int, QString)));
+
+ connect(ui->imageSizeLimitSpinBox, SIGNAL(valueChanged(int)), SLOT(imageSizeLimitChanged(int)));
+ connect(ui->tvIterationsCountSpinBox, SIGNAL(valueChanged(int)), SLOT(tvIterationsCountChanged(int)));
+ connect(ui->previewMethodComboBox, SIGNAL(currentIndexChanged(int)), SLOT(previewMethodChanged(int)));
}
void MainWindow::adjustScrollBar(QScrollBar *scrollBar, double factor) {
@@ -444,3 +470,16 @@ void MainWindow::updateZoomControls() {
fitToWindow();
}
}
+
+
+void MainWindow::imageSizeLimitChanged(int value) {
+ MAX_IMAGE_DIMENSION = value;
+}
+
+void MainWindow::tvIterationsCountChanged(int value) {
+ workerThread->getDeconvolutionTool()->setTVIterationsCount(value);
+}
+
+void MainWindow::previewMethodChanged(int value) {
+ workerThread->getDeconvolutionTool()->setPreviewMethod(value);
+}
View
19 src/MainWindow.h
@@ -6,6 +6,7 @@
#include <QImage>
#include <QPainter>
#include <QTime>
+#include <QTimer>
#include <QThread>
#include <QLabel>
#include <QScrollBar>
@@ -35,6 +36,8 @@
#endif
#include "DeconvolutionTool.h"
#include "WorkerThread.h"
+#include "HelpDialog.h"
+#include "CheckUpdatesThread.h"
namespace Ui {
@@ -49,7 +52,7 @@ class MainWindow : public QMainWindow
public:
static const double MAX_IMAGE_PIXELS;
- static const double MAX_IMAGE_DIMENSION;
+ static double MAX_IMAGE_DIMENSION;
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
@@ -61,8 +64,11 @@ class MainWindow : public QMainWindow
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
private:
- Ui::MainWindow *ui;
+ Ui::MainWindow *ui;
+ HelpDialog *helpDialog;
+ QTimer *startupTimer;
WorkerThread *workerThread;
+ CheckUpdatesThread *checkUpdatesThread;
QLabel *imageLabel;
double scaleFactor;
@@ -74,7 +80,7 @@ class MainWindow : public QMainWindow
QLabel* lblThreadsCount;
QLabel* lblImageSize;
- double radius, PSNR, feather, strength, motionLength, motionAngle;
+ double radius, quality, feather, strength, motionLength, motionAngle;
Blur* generateBlurInfo(bool previewMode);
void updatePreviewDeconvolution();
@@ -101,13 +107,16 @@ private slots:
void zoomOut();
void actualSize();
void fitToWindow();
- void about();
+ void help();
void showOriginalPressed();
void showOriginalReleased();
void updateFullDeconvolution();
void updateZoomControls();
void updatePreviewImage(int deconvolutionTime);
- void updateProgress(int value);
+ void updateProgress(int value, QString text);
+ void imageSizeLimitChanged(int value);
+ void tvIterationsCountChanged(int value);
+ void previewMethodChanged(int value);
};
View
241 src/MainWindow.ui
@@ -188,7 +188,7 @@ QLabel {background-color: none;}
</rect>
</property>
<property name="text">
- <string>About</string>
+ <string>Help</string>
</property>
<property name="icon">
<iconset resource="MainResources.qrc">
@@ -416,7 +416,7 @@ QLabel {background-color: none;}
<number>99</number>
</property>
<property name="value">
- <number>50</number>
+ <number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -467,6 +467,11 @@ QLabel {background-color: none;}
<string>Motion Blur</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>Gaussian Blur</string>
+ </property>
+ </item>
</widget>
<widget class="QSlider" name="sliderMotionLength">
<property name="geometry">
@@ -854,6 +859,238 @@ QLabel {background-color: none;}
<attribute name="title">
<string>Settings</string>
</attribute>
+ <widget class="QFrame" name="frame_4">
+ <property name="geometry">
+ <rect>
+ <x>1</x>
+ <y>2</y>
+ <width>161</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QFrame {background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(165, 206, 255, 255), stop:0.744318 rgba(219, 240, 255, 255))}
+
+QLabel {background-color: none;}
+
+</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <widget class="QLabel" name="label_6">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>90</y>
+ <width>162</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(165, 206, 255, 255), stop:0.744318 rgba(219, 240, 255, 255))
+</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text">
+ <string>General</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label">
+ <property name="geometry">
+ <rect>
+ <x>6</x>
+ <y>7</y>
+ <width>151</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Image size limit (largest side):</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="imageSizeLimitSpinBox">
+ <property name="geometry">
+ <rect>
+ <x>8</x>
+ <y>27</y>
+ <width>81</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="minimum">
+ <number>100</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ <property name="value">
+ <number>3000</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_8">
+ <property name="geometry">
+ <rect>
+ <x>5</x>
+ <y>52</y>
+ <width>151</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">color: rgb(214, 0, 0);</string>
+ </property>
+ <property name="text">
+ <string>Increase the value may cause slow/unstable work!</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QFrame" name="frame_6">
+ <property name="geometry">
+ <rect>
+ <x>165</x>
+ <y>2</y>
+ <width>173</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QFrame {background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(165, 206, 255, 255), stop:0.744318 rgba(219, 240, 255, 255))}
+
+QLabel {background-color: none;}
+
+</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <widget class="QLabel" name="label_9">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>90</y>
+ <width>174</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(165, 206, 255, 255), stop:0.744318 rgba(219, 240, 255, 255))
+</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text">
+ <string>Deconvolution</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QSpinBox" name="tvIterationsCountSpinBox">
+ <property name="geometry">
+ <rect>
+ <x>9</x>
+ <y>27</y>
+ <width>81</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="minimum">
+ <number>50</number>
+ </property>
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ <property name="value">
+ <number>500</number>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_10">
+ <property name="geometry">
+ <rect>
+ <x>7</x>
+ <y>7</y>
+ <width>161</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Iterations count for High-Quality:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label_11">
+ <property name="geometry">
+ <rect>
+ <x>9</x>
+ <y>61</y>
+ <width>91</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Preview method:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" name="previewMethodComboBox">
+ <property name="geometry">
+ <rect>
+ <x>98</x>
+ <y>58</y>
+ <width>70</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <item>
+ <property name="text">
+ <string>Wiener</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Tikhonov</string>
+ </property>
+ </item>
+ </widget>
+ </widget>
</widget>
</widget>
</item>
View
4 src/MathUtlis.cpp
@@ -0,0 +1,4 @@
+#include "MathUtlis.h"
+
+MathUtlis::MathUtlis() {
+}
View
24 src/MathUtlis.h
@@ -0,0 +1,24 @@
+#ifndef MATHUTLIS_H
+#define MATHUTLIS_H
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#if defined (Q_WS_WIN)
+#include "fftw3.h"
+#else
+#include <fftw3.h>
+#endif
+
+class MathUtlis
+{
+public:
+ MathUtlis();
+ //static void gradient(fftw_complex *inputMatrix, const QImage *inputImage, const int width, const int height);
+};
+
+#endif // MATHUTLIS_H
View
36 src/Models/Blur.h
@@ -1,15 +1,21 @@
-#ifndef BLUR_H
-#define BLUR_H
-
-#include <QString>
-
-class Blur
-{
-public:
- bool previewMode;
- double radius;
- double PSNR;
- const virtual QString getName() const = 0;
-};
-
-#endif // BLUR_H
+#ifndef BLUR_H
+#define BLUR_H
+
+#include <QString>
+
+enum ProcessMode {
+ PREVIEW_GRAY,
+ PREVIEW_COLOR,
+ HIGH_QUALITY
+};
+
+class Blur
+{
+public:
+ ProcessMode mode;
+ double radius;
+ double smooth;
+ const virtual QString getName() const = 0;
+};
+
+#endif // BLUR_H
View
14 src/Models/GaussianBlur.h
@@ -0,0 +1,14 @@
+#ifndef GAUSSIANBLUR_H
+#define GAUSSIANBLUR_H
+
+#include "Blur.h"
+
+class GaussianBlur : public Blur
+{
+public:
+ const QString getName() const{
+ return "GaussianBlur";
+ }
+};
+
+#endif // GAUSSIANBLUR_H
View
21 src/Models/ProcessingContext.h
@@ -0,0 +1,21 @@
+#ifndef PROCESSINGCONTEXT_H
+#define PROCESSINGCONTEXT_H
+
+#include "ImageUtils.h"
+
+class ProcessingContext {
+
+public:
+ QImage *inputImage;
+ QImage *outputImage;
+ double *inputImageMatrix;
+ double *outputImageMatrix;
+ fftw_complex *inputImageFFT;
+ fftw_complex *outputImageFFT;
+ fftw_complex *kernelFFT;
+ int width;
+ int height;
+ Blur *blur;
+};
+
+#endif // PROCESSINGCONTEXT_H
View
89 src/SmartDeblur.pro
@@ -1,40 +1,49 @@
-#-------------------------------------------------
-#
-# Project created by QtCreator 2012-05-19T18:47:06
-#
-#-------------------------------------------------
-
-QT += core gui
-
-TARGET = SmartDeblur
-TEMPLATE = app
-
-
-SOURCES += main.cpp\
- MainWindow.cpp \
- DeconvolutionTool.cpp \
- WorkerThread.cpp \
- ImageUtils.cpp
-
-HEADERS += MainWindow.h \
- FFTW/fftw3.h \
- DeconvolutionTool.h \
- WorkerThread.h \
- ImageUtils.h \
- Models/Blur.h \
- Models/FocusBlur.h \
- Models/MotionBlur.h
-
-FORMS += MainWindow.ui
-
-win32: LIBS += -L$$PWD/FFTW/libs/ -llibfftw3-3
-unix: LIBS += -L$$/usr/lib/ -lfftw3_threads -lfftw3
-
-INCLUDEPATH += $$PWD/FFTW
-DEPENDPATH += $$PWD/FFTW
-
-RESOURCES += \
- MainResources.qrc
-
-RC_FILE = SmartDeblur.rc
-#CONFIG += console
+#-------------------------------------------------
+#
+# Project created by QtCreator 2012-05-19T18:47:06
+#
+#-------------------------------------------------
+
+QT += core gui network xml
+
+TARGET = SmartDeblur
+TEMPLATE = app
+
+
+SOURCES += main.cpp\
+ MainWindow.cpp \
+ DeconvolutionTool.cpp \
+ WorkerThread.cpp \
+ ImageUtils.cpp \
+ MathUtlis.cpp \
+ HelpDialog.cpp \
+ CheckUpdatesThread.cpp
+
+HEADERS += MainWindow.h \
+ FFTW/fftw3.h \
+ DeconvolutionTool.h \
+ WorkerThread.h \
+ ImageUtils.h \
+ Models/Blur.h \
+ Models/FocusBlur.h \
+ Models/MotionBlur.h \
+ MathUtlis.h \
+ Models/ProcessingContext.h \
+ HelpDialog.h \
+ Models/GaussianBlur.h \
+ CheckUpdatesThread.h
+
+FORMS += MainWindow.ui \
+ HelpDialog.ui
+
+win32: LIBS += -L$$PWD/FFTW/libs/ -llibfftw3-3
+unix: LIBS += -L$$/usr/lib/ -lfftw3_threads -lfftw3
+
+INCLUDEPATH += $$PWD/FFTW
+DEPENDPATH += $$PWD/FFTW
+
+RESOURCES += \
+ MainResources.qrc
+
+RC_FILE = SmartDeblur.rc
+#CONFIG += console
View
106 src/WorkerThread.cpp
@@ -1,49 +1,57 @@
-#include "WorkerThread.h"
-
-WorkerThread::WorkerThread(QObject *parent) : QThread(parent) {
- deconvolutionTool = new DeconvolutionTool();
- mutex = new QMutex();
- requestCondition = new QWaitCondition();
-}
-
-int WorkerThread::initFFT(const QImage *inputImage) {
- deconvolutionTool->initFFT(inputImage);
- isRequestUpdated = false;
- return deconvolutionTool->getThreadsCount();
-}
-
-void WorkerThread::deconvolutionRequest(QImage *inputImage, QImage *outputImage, Blur* blur) {
- this->blur = blur;
- this->inputImage = inputImage;
- this->outputImage = outputImage;
- isRequestUpdated = true;
-
- // Wake-up waited thread
- requestCondition->wakeAll();
-}
-
-DeconvolutionTool *WorkerThread::getDeconvolutionTool() {
- return deconvolutionTool;
-}
-
-
-void WorkerThread::run() {
- qDebug("Started");
- QTime timer;
- while (true) {
- mutex->lock();