You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kipi-plugins/kipi-plugins/findimages/finddupplicateimages.cpp

437 lines
12 KiB

//////////////////////////////////////////////////////////////////////////////
//
// FINDDUPPLICATEIMAGES.CPP
//
// Copyright (C) 2001 Richard Groult <rgroult at jalix.org> (from ShowImg project)
// Copyright (C) 2004 Gilles Caulier <caulier dot gilles at gmail dot com>
// Copyright (C) 2004 Richard Groult <rgroult at jalix.org>
//
// 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 of the License, 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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
//
//////////////////////////////////////////////////////////////////////////////
// Include files for C ansi
extern "C"
{
#include <stdlib.h>
#include <math.h>
}
// Include files for TQt
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqimage.h>
#include <tqprogressdialog.h>
#include <tqmutex.h>
// Include files for KDE
#include <tdelocale.h>
#include <kinstance.h>
#include <tdeconfig.h>
#include <tdeapplication.h>
#include <kimageeffect.h>
#include <kdebug.h>
#include <kprogress.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <kurl.h>
#include <tdeio/job.h>
#include <tdeio/jobclasses.h>
#include <tdeio/netaccess.h>
#include <tdeio/global.h>
#include <kimageio.h>
// Local include files
#include "finddupplicateimages.h"
#include "finddupplicatedialog.h"
#include "displaycompare.h"
#include "actions.h"
#include <tqcursor.h>
#include "imagesimilaritydata.h"
#include "fuzzycompare.h"
#include "fastcompare.h"
namespace KIPIFindDupplicateImagesPlugin
{
//////////////////////////////////// CONSTRUCTOR ////////////////////////////////////////////
FindDuplicateImages::FindDuplicateImages( KIPI::Interface* interface, TQObject *parent)
: TQObject(parent), TQThread(), m_interface( interface ),
m_cacheDir(TDEGlobal::dirs()->saveLocation("cache", "kipi-findduplicate/")),
m_compareOp( 0 )
{
KImageIO::registerFormats();
parent_ = parent;
}
//////////////////////////////////// DESTRUCTOR /////////////////////////////////////////////
FindDuplicateImages::~FindDuplicateImages()
{
delete m_findDuplicateDialog;
wait();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
void FindDuplicateImages::writeSettings(void)
{
config = new TDEConfig("kipirc");
config->setGroup("FindDuplicateImages Settings");
// Method dialogbox setup tab
config->writeEntry("FindMethod", m_findDuplicateDialog->getFindMethod());
config->writeEntry("ApproximateThreeshold", m_findDuplicateDialog->getApproximateThreeshold());
config->sync();
delete config;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void FindDuplicateImages::readSettings(void)
{
config = new TDEConfig("kipirc");
config->setGroup("FindDuplicateImages Settings");
// Method dialogbox setup tab
m_findDuplicateDialog->setFindMethod( config->readNumEntry("FindMethod", FindDuplicateDialog::MethodAlmost ) );
m_findDuplicateDialog->setApproximateThreeshold( config->readNumEntry("ApproximateThreeshold", 88) );
delete config;
// Get the image files filters from the hosts app.
m_imagesFileFilter = m_interface->fileExtensions();
}
/////////////////////////////////////////////////////////////////////////////////////////////
bool FindDuplicateImages::execDialog()
{
tqApp->setOverrideCursor( TQCursor(TQt::WaitCursor) );
m_findDuplicateDialog = new FindDuplicateDialog( m_interface, TQT_TQWIDGET(kapp->activeWindow()) );
tqApp->restoreOverrideCursor();
readSettings();
connect( m_findDuplicateDialog, TQT_SIGNAL(updateCache(TQStringList)),
this, TQT_SLOT(slotUpdateCache(TQStringList)) );
connect( m_findDuplicateDialog, TQT_SIGNAL(clearCache(TQStringList)),
this, TQT_SLOT(slotClearCache(TQStringList)) );
connect( m_findDuplicateDialog, TQT_SIGNAL(clearAllCache()),
this, TQT_SLOT(slotClearAllCache()) );
if ( m_findDuplicateDialog->exec() == TQDialog::Accepted )
{
// This is the value for approximate comparison level between 2 images.
m_approximateLevel = (float) m_findDuplicateDialog->getApproximateThreeshold() / (float)100;
writeSettings();
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void FindDuplicateImages::showResult()
{
if( !m_res.isEmpty() )
DisplayCompare((TQWidget *)(TQT_TQWIDGET(kapp->activeWindow())), m_interface, m_res).exec();
else
KMessageBox::information(TQT_TQWIDGET(kapp->activeWindow()), i18n("No identical files found"));
}
/////////////////////////////////////////////////////////////////////////////////////////////
void FindDuplicateImages::compareAlbums(void)
{
tqApp->setOverrideCursor( TQCursor(TQt::WaitCursor) );
writeSettings();
// Prepare the data for the threaded operations.
TQValueList<KIPI::ImageCollection> ListAlbums(m_findDuplicateDialog->getSelectedAlbums());
filesList.clear();
for( TQValueList<KIPI::ImageCollection>::Iterator it = ListAlbums.begin(); it != ListAlbums.end(); ++it )
{
KURL::List Files = (*it).images();
for( KURL::List::Iterator it2 = Files.begin(); it2 != Files.end(); ++it2 )
{
if ( !filesList.contains( (*it2).path() ) )
{
filesList.append( (*it2).path() ); // PENDING(blackie) handle remote URLS
}
}
kapp->processEvents();
}
if ( m_findDuplicateDialog->getFindMethod() == FindDuplicateDialog::MethodAlmost )
{
FuzzyCompare *op = new FuzzyCompare( parent_, m_cacheDir );
op->setApproximateThreeshold( m_approximateLevel );
m_compareOp = op;
}
else
m_compareOp = new FastCompare( parent_ );
start(); // Starting the thread.
tqApp->restoreOverrideCursor();
}
/////////////////////////////////////////////////////////////////////////////////////////////
// List of threaded operations.
void FindDuplicateImages::run()
{
m_res = m_compareOp->compare(filesList );
sendMessage( parent_, KIPIFindDupplicateImagesPlugin::Progress, TQString(), 0, false, true );
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Nota: original source code from ShowImg !
void FindDuplicateImages::slotClearCache(TQStringList fromDirs)
{
bool delOk = true;
for ( TQStringList::Iterator it = fromDirs.begin(); it != fromDirs.end(); ++it )
{
TQString deleteImage = m_cacheDir + *it ;
if ( DeleteDir(deleteImage) == false )
delOk = false;
}
if ( delOk == true )
KMessageBox::information(m_findDuplicateDialog, i18n("Selected Albums cache purged successfully!"));
else
KMessageBox::error(m_findDuplicateDialog, i18n("Cannot purge selected Albums cache!"));
}
/////////////////////////////////////////////////////////////////////////////////////////////
void FindDuplicateImages::slotClearAllCache(void)
{
bool delOk = DeleteDir(m_cacheDir);
if ( delOk == true )
KMessageBox::information(m_findDuplicateDialog, i18n("All cache purged successfully!"));
else
KMessageBox::error(m_findDuplicateDialog, i18n("Cannot purge all cache!"));
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Nota: original source code from ShowImg !
void FindDuplicateImages::slotUpdateCache(TQStringList fromDirs)
{
pdCache = new TQProgressDialog (m_findDuplicateDialog, "tmppb", true);
pdCache->setLabelText(i18n("Updating in progress..."));
pdCache->setTotalSteps(2);
pdCache->show();
pdCache->setProgress(2);
for ( TQStringList::Iterator it = fromDirs.begin(); it != fromDirs.end(); ++it )
updateCache(*it);
pdCache->close();
delete(pdCache);
KMessageBox::information(m_findDuplicateDialog, i18n("Selected Albums cache updated successfully!"));
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Nota: original source code from ShowImg !
void FindDuplicateImages::updateCache(TQString fromDir)
{
// PENDING(blackie) this method doesn't seem to work.
kdDebug( 51000 ) << fromDir.ascii() << endl;
pdCache->setLabelText(i18n("Updating in progress for:\n") + fromDir);
TQDir d(m_cacheDir + fromDir);
int len = m_cacheDir.length()-1; // Remove trailing /
bool delDir = false;
kdDebug( 51000 ) << m_cacheDir + fromDir.latin1() << endl;
if ( !TQFileInfo(fromDir).exists() )
delDir = true; // If the source folder have been removed, remove also the cache...
d.setFilter( TQDir::All | TQDir::Hidden | TQDir::NoSymLinks );
const TQFileInfoList *list = d.entryInfoList();
if ( !list )
return;
TQFileInfoListIterator it( *list );
TQFileInfo *fi;
while ( (fi = it.current()) != 0 )
{
kapp->processEvents();
TQString fCache=fi->absFilePath();
TQString orgFile=fCache.right(fCache.length()-len);
if ( fi->isDir() && !fromDir.startsWith(orgFile) )
{
updateCache(orgFile);
}
else
{
if ( !TQFileInfo(orgFile).exists() && TQFileInfo(orgFile).extension(false) != "dat" )
{
TQDir().remove(fCache);
TQDir().remove(fCache + ".dat");
}
}
++it;
}
if (delDir)
TQDir().rmdir(m_cacheDir + fromDir);
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Nota: original source code from ShowImg !
float FindDuplicateImages::image_sim_compare(ImageSimilarityData *a, ImageSimilarityData *b)
{
float sim;
int i;
if ( !a || !b || !a->filled || !b->filled )
return 0.0;
sim = 0.0;
for( i = 0; i < PAS*PAS; i++ )
{
sim += (float)abs(a->avg_r[i] - b->avg_r[i]) / 255.0;
sim += (float)abs(a->avg_g[i] - b->avg_g[i]) / 255.0;
sim += (float)abs(a->avg_b[i] - b->avg_b[i]) / 255.0;
}
sim /= (1024.0 * 3.0);
return 1.0 - sim;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
bool FindDuplicateImages::DeleteDir(TQString dirname)
{
if ( !dirname.isEmpty() )
{
TQDir dir;
if (dir.exists ( dirname ) == true)
{
if (deldir(dirname) == false)
return false;
if (dir.rmdir( dirname ) == false )
return false;
}
else
return false;
}
else
return false;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
bool FindDuplicateImages::deldir(TQString dirname)
{
TQDir *dir = new TQDir(dirname);
dir->setFilter ( TQDir::Dirs | TQDir::Files | TQDir::NoSymLinks );
const TQFileInfoList* fileinfolist = dir->entryInfoList();
TQFileInfoListIterator it(*fileinfolist);
TQFileInfo* fi;
while( (fi = it.current() ) )
{
if(fi->fileName() == "." || fi->fileName() == ".." )
{
++it;
continue;
}
if( fi->isDir() )
{
if (deldir( fi->absFilePath() ) == false)
return false;
if (dir->rmdir( fi->absFilePath() ) == false)
return false;
}
else
if( fi->isFile() )
if (dir->remove(fi->absFilePath() ) == false)
return false;
kapp->processEvents();
++it;
}
return true;
}
} // NameSpace KIPIFindDupplicateImagesPlugin
void KIPIFindDupplicateImagesPlugin::FindDuplicateImages::stopPlease()
{
if ( m_compareOp )
m_compareOp->stopPlease();
}
#include "finddupplicateimages.moc"