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.
437 lines
12 KiB
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"
|