From 6f9546e52c2af2ea1cd271862d4a68dab1768db0 Mon Sep 17 00:00:00 2001 From: Frederic Cervenansky Date: Wed, 20 Oct 2010 16:14:34 +0000 Subject: [PATCH] Qt improvement --- appli/TestQtGimmick/main.cxx | 2 +- src/CMakeLists.txt | 11 +- src/creaImageIOGimmickView.h | 7 + src/creaImageIOQMultiThreadImageReader.cpp | 645 +++++++++++++++++++++ src/creaImageIOQMultiThreadImageReader.h | 259 +++++++++ 5 files changed, 919 insertions(+), 5 deletions(-) create mode 100644 src/creaImageIOQMultiThreadImageReader.cpp create mode 100644 src/creaImageIOQMultiThreadImageReader.h diff --git a/appli/TestQtGimmick/main.cxx b/appli/TestQtGimmick/main.cxx index 74c0b23..e8809f6 100644 --- a/appli/TestQtGimmick/main.cxx +++ b/appli/TestQtGimmick/main.cxx @@ -1,5 +1,5 @@ #include -#include +#include "creaImageIOQTGimmickReaderDialog.h" //#include "vtkMetaImageReader.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ae8fc6..05d6b09 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,8 +53,8 @@ FILE(GLOB SOURCES_CREAIMAGEIO_IMG_READER creaImageIOImageReader.cpp creaImageIOUltrasonixImageReader.cpp creaImageIOVtkImageReader.cpp - creaImageIOMultiThreadImageReader.cpp - ${SOURCES_CREAIMAGEIO_IMG_DICOM_READER} + creaImageIOQMultiThreadImageReader.cpp + ${SOURCES_CREAIMAGEIO_IMG_DICOM_READER} ) # The wxWidgets-based components @@ -81,6 +81,7 @@ FILE(GLOB SOURCES_CREAIMAGEIO_WX creaImageIOExternalGimmick.cpp BlockScopeWxApp.cpp creaImageIOListener.cpp + creaImageIOMultiThreadImageReader.cpp ) SOURCE_GROUP("Source Files\\GUI" FILES ${SOURCES_CREAIMAGEIO_WX}) endif() @@ -89,7 +90,8 @@ endif() IF (USE_QT4) #cpp FILE(GLOB QT_CPP - QtGUI/*.cpp QtGUI/*.cxx + QtGUI/*.cpp + QtGUI/*.cxx ) # headers @@ -123,7 +125,8 @@ ENDIF(USE_XERCES) # Header Files -FILE(GLOB HEADER_CREAIMAGEIO creaImageIOImagePointerHolder.h CppSQLite3.h) +FILE(GLOB HEADER_CREAIMAGEIO #creaImageIOImagePointerHolder.h CppSQLite3.h) +#*.h) FILE(GLOB SOURCES_CREAIMAGEIO_PACS PACS/*.cpp) FILE(GLOB HEADER_CREAIMAGEIO_PACS PACS/*.h) diff --git a/src/creaImageIOGimmickView.h b/src/creaImageIOGimmickView.h index fed6e17..d41ea1a 100644 --- a/src/creaImageIOGimmickView.h +++ b/src/creaImageIOGimmickView.h @@ -8,7 +8,14 @@ //#include #include +#if defined (USE_WXWIDGETS) #include +#endif + +#if defined(USE_QT4) +#include +#endif + // Signal/slot mechanism for progress events #include diff --git a/src/creaImageIOQMultiThreadImageReader.cpp b/src/creaImageIOQMultiThreadImageReader.cpp new file mode 100644 index 0000000..c0a9f4d --- /dev/null +++ b/src/creaImageIOQMultiThreadImageReader.cpp @@ -0,0 +1,645 @@ +#include +#include +//#include +#include +#include + +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + + //===================================================================== + void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent + ( const std::string& filename, + EventType type, + vtkImageData* image) + { + QMutexLocker lock(&mMultiThreadImageReaderUserMutex); + + this->OnMultiThreadImageReaderEvent(filename,type,image); + } + //===================================================================== + + //===================================================================== + class ThreadedImageReader: public QThread + { + public: + ThreadedImageReader(MultiThreadImageReader* tir) : + mMultiThreadImageReader(tir) + {} + + void* Entry(); + void OnExit(); + + vtkImageData* Read(const std::string& filename); + + struct deleter + { + void operator()(ThreadedImageReader* p) + { + delete p; + } + }; + friend struct deleter; + + + private: + ImageReader mReader; + MultiThreadImageReader* mMultiThreadImageReader; + + }; + + //===================================================================== + + + //===================================================================== + MultiThreadImageReader::MultiThreadImageReader(int number_of_threads) + : //mDoNotSignal(false), + mReader(0), + mTotalMem(0), + mTotalMemMax(1000000) + { + // std::cout << "#### MultiThreadImageReader::MultiThreadImageReader(" + // << " #threads= " << number_of_threads <<" )"< t(new ThreadedImageReader(this), ThreadedImageReader::deleter()); + mThreadedImageReaderList.push_back(t); + std::cout << " ===> Thread "< 0) return true; + + ThreadedImageReaderListType::iterator i; + for (i =mThreadedImageReaderList.begin(); + i!=mThreadedImageReaderList.end(); + i++) + { + (*i)->start(); + if ( !(*i)->isRunning() ) + { + std::cout << "ERROR starting a thread"<< std::endl; + return false; + } + else + { + std::cout << " ===> Thread "<<(*i)->currentThreadId() + <<" successfully created"<< std::endl; + + } + } + QMutexLocker locker(GetMultiThreadImageReaderUserMutex()); + // std::cout << "EO Start : #Threads running = " + // << mNumberOfThreadedReadersRunning< Thread "<<(*i)->currentThreadId() + <<" successfully stopped"<< std::endl; + if(!(*i)->isFinished()) + {(*i)->wait(); + (*i).reset(); + // (*i)->Delete(); + } + } + mThreadedImageReaderList.clear(); + // Wait a little to be sure that all threads have stopped + // A better way to do this ? + // wxMilliSleep(1000); + // New method : the threads generate a stop event when they have finished + // We wait until all threads have stopped +// std::cout << "Waiting for stop signals..."<first; + } + mImages.clear(); + mDone = true; + } + //===================================================================== + + //===================================================================== + MultiThreadImageReader::~MultiThreadImageReader() + { + // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()" + // <UnloadIndex()<0) return; + int old_prio = p->GetPriority(); + if (priority > old_prio) + { + p->SetPriority(priority); + mUnloadQueue.downsort(p->UnloadIndex()); + } + else if ( old_prio > priority ) + { + p->SetPriority(priority); + mUnloadQueue.upsort(p->UnloadIndex()); + } + } + //===================================================================== + // function to read attributes for a file + void MultiThreadImageReader::getAttributes(const std::string filename, + std::map &infos,std::vector i_attr) + { + mReader->getAttributes(filename, infos, i_attr); + } + + //===================================================================== + void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user, + const std::string& filename, + int priority ) + { + QMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex); + + if (mNumberOfThreadedReadersRunning==0) +// if (mThreadedImageReaderList.size()==0) + { + // no detached reader : use self reader + ImageToLoad itl(user,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + ImageToLoadPtr pitl = const_cast(i->first); + // Already inserted + if (pitl->GetImage() != 0) + { + // Already read + pitl->SetUser(user); + UpdateUnloadPriority(pitl,priority); + SignalImageRead(pitl,false); + return; // pitl->GetImage(); + } + } + ImageToLoadPtr pitl = new ImageToLoad(user,filename,0); + mImages[pitl] = 0; + pitl->SetImage(mReader->ReadImage(filename)); + UpdateUnloadPriority(pitl,priority); + SignalImageRead(pitl,true); + // return pitl->GetImage(); + return; + } + + ImageToLoad itl(user,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + // Already inserted + if (i->first->GetImage() != 0) + { + // Already read : ok :signal the user + UpdateUnloadPriority(i->first,priority); + SignalImageRead(i->first,false); + return; + } + /// Already requested : change the priority + ImageToLoadPtr pitl = const_cast(i->first); + pitl->SetPriority(priority); + // Already in queue + if (pitl->Index()>=0) + { + // Re-sort the queue + mQueue.upsort(pitl->Index()); + } + // Not read but not in queue = being read = ok + else + { + + } + } + else + { + // Never requested before or unloaded + ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority); + mImages[pitl] = 0; + mQueue.insert(pitl); + } + } + //===================================================================== + + //===================================================================== + void MultiThreadImageReader::OnMultiThreadImageReaderEvent + (const std::string& filename, + MultiThreadImageReaderUser::EventType e, + vtkImageData* image) + { + if ((e==MultiThreadImageReaderUser::ImageLoaded) && + (filename == mRequestedFilename)) + { + mRequestedImage = image; + } + else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted) + { + mNumberOfThreadedReadersRunning++; + // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl; + } + else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped) + { + + mNumberOfThreadedReadersRunning--; + // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl; + } + } + //===================================================================== + + //===================================================================== + vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename) + { + // Start(); + // std::cout << "** MultiThreadImageReader::GetImage('"<(i->first); + // Already inserted + if (pitl->GetImage() != 0) + { + // Already read + UpdateUnloadPriority(pitl, + GetMaximalPriorityWithoutLocking()+1); + return pitl->GetImage(); + } + } + ImageToLoadPtr pitl = new ImageToLoad(this,filename,0); + mImages[pitl] = 0; + pitl->SetImage(mReader->ReadImage(filename)); + UpdateUnloadPriority(pitl, + GetMaximalPriorityWithoutLocking()+1); + return pitl->GetImage(); + } + + /* + mRequestedFilename = filename; + mRequestedImage = 0; + ImageToLoad itl(this,filename); + ImageMapType::iterator i = mImages.find(&itl); + if (i!=mImages.end()) + { + // Already inserted in queue + if (i->first->GetImage() != 0) + { + // Already read : ok : return it + return i->first->GetImage(); + } + /// Already requested : change the priority + ImageToLoadPtr pitl = const_cast(i->first); + pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 ); + pitl->SetUser( this ); + // Already in queue + if (pitl->Index()>=0) + { + // Re-sort the queue + mQueue.upsort(pitl->Index()); + } + // Not read but not in queue = being read = ok + else + { + pitl->SetUser( this ); + } + } + else + { + + // Never requested before or unloaded + ImageToLoadPtr pitl = + new ImageToLoad(this,filename, + GetMaximalPriorityWithoutLocking() + 1); + mImages[pitl] = 0; + mQueue.insert(pitl); + } + */ + } + while (0); + + // std::cout << "Waiting..."<GetFilename()<<"' read"<GetImage()->UpdateInformation(); + p->GetImage()->PropagateUpdateExtent(); + long ImMem = p->GetImage()->GetEstimatedMemorySize(); + mTotalMem += ImMem; + + GimmickMessage(5,"==> Image in memory = "< Total mem = "< mTotalMemMax) + { + GimmickMessage(5, + " ! Exceeded max of " + << mTotalMemMax << " Ko : unloading oldest image ... " + << std::endl); + if ( mUnloadQueue.size() <= 1 ) + { + GimmickMessage(5, + " Only one image : cannot load AND unload it !!" + <GetUser(); + + /* + if ((user!=0)&&(user!=this)) + { + user->GetMultiThreadImageReaderUserMutex().Lock(); + } + */ + + std::string filename = unload->GetFilename(); + + GimmickMessage(5,"'" << filename << "'" << std::endl); + mTotalMem -= unload->GetImage()->GetEstimatedMemorySize(); + + GimmickMessage(5," ==> Total mem = "<GetMultiThreadImageReaderUserMutex().Unlock(); + // std::cout << "event"<MultiThreadImageReaderSendEvent + (filename, + MultiThreadImageReaderUser::ImageUnloaded, + 0); + // std::cout << "event ok"<Index()>=0) + { + // GimmickMessage(5,"still in queue"<Index() = -1; + + + ImageMapType::iterator it = mImages.find(unload); + if (it!=mImages.end()) + { + mImages.erase(it); + } + // std::cout << "delete..."<0) + { + max = mQueue.top()->GetPriority(); + } + if (mUnloadQueue.size()>0) + { + int max2 = mUnloadQueue.top()->GetPriority(); + if (max2>max) max=max2; + } + return max; + } + //===================================================================== + + + //===================================================================== + //===================================================================== + //===================================================================== + //===================================================================== + + //===================================================================== + void* ThreadedImageReader::Entry() + { + // std::cout << "### Thread "<MultiThreadImageReaderSendEvent + ("", + MultiThreadImageReaderUser::ThreadedReaderStarted, + 0); + + // While was not deleted + while (!isFinished()) + { + //std::cout << "### Thread "<MultiThreadImageReaderEventLock(); + //mMutex.Lock(); + // If image in queue + if (mMultiThreadImageReader->mQueue.size()>0) + { + MultiThreadImageReader::ImageToLoadPtr i = + mMultiThreadImageReader->mQueue.remove_top(); + + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); + //mMutex.Unlock(); + + + // std::cout << "### Thread "<GetFilename() << "'" << std::endl; + + // Do the job + vtkImageData* im = Read(i->GetFilename()); + + // Store it in the map + mMultiThreadImageReader->MultiThreadImageReaderEventLock(); + //mMutex.Lock(); + MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename()); + MultiThreadImageReader::ImageMapType::iterator it = + mMultiThreadImageReader->mImages.find(&itl); + MultiThreadImageReader::ImageToLoadPtr + pitl = const_cast + (it->first); + pitl->SetImage(im); + mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename()); + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock(); + + // std::cout << "### Thread "<GetFilename() << "' : DONE" << std::endl; + + } + else + { + mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); + //mMutex.Unlock(); + // Wait a little to avoid blocking + Sleep(10); + } + }; + // std::cout << "### Thread "<MultiThreadImageReaderSendEvent + ("", + MultiThreadImageReaderUser::ThreadedReaderStopped, + 0); + } + //===================================================================== + + //===================================================================== + vtkImageData* ThreadedImageReader::Read(const std::string& filename) + { + return mReader.ReadImage(filename); + } + //===================================================================== + +} // namespace creaImageIO diff --git a/src/creaImageIOQMultiThreadImageReader.h b/src/creaImageIOQMultiThreadImageReader.h new file mode 100644 index 0000000..bd84033 --- /dev/null +++ b/src/creaImageIOQMultiThreadImageReader.h @@ -0,0 +1,259 @@ +#ifndef __creaImageIOQMultiThreadImageReader_h_INCLUDED__ +#define __creaImageIOQMultiThreadImageReader_h_INCLUDED__ + +#include +#include +#include +#include +#include +#include +#include + + + +namespace creaImageIO +{ + /** + * \ingroup IO + */ + //===================================================================== + class ThreadedImageReader; + class MultiThreadImageReader; + //===================================================================== + + //===================================================================== + class CREAIMAGEIO_EXPORT MultiThreadImageReaderUser + { + public: + friend class ThreadedImageReader; + friend class MultiThreadImageReader; + + MultiThreadImageReaderUser() {} + virtual ~MultiThreadImageReaderUser() {} + + typedef enum + { + ThreadedReaderStarted, + ThreadedReaderStopped, + ImageLoaded, + ImageUnloaded, + Error + } + EventType; + /// The virtual method to overload by MultiThreadImageReader users + /// It is called when an image has been loaded or unloaded + /// Provides : + /// * The image file name which was requested + /// * The type of event + /// * If type==ImageLoaded the image pointer, else NULL pointer + virtual void OnMultiThreadImageReaderEvent( const std::string& filename, + EventType type, + vtkImageData* image) + {} + inline void MultiThreadImageReaderEventLock() + { mMultiThreadImageReaderUserMutex.lock(); } + inline void MultiThreadImageReaderEventUnlock() + { mMultiThreadImageReaderUserMutex.unlock(); } + inline QMutex* GetMultiThreadImageReaderUserMutex() + { return &mMultiThreadImageReaderUserMutex; } + private: + /// + void MultiThreadImageReaderSendEvent( const std::string& filename, + EventType type, + vtkImageData* image); + QMutex mMultiThreadImageReaderUserMutex; + }; + //===================================================================== + + //===================================================================== + /// + /// TAKE CARE : For the moment it only supports a **SINGLE USER** + + ///Class that allows parallel lectures of several images + class MultiThreadImageReader : public MultiThreadImageReaderUser + { + public: + friend class ThreadedImageReader; + + /// Ctor with the number of threads to use + MultiThreadImageReader(int number_of_threads = 1); + /// Dtor + ~MultiThreadImageReader(); + + /// Starts the reader = create the threads which start to check + /// periodically the queue of requested images to read + bool Start(); + /// Stops the reader = stops the threads and delete the images loaded + void Stop(); + + /// Request the image "filename" with a given priority + /// When the image is ready (or an error occurred) + /// The observer's callback is invoked + void Request( MultiThreadImageReaderUser* user, + const std::string& filename, + int priority ); + + /// Request the image "filename" immediately + /// Blocks until image loaded + /// (no user callback but image returned) + vtkImageData* GetImage(const std::string& filename); + + /// + int GetMaximalPriority(); + + /// + void OnMultiThreadImageReaderEvent( const std::string& filename, + EventType type, + vtkImageData* image); + + /// Function to read attributes for a file + void getAttributes(const std::string filename, std::map &infos, std::vector i_attr); + + protected: + bool mDone; + int GetMaximalPriorityWithoutLocking(); + ///Class that represents an image to be loaded + class ImageToLoad + { + public: + ImageToLoad( MultiThreadImageReaderUser* user, + const std::string& filename, + int prio=0) + : mUser(user), + mFilename(filename), + mPriority(prio), + mIndex(-1), + mUnloadIndex(-1), + mImage(0) + {} + ~ImageToLoad() + { + if (mImage>0) + { + // std::cout << "Refs = "<GetReferenceCount()<Delete(); + } + } + MultiThreadImageReaderUser* GetUser() const { return mUser; } + void SetUser( MultiThreadImageReaderUser* u ) { mUser = u; } + const std::string& GetFilename() const { return mFilename; } + int GetPriority() const { return mPriority; } + void SetPriority(int p) { mPriority=p; } + int& Index() { return mIndex; } + int& UnloadIndex() { return mUnloadIndex; } + vtkImageData* GetImage() const { return mImage; } + void SetImage( vtkImageData* i ) { mImage=i; } + + std::map getAttributes(const std::vector i_attr); + private: + MultiThreadImageReaderUser* mUser; + std::string mFilename; + int mPriority; + int mIndex; + int mUnloadIndex; + vtkImageData* mImage; + }; + // + + /// Type of pointer on an ImageToLoad struct + typedef ImageToLoad* ImageToLoadPtr; + + /// ImageToLoadPtr comparator on priority (for image queue) + struct ImageToLoadPtrPriorityComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetPriority() > b->GetPriority() ); + } + }; + /// ImageToLoadPtr comparator on inverse priority (for image to unload queue) + struct ImageToLoadPtrInversePriorityComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetPriority() < b->GetPriority() ); + } + }; + + + /// ImageToLoadPtr comparator on filename (for image map) + struct ImageToLoadPtrFilenameComparator + { + bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b) + const + { + return ( a->GetFilename() < b->GetFilename() ); + } + }; + + /// ImageToLoadPtr indexer for image queue + struct ImageToLoadPtrIndexer + { + int& operator()(ImageToLoadPtr & t) const { return t->Index(); } + }; + /// ImageToLoadPtr indexer for to unload image queue + struct ImageToUnloadPtrIndexer + { + int& operator()(ImageToLoadPtr & t) const { return t->UnloadIndex(); } + }; + + /// The callback from threaded readers when an image is read + void SignalImageRead(ImageToLoadPtr p, bool purge); + + /// The type of map of images + typedef std::map ImageMapType; + /// The map of images + ImageMapType mImages; + /// Comparator for the image to load queue + ImageToLoadPtrPriorityComparator mComparator; + /// Indexer for the image to load queue + ImageToLoadPtrIndexer mIndexer; + /// The image to load priority queue + IndexedHeap mQueue; + + /// The type of list of threaded readers + typedef std::vector > ThreadedImageReaderListType; + //typedef std::vector ThreadedImageReaderListType; + ThreadedImageReaderListType mThreadedImageReaderList; + /// The number of currently running threaded readers + int mNumberOfThreadedReadersRunning; + /// The mutex used to access safely internal data from any thread + /// LG : Removed ! We now use the embedded mutex in User from which + /// we inherit... + // wxMutex mMutex; + + /// For GetImage : the filename requested + std::string mRequestedFilename; + /// For GetImage : the image requested + vtkImageData* mRequestedImage; + + /// If number of threads == 0 then uses an internal non-threaded reader + ImageReader* mReader; + + /// The type of list of images loaded + /// used to unload oldest image when memory limit exceeded + /// The image to unload priority queue + IndexedHeap mUnloadQueue; + + void UpdateUnloadPriority(ImageToLoadPtr p, int priority); + long mTotalMem; + long mTotalMemMax; + + + }; // class MultiThreadImageReader + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOQMultiThreadImageReader_h_INCLUDED__ -- 2.47.1