diff options
Diffstat (limited to 'digikam/imageplugins/inpainting/inpaintingtool.cpp')
-rw-r--r-- | digikam/imageplugins/inpainting/inpaintingtool.cpp | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/digikam/imageplugins/inpainting/inpaintingtool.cpp b/digikam/imageplugins/inpainting/inpaintingtool.cpp new file mode 100644 index 0000000..ce6efec --- /dev/null +++ b/digikam/imageplugins/inpainting/inpaintingtool.cpp @@ -0,0 +1,430 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdio> +#include <cmath> +#include <cstring> + +// Qt includes. + +#include <qbrush.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qevent.h> +#include <qfile.h> +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qtabwidget.h> +#include <qtimer.h> +#include <qtooltip.h> +#include <qvgroupbox.h> +#include <qwhatsthis.h> + +// KDE includes. + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcursor.h> +#include <kfiledialog.h> +#include <kglobalsettings.h> +#include <khelpmenu.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprogress.h> +#include <kstandarddirs.h> +#include <kurllabel.h> + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimgthreadedfilter.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "greycstorationsettings.h" +#include "greycstorationwidget.h" +#include "greycstorationiface.h" +#include "inpaintingtool.h" +#include "inpaintingtool.moc" + +using namespace Digikam; + +namespace DigikamInPaintingImagesPlugin +{ + +InPaintingTool::InPaintingTool(QObject* parent) + : EditorToolThreaded(parent) +{ + setName("inpainting"); + setToolName(i18n("Inpainting")); + setToolIcon(SmallIcon("inpainting")); + + m_isComputed = false; + + m_previewWidget = new ImageWidget("inpainting Tool", 0, + i18n("<p>Here you can see the image selection preview with " + "inpainting applied."), + true, ImageGuideWidget::HVGuideMode, false, true); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Try| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + QGridLayout* gridSettings = new QGridLayout(m_gboxSettings->plainPage(), 2, 1); + m_mainTab = new QTabWidget( m_gboxSettings->plainPage() ); + + QWidget* firstPage = new QWidget( m_mainTab ); + QGridLayout* grid = new QGridLayout( firstPage, 2, 2); + m_mainTab->addTab( firstPage, i18n("Preset") ); + + KURLLabel *cimgLogoLabel = new KURLLabel(firstPage); + cimgLogoLabel->setText(QString()); + cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + KGlobal::dirs()->addResourceType("logo-cimg", KGlobal::dirs()->kde_default("data") + "digikam/data"); + QString directory = KGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + cimgLogoLabel->setPixmap( QPixmap( directory + "logo-cimg.png" ) ); + QToolTip::add(cimgLogoLabel, i18n("Visit CImg library website")); + + QLabel *typeLabel = new QLabel(i18n("Filtering type:"), firstPage); + typeLabel->setAlignment ( Qt::AlignRight | Qt::AlignVCenter); + m_inpaintingTypeCB = new QComboBox( false, firstPage ); + m_inpaintingTypeCB->insertItem( i18n("None") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Small Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Medium Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Large Artefact") ); + QWhatsThis::add( m_inpaintingTypeCB, i18n("<p>Select the filter preset to use for photograph restoration:<p>" + "<b>None</b>: Most common values. Puts settings to default.<p>" + "<b>Remove Small Artefact</b>: inpaint small image artefact like image glitch.<p>" + "<b>Remove Medium Artefact</b>: inpaint medium image artefact.<p>" + "<b>Remove Large Artefact</b>: inpaint image artefact like unwanted object.<p>")); + + grid->addMultiCellWidget(cimgLogoLabel, 0, 0, 1, 1); + grid->addMultiCellWidget(typeLabel, 1, 1, 0, 0); + grid->addMultiCellWidget(m_inpaintingTypeCB, 1, 1, 1, 1); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + grid->setRowStretch(1, 10); + + // ------------------------------------------------------------- + + m_settingsWidget = new GreycstorationWidget(m_mainTab); + + gridSettings->addMultiCellWidget(m_mainTab, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(new QLabel(m_gboxSettings->plainPage()), 1, 1, 1, 1); + gridSettings->setMargin(m_gboxSettings->spacingHint()); + gridSettings->setSpacing(m_gboxSettings->spacingHint()); + gridSettings->setRowStretch(1, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(cimgLogoLabel, SIGNAL(leftClickedURL(const QString&)), + this, SLOT(processCImgURL(const QString&))); + + connect(m_inpaintingTypeCB, SIGNAL(activated(int)), + this, SLOT(slotResetValues(int))); + + // ------------------------------------------------------------- + + GreycstorationSettings defaults; + defaults.setInpaintingDefaultSettings(); + m_settingsWidget->setDefaultSettings(defaults); +} + +InPaintingTool::~InPaintingTool() +{ +} + +void InPaintingTool::renderingFinished() +{ + m_mainTab->setEnabled(true); +} + +void InPaintingTool::readSettings() +{ + KConfig* config = kapp->config(); + config->setGroup("inpainting Tool"); + + GreycstorationSettings settings; + GreycstorationSettings defaults; + defaults.setInpaintingDefaultSettings(); + + settings.fastApprox = config->readBoolEntry("FastApprox", defaults.fastApprox); + settings.interp = config->readNumEntry("Interpolation", defaults.interp); + settings.amplitude = config->readDoubleNumEntry("Amplitude", defaults.amplitude); + settings.sharpness = config->readDoubleNumEntry("Sharpness", defaults.sharpness); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", defaults.anisotropy); + settings.alpha = config->readDoubleNumEntry("Alpha", defaults.alpha); + settings.sigma = config->readDoubleNumEntry("Sigma", defaults.sigma); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", defaults.gaussPrec); + settings.dl = config->readDoubleNumEntry("Dl", defaults.dl); + settings.da = config->readDoubleNumEntry("Da", defaults.da); + settings.nbIter = config->readNumEntry("Iteration", defaults.nbIter); + settings.tile = config->readNumEntry("Tile", defaults.tile); + settings.btile = config->readNumEntry("BTile", defaults.btile); + m_settingsWidget->setSettings(settings); + + int p = config->readNumEntry("Preset", NoPreset); + m_inpaintingTypeCB->setCurrentItem(p); + if (p == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); +} + +void InPaintingTool::writeSettings() +{ + GreycstorationSettings settings = m_settingsWidget->getSettings(); + KConfig* config = kapp->config(); + config->setGroup("inpainting Tool"); + config->writeEntry("Preset", m_inpaintingTypeCB->currentItem()); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + m_previewWidget->writeSettings(); + config->sync(); +} + +void InPaintingTool::slotResetValues(int i) +{ + if (i == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); + + slotResetSettings(); +} + +void InPaintingTool::slotResetSettings() +{ + GreycstorationSettings settings; + settings.setInpaintingDefaultSettings(); + + switch(m_inpaintingTypeCB->currentItem()) + { + case RemoveSmallArtefact: + // We use default settings here. + break; + + case RemoveMediumArtefact: + { + settings.amplitude = 50.0; + settings.nbIter = 50; + break; + } + + case RemoveLargeArtefact: + { + settings.amplitude = 100.0; + settings.nbIter = 100; + break; + } + } + + m_settingsWidget->setSettings(settings); +} + +void InPaintingTool::processCImgURL(const QString& url) +{ + KApplication::kApplication()->invokeBrowser(url); +} + +void InPaintingTool::prepareEffect() +{ + m_mainTab->setEnabled(false); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + m_originalImage = DImg(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + // Selected area from the image and mask creation: + // + // We optimize the computation time to use the current selected area in image editor + // and to create an inpainting mask with it. Because inpainting is done by interpolation + // neighboor pixels which can be located far from the selected area, we need to ajust the + // mask size in according with the parameter algorithms, especially 'amplitude'. + // Mask size is computed like this : + // + // (image_size_x + 2*amplitude , image_size_y + 2*amplitude) + + + QRect selectionRect = QRect(iface.selectedXOrg(), iface.selectedYOrg(), + iface.selectedWidth(), iface.selectedHeight()); + + QPixmap inPaintingMask(iface.originalWidth(), iface.originalHeight()); + inPaintingMask.fill(Qt::black); + QPainter p(&inPaintingMask); + p.fillRect( selectionRect, QBrush(Qt::white) ); + p.end(); + + GreycstorationSettings settings = m_settingsWidget->getSettings(); + + int x1 = (int)(selectionRect.left() - 2*settings.amplitude); + int y1 = (int)(selectionRect.top() - 2*settings.amplitude); + int x2 = (int)(selectionRect.right() + 2*settings.amplitude); + int y2 = (int)(selectionRect.bottom() + 2*settings.amplitude); + m_maskRect = QRect(x1, y1, x2-x1, y2-y1); + + // Mask area normalization. + // We need to check if mask area is out of image size else inpainting give strange results. + + if (m_maskRect.left() < 0) m_maskRect.setLeft(0); + if (m_maskRect.top() < 0) m_maskRect.setTop(0); + if (m_maskRect.right() > iface.originalWidth()) m_maskRect.setRight(iface.originalWidth()); + if (m_maskRect.bottom() > iface.originalHeight()) m_maskRect.setBottom(iface.originalHeight()); + + m_maskImage = inPaintingMask.convertToImage().copy(m_maskRect); + m_cropImage = m_originalImage.copy(m_maskRect); + + setFilter(dynamic_cast<DImgThreadedFilter*>( + new GreycstorationIface( + &m_cropImage, + settings, + GreycstorationIface::InPainting, + 0, 0, + m_maskImage, this))); +} + +void InPaintingTool::prepareFinal() +{ + if (!m_isComputed) + { + prepareEffect(); + } + else + { + Digikam::DImgThreadedFilter::EventData *eventData = new Digikam::DImgThreadedFilter::EventData(); + eventData->progress = 100; + eventData->starting = false; + eventData->success = true; + QApplication::postEvent(this, new QCustomEvent(QEvent::User, eventData)); + } +} + +void InPaintingTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + GreycstorationSettings settings = m_settingsWidget->getSettings(); + + m_cropImage = filter()->getTargetImage(); + QRect cropSel((int)(2*settings.amplitude), (int)(2*settings.amplitude), + iface->selectedWidth(), iface->selectedHeight()); + DImg imDest = m_cropImage.copy(cropSel); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + m_previewWidget->updatePreview(); + m_isComputed = true; +} + +void InPaintingTool::putFinalData() +{ + ImageIface iface(0, 0); + + if (!m_isComputed) + m_cropImage = filter()->getTargetImage(); + + m_originalImage.bitBltImage(&m_cropImage, m_maskRect.left(), m_maskRect.top()); + + iface.putOriginalImage(i18n("InPainting"), m_originalImage.bits()); +} + +void InPaintingTool::slotLoadSettings() +{ + KURL loadInpaintingFile = KFileDialog::getOpenURL(KGlobalSettings::documentPath(), + QString( "*" ), kapp->activeWindow(), + QString( i18n("Photograph Inpainting Settings File to Load")) ); + if( loadInpaintingFile.isEmpty() ) + return; + + QFile file(loadInpaintingFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!m_settingsWidget->loadSettings(file, QString("# Photograph Inpainting Configuration File V2"))) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Photograph Inpainting settings text file.") + .arg(loadInpaintingFile.fileName())); + file.close(); + return; + } + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Photograph Inpainting text file.")); + + file.close(); + m_inpaintingTypeCB->blockSignals(true); + m_inpaintingTypeCB->setCurrentItem(NoPreset); + m_inpaintingTypeCB->blockSignals(false); + m_settingsWidget->setEnabled(true); +} + +void InPaintingTool::slotSaveAsSettings() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(KGlobalSettings::documentPath(), + QString( "*" ), kapp->activeWindow(), + QString( i18n("Photograph Inpainting Settings File to Save")) ); + if( saveRestorationFile.isEmpty() ) + return; + + QFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + m_settingsWidget->saveSettings(file, QString("# Photograph Inpainting Configuration File V2")); + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Inpainting text file.")); + + file.close(); +} + +} // NameSpace DigikamInPaintingImagesPlugin |