From: Frederic Cervenansky Date: Tue, 16 Mar 2010 15:46:13 +0000 (+0000) Subject: move directory X-Git-Tag: CREATOOLS.2-0-3~47 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?p=creaImageIO.git;a=commitdiff_plain;h=3a22e19184c369b130d4caa992a8e98e50c7a0ee move directory --- diff --git a/src/AdditionalUsecreaImageIO2.cmake.in b/src/AdditionalUsecreaImageIO2.cmake.in new file mode 100644 index 0000000..bdf20d7 --- /dev/null +++ b/src/AdditionalUsecreaImageIO2.cmake.in @@ -0,0 +1,2 @@ +INCLUDE(${crea_USE_FILE}) + diff --git a/src/AdditionalcreaImageIO2Config.cmake.in b/src/AdditionalcreaImageIO2Config.cmake.in new file mode 100644 index 0000000..35ae10d --- /dev/null +++ b/src/AdditionalcreaImageIO2Config.cmake.in @@ -0,0 +1,2 @@ +# We have to find crea +FIND_PACKAGE(crea REQUIRED) \ No newline at end of file diff --git a/src/BlockScopeWxApp.cpp b/src/BlockScopeWxApp.cpp new file mode 100644 index 0000000..c8c361c --- /dev/null +++ b/src/BlockScopeWxApp.cpp @@ -0,0 +1,45 @@ +#include +#include + + +class DummyWxApp : public wxApp +{ +public: + bool OnInit( ); + int OnExit() { return true; } +}; + +IMPLEMENT_APP_NO_MAIN(DummyWxApp); + + +bool DummyWxApp::OnInit( ) +{ +// std::cout << "OnInit()"< + + + + + +// Named constant for passing to CppSQLite3Exception when passing it a string + +// that cannot be deleted. + +static const bool DONT_DELETE_MSG=false; + + + +//////////////////////////////////////////////////////////////////////////////// + +// Prototypes for SQLite functions not included in SQLite DLL, but copied below + +// from SQLite encode.c + +//////////////////////////////////////////////////////////////////////////////// + +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out); + +int sqlite3_decode_binary(const unsigned char *in, unsigned char *out); + + + +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + + const char* szErrMess, + + bool bDeleteMsg/*=true*/) : + + mnErrCode(nErrCode) + +{ + + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + + errorCodeAsString(nErrCode), + + nErrCode, + + szErrMess ? szErrMess : ""); + + /* + + if (bDeleteMsg && szErrMess) + + { + + sqlite3_free(szErrMess); + + } + + */ + +} + + + +CppSQLite3Exception::CppSQLite3Exception(const int nErrCode, + + char* szErrMess, + + bool bDeleteMsg/*=true*/) : + + mnErrCode(nErrCode) + +{ + + mpszErrMess = sqlite3_mprintf("%s[%d]: %s", + + errorCodeAsString(nErrCode), + + nErrCode, + + szErrMess ? szErrMess : ""); + + + + if (bDeleteMsg && szErrMess) + + { + + sqlite3_free(szErrMess); + + } + +} + + + +CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) : + + mnErrCode(e.mnErrCode) + +{ + + mpszErrMess = 0; + + if (e.mpszErrMess) + + { + + mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess); + + } + +} + + + + + +const char* CppSQLite3Exception::errorCodeAsString(int nErrCode) + +{ + + switch (nErrCode) + + { + + case SQLITE_OK : return "SQLITE_OK"; + + case SQLITE_ERROR : return "SQLITE_ERROR"; + + case SQLITE_INTERNAL : return "SQLITE_INTERNAL"; + + case SQLITE_PERM : return "SQLITE_PERM"; + + case SQLITE_ABORT : return "SQLITE_ABORT"; + + case SQLITE_BUSY : return "SQLITE_BUSY"; + + case SQLITE_LOCKED : return "SQLITE_LOCKED"; + + case SQLITE_NOMEM : return "SQLITE_NOMEM"; + + case SQLITE_READONLY : return "SQLITE_READONLY"; + + case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT"; + + case SQLITE_IOERR : return "SQLITE_IOERR"; + + case SQLITE_CORRUPT : return "SQLITE_CORRUPT"; + + case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND"; + + case SQLITE_FULL : return "SQLITE_FULL"; + + case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN"; + + case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL"; + + case SQLITE_EMPTY : return "SQLITE_EMPTY"; + + case SQLITE_SCHEMA : return "SQLITE_SCHEMA"; + + case SQLITE_TOOBIG : return "SQLITE_TOOBIG"; + + case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT"; + + case SQLITE_MISMATCH : return "SQLITE_MISMATCH"; + + case SQLITE_MISUSE : return "SQLITE_MISUSE"; + + case SQLITE_NOLFS : return "SQLITE_NOLFS"; + + case SQLITE_AUTH : return "SQLITE_AUTH"; + + case SQLITE_FORMAT : return "SQLITE_FORMAT"; + + case SQLITE_RANGE : return "SQLITE_RANGE"; + + case SQLITE_ROW : return "SQLITE_ROW"; + + case SQLITE_DONE : return "SQLITE_DONE"; + + case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR"; + + default: return "UNKNOWN_ERROR"; + + } + +} + + + + + +CppSQLite3Exception::~CppSQLite3Exception() + +{ + + if (mpszErrMess) + + { + + sqlite3_free(mpszErrMess); + + mpszErrMess = 0; + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Buffer::CppSQLite3Buffer() + +{ + + mpBuf = 0; + +} + + + + + +CppSQLite3Buffer::~CppSQLite3Buffer() + +{ + + clear(); + +} + + + + + +void CppSQLite3Buffer::clear() + +{ + + if (mpBuf) + + { + + sqlite3_free(mpBuf); + + mpBuf = 0; + + } + + + +} + + + + + +const char* CppSQLite3Buffer::format(const char* szFormat, ...) + +{ + + clear(); + + va_list va; + + va_start(va, szFormat); + + mpBuf = sqlite3_vmprintf(szFormat, va); + + va_end(va); + + return mpBuf; + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Binary::CppSQLite3Binary() : + + mpBuf(0), + + mnBinaryLen(0), + + mnBufferLen(0), + + mnEncodedLen(0), + + mbEncoded(false) + +{ + +} + + + + + +CppSQLite3Binary::~CppSQLite3Binary() + +{ + + clear(); + +} + + + + + +void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen) + +{ + + mpBuf = allocBuffer(nLen); + + memcpy(mpBuf, pBuf, nLen); + +} + + + + + +void CppSQLite3Binary::setEncoded(const unsigned char* pBuf) + +{ + + clear(); + + + + mnEncodedLen = strlen((const char*)pBuf); + + mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator + + + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + + + if (!mpBuf) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot allocate memory", + + DONT_DELETE_MSG); + + } + + + + memcpy(mpBuf, pBuf, mnBufferLen); + + mbEncoded = true; + +} + + + + + +const unsigned char* CppSQLite3Binary::getEncoded() + +{ + + if (!mbEncoded) + + { + + unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen); + + memcpy(ptmp, mpBuf, mnBinaryLen); + + mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf); + + free(ptmp); + + mbEncoded = true; + + } + + + + return mpBuf; + +} + + + + + +const unsigned char* CppSQLite3Binary::getBinary() + +{ + + if (mbEncoded) + + { + + // in/out buffers can be the same + + mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf); + + + + if (mnBinaryLen == -1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot decode binary", + + DONT_DELETE_MSG); + + } + + + + mbEncoded = false; + + } + + + + return mpBuf; + +} + + + + + +int CppSQLite3Binary::getBinaryLength() + +{ + + getBinary(); + + return mnBinaryLen; + +} + + + + + +unsigned char* CppSQLite3Binary::allocBuffer(int nLen) + +{ + + clear(); + + + + // Allow extra space for encoded binary as per comments in + + // SQLite encode.c See bottom of this file for implementation + + // of SQLite functions use 3 instead of 2 just to be sure ;-) + + mnBinaryLen = nLen; + + mnBufferLen = 3 + (257*nLen)/254; + + + + mpBuf = (unsigned char*)malloc(mnBufferLen); + + + + if (!mpBuf) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Cannot allocate memory", + + DONT_DELETE_MSG); + + } + + + + mbEncoded = false; + + + + return mpBuf; + +} + + + + + +void CppSQLite3Binary::clear() + +{ + + if (mpBuf) + + { + + mnBinaryLen = 0; + + mnBufferLen = 0; + + free(mpBuf); + + mpBuf = 0; + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Query::CppSQLite3Query() + +{ + + mpVM = 0; + + mbEof = true; + + mnCols = 0; + + mbOwnVM = false; + +} + + + + + +CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery) + +{ + + mpVM = rQuery.mpVM; + + // Only one object can own the VM + + const_cast(rQuery).mpVM = 0; + + mbEof = rQuery.mbEof; + + mnCols = rQuery.mnCols; + + mbOwnVM = rQuery.mbOwnVM; + +} + + + + + +CppSQLite3Query::CppSQLite3Query(sqlite3* pDB, + + sqlite3_stmt* pVM, + + bool bEof, + + bool bOwnVM/*=true*/) + +{ + + mpDB = pDB; + + mpVM = pVM; + + mbEof = bEof; + + mnCols = sqlite3_column_count(mpVM); + + mbOwnVM = bOwnVM; + +} + + + + + +CppSQLite3Query::~CppSQLite3Query() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery) + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + + mpVM = rQuery.mpVM; + + // Only one object can own the VM + + const_cast(rQuery).mpVM = 0; + + mbEof = rQuery.mbEof; + + mnCols = rQuery.mnCols; + + mbOwnVM = rQuery.mbOwnVM; + + return *this; + +} + + + + + +int CppSQLite3Query::numFields() + +{ + + checkVM(); + + return mnCols; + +} + + + + + +const char* CppSQLite3Query::fieldValue(int nField) + +{ + + checkVM(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return (const char*)sqlite3_column_text(mpVM, nField); + +} + + + + + +const char* CppSQLite3Query::fieldValue(const char* szField) + +{ + + int nField = fieldIndex(szField); + + return (const char*)sqlite3_column_text(mpVM, nField); + +} + + + + + +int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return nNullValue; + + } + + else + + { + + return sqlite3_column_int(mpVM, nField); + + } + +} + + + + + +int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/) + +{ + + int nField = fieldIndex(szField); + + return getIntField(nField, nNullValue); + +} + + + + + +double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return fNullValue; + + } + + else + + { + + return sqlite3_column_double(mpVM, nField); + + } + +} + + + + + +double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/) + +{ + + int nField = fieldIndex(szField); + + return getFloatField(nField, fNullValue); + +} + + + + + +const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/) + +{ + + if (fieldDataType(nField) == SQLITE_NULL) + + { + + return szNullValue; + + } + + else + + { + + return (const char*)sqlite3_column_text(mpVM, nField); + + } + +} + + + + + +const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/) + +{ + + int nField = fieldIndex(szField); + + return getStringField(nField, szNullValue); + +} + + + + + +const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen) + +{ + + checkVM(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + nLen = sqlite3_column_bytes(mpVM, nField); + + return (const unsigned char*)sqlite3_column_blob(mpVM, nField); + +} + + + + + +const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen) + +{ + + int nField = fieldIndex(szField); + + return getBlobField(nField, nLen); + +} + + + + + +bool CppSQLite3Query::fieldIsNull(int nField) + +{ + + return (fieldDataType(nField) == SQLITE_NULL); + +} + + + + + +bool CppSQLite3Query::fieldIsNull(const char* szField) + +{ + + int nField = fieldIndex(szField); + + return (fieldDataType(nField) == SQLITE_NULL); + +} + + + + + +int CppSQLite3Query::fieldIndex(const char* szField) + +{ + + checkVM(); + + + + if (szField) + + { + + for (int nField = 0; nField < mnCols; nField++) + + { + + const char* szTemp = sqlite3_column_name(mpVM, nField); + + + + if (strcmp(szField, szTemp) == 0) + + { + + return nField; + + } + + } + + } + + + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field name requested", + + DONT_DELETE_MSG); + +} + + + + + +const char* CppSQLite3Query::fieldName(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_name(mpVM, nCol); + +} + + + + + +const char* CppSQLite3Query::fieldDeclType(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_decltype(mpVM, nCol); + +} + + + + + +int CppSQLite3Query::fieldDataType(int nCol) + +{ + + checkVM(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return sqlite3_column_type(mpVM, nCol); + +} + + + + + +bool CppSQLite3Query::eof() + +{ + + checkVM(); + + return mbEof; + +} + + + + + +void CppSQLite3Query::nextRow() + +{ + + checkVM(); + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + mbEof = true; + + } + + else if (nRet == SQLITE_ROW) + + { + + // more rows, nothing to do + + } + + else + + { + + nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, + + (char*)szError, + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Query::finalize() + +{ + + if (mpVM && mbOwnVM) + + { + + int nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Query::checkVM() + +{ + + if (mpVM == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Virtual Machine pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Table::CppSQLite3Table() + +{ + + mpaszResults = 0; + + mnRows = 0; + + mnCols = 0; + + mnCurrentRow = 0; + +} + + + + + +CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable) + +{ + + mpaszResults = rTable.mpaszResults; + + // Only one object can own the results + + const_cast(rTable).mpaszResults = 0; + + mnRows = rTable.mnRows; + + mnCols = rTable.mnCols; + + mnCurrentRow = rTable.mnCurrentRow; + +} + + + + + +CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols) + +{ + + mpaszResults = paszResults; + + mnRows = nRows; + + mnCols = nCols; + + mnCurrentRow = 0; + +} + + + + + +CppSQLite3Table::~CppSQLite3Table() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable) + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + + mpaszResults = rTable.mpaszResults; + + // Only one object can own the results + + const_cast(rTable).mpaszResults = 0; + + mnRows = rTable.mnRows; + + mnCols = rTable.mnCols; + + mnCurrentRow = rTable.mnCurrentRow; + + return *this; + +} + + + + + +void CppSQLite3Table::finalize() + +{ + + if (mpaszResults) + + { + + sqlite3_free_table(mpaszResults); + + mpaszResults = 0; + + } + +} + + + + + +int CppSQLite3Table::numFields() + +{ + + checkResults(); + + return mnCols; + +} + + + + + +int CppSQLite3Table::numRows() + +{ + + checkResults(); + + return mnRows; + +} + + + + + +const char* CppSQLite3Table::fieldValue(int nField) + +{ + + checkResults(); + + + + if (nField < 0 || nField > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + + return mpaszResults[nIndex]; + +} + + + + + +const char* CppSQLite3Table::fieldValue(const char* szField) + +{ + + checkResults(); + + + + if (szField) + + { + + for (int nField = 0; nField < mnCols; nField++) + + { + + if (strcmp(szField, mpaszResults[nField]) == 0) + + { + + int nIndex = (mnCurrentRow*mnCols) + mnCols + nField; + + return mpaszResults[nIndex]; + + } + + } + + } + + + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field name requested", + + DONT_DELETE_MSG); + +} + + + + + +int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/) + +{ + + if (fieldIsNull(nField)) + + { + + return nNullValue; + + } + + else + + { + + return atoi(fieldValue(nField)); + + } + +} + + + + + +int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/) + +{ + + if (fieldIsNull(szField)) + + { + + return nNullValue; + + } + + else + + { + + return atoi(fieldValue(szField)); + + } + +} + + + + + +double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/) + +{ + + if (fieldIsNull(nField)) + + { + + return fNullValue; + + } + + else + + { + + return atof(fieldValue(nField)); + + } + +} + + + + + +double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/) + +{ + + if (fieldIsNull(szField)) + + { + + return fNullValue; + + } + + else + + { + + return atof(fieldValue(szField)); + + } + +} + + + + + +const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/) + +{ + + if (fieldIsNull(nField)) + + { + + return szNullValue; + + } + + else + + { + + return fieldValue(nField); + + } + +} + + + + + +const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/) + +{ + + if (fieldIsNull(szField)) + + { + + return szNullValue; + + } + + else + + { + + return fieldValue(szField); + + } + +} + + + + + +bool CppSQLite3Table::fieldIsNull(int nField) + +{ + + checkResults(); + + return (fieldValue(nField) == 0); + +} + + + + + +bool CppSQLite3Table::fieldIsNull(const char* szField) + +{ + + checkResults(); + + return (fieldValue(szField) == 0); + +} + + + + + +const char* CppSQLite3Table::fieldName(int nCol) + +{ + + checkResults(); + + + + if (nCol < 0 || nCol > mnCols-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid field index requested", + + DONT_DELETE_MSG); + + } + + + + return mpaszResults[nCol]; + +} + + + + + +void CppSQLite3Table::setRow(int nRow) + +{ + + checkResults(); + + + + if (nRow < 0 || nRow > mnRows-1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid row index requested", + + DONT_DELETE_MSG); + + } + + + + mnCurrentRow = nRow; + +} + + + + + +void CppSQLite3Table::checkResults() + +{ + + if (mpaszResults == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Results pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3Statement::CppSQLite3Statement() + +{ + + mpDB = 0; + + mpVM = 0; + +} + + + + + +CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement) + +{ + + mpDB = rStatement.mpDB; + + mpVM = rStatement.mpVM; + + // Only one object can own VM + + const_cast(rStatement).mpVM = 0; + +} + + + + + +CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM) + +{ + + mpDB = pDB; + + mpVM = pVM; + +} + + + + + +CppSQLite3Statement::~CppSQLite3Statement() + +{ + + try + + { + + finalize(); + + } + + catch (...) + + { + + } + +} + + + + + +CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement) + +{ + + mpDB = rStatement.mpDB; + + mpVM = rStatement.mpVM; + + // Only one object can own VM + + const_cast(rStatement).mpVM = 0; + + return *this; + +} + + + + + +int CppSQLite3Statement::execDML() + +{ + + checkDB(); + + checkVM(); + + + + const char* szError=0; + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + int nRowsChanged = sqlite3_changes(mpDB); + + + + nRet = sqlite3_reset(mpVM); + + + + if (nRet != SQLITE_OK) + + { + + szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + + + return nRowsChanged; + + } + + else + + { + + nRet = sqlite3_reset(mpVM); + + szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +CppSQLite3Query CppSQLite3Statement::execQuery() + +{ + + checkDB(); + + checkVM(); + + + + int nRet = sqlite3_step(mpVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false); + + } + + else if (nRet == SQLITE_ROW) + + { + + // at least 1 row + + return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false); + + } + + else + + { + + nRet = sqlite3_reset(mpVM); + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const char* szValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding string param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const int nValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_int(mpVM, nParam, nValue); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding int param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const double dValue) + +{ + + checkVM(); + + int nRes = sqlite3_bind_double(mpVM, nParam, dValue); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding double param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen) + +{ + + checkVM(); + + int nRes = sqlite3_bind_blob(mpVM, nParam, + + (const void*)blobValue, nLen, SQLITE_TRANSIENT); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding blob param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::bindNull(int nParam) + +{ + + checkVM(); + + int nRes = sqlite3_bind_null(mpVM, nParam); + + + + if (nRes != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRes, + + "Error binding NULL param", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::reset() + +{ + + if (mpVM) + + { + + int nRet = sqlite3_reset(mpVM); + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Statement::finalize() + +{ + + if (mpVM) + + { + + int nRet = sqlite3_finalize(mpVM); + + mpVM = 0; + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + } + +} + + + + + +void CppSQLite3Statement::checkDB() + +{ + + if (mpDB == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Database not open", + + DONT_DELETE_MSG); + + } + +} + + + + + +void CppSQLite3Statement::checkVM() + +{ + + if (mpVM == 0) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Null Virtual Machine pointer", + + DONT_DELETE_MSG); + + } + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + + + +CppSQLite3DB::CppSQLite3DB() + +{ + + mpDB = 0; + + mnBusyTimeoutMs = 60000; // 60 seconds + +} + + + + + +CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db) + +{ + + mpDB = db.mpDB; + + mnBusyTimeoutMs = 60000; // 60 seconds + +} + + + + + +CppSQLite3DB::~CppSQLite3DB() + +{ + + close(); + +} + + + + + +CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db) + +{ + + mpDB = db.mpDB; + + mnBusyTimeoutMs = 60000; // 60 seconds + + return *this; + +} + + + + + +void CppSQLite3DB::open(const char* szFile) + +{ + + int nRet = sqlite3_open(szFile, &mpDB); + + + + if (nRet != SQLITE_OK) + + { + + const char* szError = sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + + + + setBusyTimeout(mnBusyTimeoutMs); + +} + + + + + +void CppSQLite3DB::close() + +{ + + if (mpDB) + + { + + sqlite3_close(mpDB); + + mpDB = 0; + + } + +} + + + + + +CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL) + +{ + + checkDB(); + + + + sqlite3_stmt* pVM = compile(szSQL); + + return CppSQLite3Statement(mpDB, pVM); + +} + + + + + +bool CppSQLite3DB::tableExists(const char* szTable) + +{ + + char szSQL[128]; + + sprintf(szSQL, + + "select count(*) from sqlite_master where type='table' and name='%s'", + + szTable); + + int nRet = execScalar(szSQL); + + return (nRet > 0); + +} + + + + + +int CppSQLite3DB::execDML(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + + + int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError); + + + + if (nRet == SQLITE_OK) + + { + + return sqlite3_changes(mpDB); + + } + + else + + { + + throw CppSQLite3Exception(nRet, szError); + + } + +} + + + + + +CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL) + +{ + + checkDB(); + + + + sqlite3_stmt* pVM = compile(szSQL); + + + + int nRet = sqlite3_step(pVM); + + + + if (nRet == SQLITE_DONE) + + { + + // no rows + + return CppSQLite3Query(mpDB, pVM, true/*eof*/); + + } + + else if (nRet == SQLITE_ROW) + + { + + // at least 1 row + + return CppSQLite3Query(mpDB, pVM, false/*eof*/); + + } + + else + + { + + nRet = sqlite3_finalize(pVM); + + const char* szError= sqlite3_errmsg(mpDB); + + throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG); + + } + +} + + + + + +int CppSQLite3DB::execScalar(const char* szSQL) + +{ + + CppSQLite3Query q = execQuery(szSQL); + + + + if (q.eof() || q.numFields() < 1) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Invalid scalar query", + + DONT_DELETE_MSG); + + } + + + + return atoi(q.fieldValue(0)); + +} + + + + + +CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + char** paszResults=0; + + int nRet; + + int nRows(0); + + int nCols(0); + + + + nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError); + + + + if (nRet == SQLITE_OK) + + { + + return CppSQLite3Table(paszResults, nRows, nCols); + + } + + else + + { + + throw CppSQLite3Exception(nRet, szError); + + } + +} + + + + + +sqlite_int64 CppSQLite3DB::lastRowId() + +{ + + return sqlite3_last_insert_rowid(mpDB); + +} + + + + + +void CppSQLite3DB::setBusyTimeout(int nMillisecs) + +{ + + mnBusyTimeoutMs = nMillisecs; + + sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs); + +} + + + + + +void CppSQLite3DB::checkDB() + +{ + + if (!mpDB) + + { + + throw CppSQLite3Exception(CPPSQLITE_ERROR, + + "Database not open", + + DONT_DELETE_MSG); + + } + +} + + + + + +sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL) + +{ + + checkDB(); + + + + char* szError=0; + + const char* szTail=0; + + sqlite3_stmt* pVM; + + + + int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail); + + + + if (nRet != SQLITE_OK) + + { + + throw CppSQLite3Exception(nRet, szError); + + } + + + + return pVM; + +} + + + + + +//////////////////////////////////////////////////////////////////////////////// + +// SQLite encode.c reproduced here, containing implementation notes and source + +// for sqlite3_encode_binary() and sqlite3_decode_binary() + +//////////////////////////////////////////////////////////////////////////////// + + + +/* + +** 2002 April 25 + +** + +** The author disclaims copyright to this source code. In place of + +** a legal notice, here is a blessing: + +** + +** May you do good and not evil. + +** May you find forgiveness for yourself and forgive others. + +** May you share freely, never taking more than you give. + +** + +************************************************************************* + +** This file contains helper routines used to translate binary data into + +** a null-terminated string (suitable for use in SQLite) and back again. + +** These are convenience routines for use by people who want to store binary + +** data in an SQLite database. The code in this file is not used by any other + +** part of the SQLite library. + +** + +** $Id: CppSQLite3.cpp,v 1.4 2010/03/16 15:46:13 cervenansky Exp $ + +*/ + + + +/* + +** How This Encoder Works + +** + +** The output is allowed to contain any character except 0x27 (') and + +** 0x00. This is accomplished by using an escape character to encode + +** 0x27 and 0x00 as a two-byte sequence. The escape character is always + +** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The + +** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally, + +** the escape character itself is encoded as the two-character sequence + +** 0x01 0x02. + +** + +** To summarize, the encoder works by using an escape sequences as follows: + +** + +** 0x00 -> 0x01 0x01 + +** 0x01 -> 0x01 0x02 + +** 0x27 -> 0x01 0x03 + +** + +** If that were all the encoder did, it would work, but in certain cases + +** it could double the size of the encoded string. For example, to + +** encode a string of 100 0x27 characters would require 100 instances of + +** the 0x01 0x03 escape sequence resulting in a 200-character output. + +** We would prefer to keep the size of the encoded string smaller than + +** this. + +** + +** To minimize the encoding size, we first add a fixed offset value to each + +** byte in the sequence. The addition is modulo 256. (That is to say, if + +** the sum of the original character value and the offset exceeds 256, then + +** the higher order bits are truncated.) The offset is chosen to minimize + +** the number of characters in the string that need to be escaped. For + +** example, in the case above where the string was composed of 100 0x27 + +** characters, the offset might be 0x01. Each of the 0x27 characters would + +** then be converted into an 0x28 character which would not need to be + +** escaped at all and so the 100 character input string would be converted + +** into just 100 characters of output. Actually 101 characters of output - + +** we have to record the offset used as the first byte in the sequence so + +** that the string can be decoded. Since the offset value is stored as + +** part of the output string and the output string is not allowed to contain + +** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. + +** + +** Here, then, are the encoding steps: + +** + +** (1) Choose an offset value and make it the first character of + +** output. + +** + +** (2) Copy each input character into the output buffer, one by + +** one, adding the offset value as you copy. + +** + +** (3) If the value of an input character plus offset is 0x00, replace + +** that one character by the two-character sequence 0x01 0x01. + +** If the sum is 0x01, replace it with 0x01 0x02. If the sum + +** is 0x27, replace it with 0x01 0x03. + +** + +** (4) Put a 0x00 terminator at the end of the output. + +** + +** Decoding is obvious: + +** + +** (5) Copy encoded characters except the first into the decode + +** buffer. Set the first encoded character aside for use as + +** the offset in step 7 below. + +** + +** (6) Convert each 0x01 0x01 sequence into a single character 0x00. + +** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27. + +** + +** (7) Subtract the offset value that was the first character of + +** the encoded buffer from all characters in the output buffer. + +** + +** The only tricky part is step (1) - how to compute an offset value to + +** minimize the size of the output buffer. This is accomplished by testing + +** all offset values and picking the one that results in the fewest number + +** of escapes. To do that, we first scan the entire input and count the + +** number of occurances of each character value in the input. Suppose + +** the number of 0x00 characters is N(0), the number of occurances of 0x01 + +** is N(1), and so forth up to the number of occurances of 0xff is N(255). + +** An offset of 0 is not allowed so we don't have to test it. The number + +** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number + +** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. + +** In this way we find the offset that gives the minimum number of escapes, + +** and thus minimizes the length of the output string. + +*/ + + + +/* + +** Encode a binary buffer "in" of size n bytes so that it contains + +** no instances of characters '\'' or '\000'. The output is + +** null-terminated and can be used as a string value in an INSERT + +** or UPDATE statement. Use sqlite3_decode_binary() to convert the + +** string back into its original binary. + +** + +** The result is written into a preallocated output buffer "out". + +** "out" must be able to hold at least 2 +(257*n)/254 bytes. + +** In other words, the output will be expanded by as much as 3 + +** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. + +** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) + +** + +** The return value is the number of characters in the encoded + +** string, excluding the "\000" terminator. + +*/ + +int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){ + + int i, j, e, m; + + int cnt[256]; + + if( n<=0 ){ + + out[0] = 'x'; + + out[1] = 0; + + return 1; + + } + + memset(cnt, 0, sizeof(cnt)); + + for(i=n-1; i>=0; i--){ cnt[in[i]]++; } + + m = n; + + for(i=1; i<256; i++){ + + int sum; + + if( i=='\'' ) continue; + + sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; + + if( sum + +#include + + + +#define CPPSQLITE_ERROR 1000 + + + +class CppSQLite3Exception + +{ + +public: + + + + CppSQLite3Exception(const int nErrCode, + + char* szErrMess, + + bool bDeleteMsg=true); + + CppSQLite3Exception(const int nErrCode, + + const char* szErrMess, + + bool bDeleteMsg=true); + + + + CppSQLite3Exception(const CppSQLite3Exception& e); + + + + virtual ~CppSQLite3Exception(); + + + + const int errorCode() { return mnErrCode; } + + + + const char* errorMessage() { return mpszErrMess; } + + + + static const char* errorCodeAsString(int nErrCode); + + + +private: + + + + int mnErrCode; + + char* mpszErrMess; + +}; + + + + + +class CppSQLite3Buffer + +{ + +public: + + + + CppSQLite3Buffer(); + + + + ~CppSQLite3Buffer(); + + + + const char* format(const char* szFormat, ...); + + + + operator const char*() { return mpBuf; } + + + + void clear(); + + + +private: + + + + char* mpBuf; + +}; + + + + + +class CppSQLite3Binary + +{ + +public: + + + + CppSQLite3Binary(); + + + + ~CppSQLite3Binary(); + + + + void setBinary(const unsigned char* pBuf, int nLen); + + void setEncoded(const unsigned char* pBuf); + + + + const unsigned char* getEncoded(); + + const unsigned char* getBinary(); + + + + int getBinaryLength(); + + + + unsigned char* allocBuffer(int nLen); + + + + void clear(); + + + +private: + + + + unsigned char* mpBuf; + + int mnBinaryLen; + + int mnBufferLen; + + int mnEncodedLen; + + bool mbEncoded; + +}; + + + + + +class CppSQLite3Query + +{ + +public: + + + + CppSQLite3Query(); + + + + CppSQLite3Query(const CppSQLite3Query& rQuery); + + + + CppSQLite3Query(sqlite3* pDB, + + sqlite3_stmt* pVM, + + bool bEof, + + bool bOwnVM=true); + + + + CppSQLite3Query& operator=(const CppSQLite3Query& rQuery); + + + + virtual ~CppSQLite3Query(); + + + + int numFields(); + + + + int fieldIndex(const char* szField); + + const char* fieldName(int nCol); + + + + const char* fieldDeclType(int nCol); + + int fieldDataType(int nCol); + + + + const char* fieldValue(int nField); + + const char* fieldValue(const char* szField); + + + + int getIntField(int nField, int nNullValue=0); + + int getIntField(const char* szField, int nNullValue=0); + + + + double getFloatField(int nField, double fNullValue=0.0); + + double getFloatField(const char* szField, double fNullValue=0.0); + + + + const char* getStringField(int nField, const char* szNullValue=""); + + const char* getStringField(const char* szField, const char* szNullValue=""); + + + + const unsigned char* getBlobField(int nField, int& nLen); + + const unsigned char* getBlobField(const char* szField, int& nLen); + + + + bool fieldIsNull(int nField); + + bool fieldIsNull(const char* szField); + + + + bool eof(); + + + + void nextRow(); + + + + void finalize(); + + + +private: + + + + void checkVM(); + + + + sqlite3* mpDB; + + sqlite3_stmt* mpVM; + + bool mbEof; + + int mnCols; + + bool mbOwnVM; + +}; + + + + + +class CppSQLite3Table + +{ + +public: + + + + CppSQLite3Table(); + + + + CppSQLite3Table(const CppSQLite3Table& rTable); + + + + CppSQLite3Table(char** paszResults, int nRows, int nCols); + + + + virtual ~CppSQLite3Table(); + + + + CppSQLite3Table& operator=(const CppSQLite3Table& rTable); + + + + int numFields(); + + + + int numRows(); + + + + const char* fieldName(int nCol); + + + + const char* fieldValue(int nField); + + const char* fieldValue(const char* szField); + + + + int getIntField(int nField, int nNullValue=0); + + int getIntField(const char* szField, int nNullValue=0); + + + + double getFloatField(int nField, double fNullValue=0.0); + + double getFloatField(const char* szField, double fNullValue=0.0); + + + + const char* getStringField(int nField, const char* szNullValue=""); + + const char* getStringField(const char* szField, const char* szNullValue=""); + + + + bool fieldIsNull(int nField); + + bool fieldIsNull(const char* szField); + + + + void setRow(int nRow); + + + + void finalize(); + + + +private: + + + + void checkResults(); + + + + int mnCols; + + int mnRows; + + int mnCurrentRow; + + char** mpaszResults; + +}; + + + + + +class CppSQLite3Statement + +{ + +public: + + + + CppSQLite3Statement(); + + + + CppSQLite3Statement(const CppSQLite3Statement& rStatement); + + + + CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM); + + + + virtual ~CppSQLite3Statement(); + + + + CppSQLite3Statement& operator=(const CppSQLite3Statement& rStatement); + + + + int execDML(); + + + + CppSQLite3Query execQuery(); + + + + void bind(int nParam, const char* szValue); + + void bind(int nParam, const int nValue); + + void bind(int nParam, const double dwValue); + + void bind(int nParam, const unsigned char* blobValue, int nLen); + + void bindNull(int nParam); + + + + void reset(); + + + + void finalize(); + + + +private: + + + + void checkDB(); + + void checkVM(); + + + + sqlite3* mpDB; + + sqlite3_stmt* mpVM; + +}; + + + + + +class CppSQLite3DB + +{ + +public: + + + + CppSQLite3DB(); + + + + virtual ~CppSQLite3DB(); + + + + void open(const char* szFile); + + + + void close(); + + + + bool tableExists(const char* szTable); + + + + int execDML(const char* szSQL); + + + + CppSQLite3Query execQuery(const char* szSQL); + + + + int execScalar(const char* szSQL); + + + + CppSQLite3Table getTable(const char* szSQL); + + + + CppSQLite3Statement compileStatement(const char* szSQL); + + + + sqlite_int64 lastRowId(); + + + + void interrupt() { sqlite3_interrupt(mpDB); } + + + + void setBusyTimeout(int nMillisecs); + + + + static const char* SQLiteVersion() { return SQLITE_VERSION; } + + + +private: + + + + CppSQLite3DB(const CppSQLite3DB& db); + + CppSQLite3DB& operator=(const CppSQLite3DB& db); + + + + sqlite3_stmt* compile(const char* szSQL); + + + + void checkDB(); + + + + sqlite3* mpDB; + + int mnBusyTimeoutMs; + +}; + + + +#endif + diff --git a/src/OldcreaImageIOGimmickView.h b/src/OldcreaImageIOGimmickView.h new file mode 100644 index 0000000..3aa98ac --- /dev/null +++ b/src/OldcreaImageIOGimmickView.h @@ -0,0 +1,162 @@ +namespace creaImageIO +{ + /** + * \ingroup View + */ + //===================================================================== + + //===================================================================== + ///Abstract class that handles views, attributes and previews (GUI) for Gimmick. + class GimmickView + { + public: + /// Ctor + GimmickView(); + /// Virtual destructor + virtual ~GimmickView(); + + //==================================================================== + // General + //==================================================================== + + /// Returns the size of the current selection + virtual int GetSelectionSize() { return 0; } + /// Returns true if there is a valid selection + virtual bool IsSelectionValid(){ return false; } + /// Returns the vector of full filenames of selected images + virtual void GetSelectedFiles(std::vector&){ return; } + /// Returns the vector of images corresponding to selection + virtual void GetSelectedImages(std::vector&){ return; } + /// Returns the vector of DicomNode corresponding to selection + virtual void GetSelectedDicomNodes(std::vector&){ return; } + /// Returns the DicomNode corresponding to the tree item + virtual DicomNode* GetDicomNodeOfItem(const TreeItemId& i); + + + /// Type of list of DicomDatabase + typedef std::vector DicomDatabaseListType; + /// Returns the list of DicomDatabase open + virtual DicomDatabaseListType& GetDicomDatabaseList() + { return null; } + /// Returns the list of DicomDatabase open (const) + virtual const DicomDatabaseListType& GetDicomDatabaseList() const + { return null; } + + protected: + ///Opens an existing database, or else, creates a local database. + virtual void OpenOrNewDatabase(bool open){ return; } + ///Shows the help + virtual void ShowHelp(); + + private: + ///Gets the extension of the database + const std::string& GetDatabaseExtension() { return null; } + ///Sets the extension of the database + virtual void SetDatabaseExtension(const std::string& ext){ return; } + + + //==================================================================== + // Preview Display Related + //==================================================================== + + + ///Shows the image sent as a parameter + private: + virtual void ShowImage(vtkImageData* image){ return; } + + //==================================================================== + // Favorites Related + //==================================================================== + + + public: + ///Loads or creates a favorites database + virtual void LoadOrCreateFavoritesDatabase(){ return; } + private: + ///Creates the user settings directory + void CreateUserSettingsDirectory(){ return; } + ///Obtains the user settings directory + const std::string& GetUserSettingsDirectory(){ return null; } + + //==================================================================== + // Attribute Display Related + //==================================================================== + + + ///Shows the Information regarding the node sent as a parameter + private: + virtual void ShowInformation(DicomNode*){ return; } + + //==================================================================== + // Tree Display Related + //==================================================================== + + protected: + /// Completely rebuilds the view with + /// current DicomDatabaseList + virtual void RebuildView(){ return; } + /// Recursively updates the part of the view corresponding + /// to the DicomDatabase passed + /// i.e. creates items for the DicomNode which do not have + /// deletes obsolete items (whose DicomNode has been deleted) + virtual void UpdateDicomDatabaseView(DicomDatabase*){ return; } + /// Recursively updates the part of the view corresponding + /// to the DicomNode provided. + /// parent is its parent in the tree (where to insert / remove it) + virtual void UpdateDicomNodeView(DicomNode* n, const TreeItemId& parent){ return; } + + private: + ///Type definition of the data regarding the tree + typedef WxGimmickTreeItemData TreeItemData; + ///Gets the item data of the tree item passed as a parameter + TreeItemData* GetItemData(const TreeItemId& id){ return null; } + ///Type definition of the data insid a node of the tree + typedef WxGimmickDicomNodeData NodeData; + + + //==================================================================== + // Class Attributes + //==================================================================== + + + int mSelectionType; + int mSelectionMaxImageDimension; + int mCurrentSelectionImageSize[4]; + + ///Existent Database List + DicomDatabaseListType mDicomDatabaseList; + ///Favorites database + DicomDatabase* mFavoriteDatabase; + + ///Path to the database list file + std::string mDatabaseListFile; + ///Extension of the database + std::string mDatabaseExtension; + + bool mJustStarted; + + int mFirstDicomDatabaseIconIndex; + + // Previewer + vtkImageViewer2* mViewer; + + int mx1,mx2,my1,my2,mz1,mz2; + double mspx,mspy,mspz; + + // Image preview : + // Multi-thread image reader + MultiThreadImageReader mReader; + // map of images name to node + std::map mImageFileNameToNode; + + //Controller which manages the interaction with the model + Gimmick* controller; + + }; + // EO class GimmickView + //===================================================================== + +} // EO namespace creaImageIO + +// EOF +#endif \ No newline at end of file diff --git a/src/README.txt b/src/README.txt new file mode 100644 index 0000000..fbe4fd9 --- /dev/null +++ b/src/README.txt @@ -0,0 +1,71 @@ +New architecture created on 04/02/09 following a MVC approach + +Elements of the architecture: + +* Data structure: Attributed tree +-------------------------------------- +in namespace creaImageIO::tree +Tree : Attributed tree structure + inherits Node + holds a vector of root TreeNode +TreeData : Abstract class to store user data on a tree +Node : + belong to a Tree, + holds a pointer on parent Node, + holds a vector of children Node +NodeData : Abstract class to store user data on a tree node +Descriptor : Descriptor of the structure a tree (number of levels, descriptors of each level, ...) + + holds a vector of LevelDescriptor +LevelDescriptor : + holds a vector of TreeAttributeDescriptor +AttributeDescriptor : + stores name, dicom group/elem, flags +Comparator: Abstract definition of a comparator of Node + Comparison is done by operator()(Node* const &, Node* const &) +ComparatorWithOrder : Abstract Comparator whose order can be reversed + Concrete comparison is done by method compare(Node* const &, Node* const &) +LexicographicalComparator : A Comparator which stores a vector of Comparators and which performs lexicographical comparison + +IntComparator : Compares the values of a given Attribute of the Nodes which is decoded as an int value +FloatComparator : Compares the values of a given Attribute of the Nodes which is decoded as a float value +StringComparator : Compares the values of a given Attribute of the Nodes which is decoded as a string value + + +* Models : TreeHandler and descendants +-------------------------------------- + +TreeHandler : Abstract class which 'handles' a Tree structure. + Can: + Load the children of a given Node + +SQLiteTreeHandler : Concrete TreeHandler which manages a tree stored in a sqlite database +CppSQLite3.h / CppSQLite3.cpp : C++ interface to sqlite db +... + +ImageFinder : Parses (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch) + + +* Image handling +---------------- + +creaImageIOImageReader.h/cpp : +SpecificImageReader +ImageReader + +creaImageIOMultiThreadImageReader.h/cpp +creaImageIOIndexedHeap.h/cpp + +* Views : +--------- +TreeView +WxTreeView +WxGimmickView +QTreeView +QGimmickView +... + +* Controller : +-------------- +Gimmick +... diff --git a/src/creaImageIOAbstractImageReader.h b/src/creaImageIOAbstractImageReader.h new file mode 100644 index 0000000..3957f3a --- /dev/null +++ b/src/creaImageIOAbstractImageReader.h @@ -0,0 +1,58 @@ +#ifndef __creaImageIOAbstractImageReader_h_INCLUDED__ +#define __creaImageIOAbstractImageReader_h_INCLUDED__ + + +#include +#include +#include +#include +#include "creaImageIOTreeAttributeMapType.h" + +namespace creaImageIO +{ + + + /** + * \ingroup IO + */ + + //===================================================================== + /// Abstract image reader + class AbstractImageReader + { + public: + AbstractImageReader() {} + virtual ~AbstractImageReader() {} + + /// Get the reader's name + const std::string& GetName() const { return mName; } + + /// Add file extensions read by the reader + virtual void PushBackExtensions(std::vector&) {} + + /// Test if file is read by this reader + virtual bool CanRead(const std::string& filename) { return false; } + + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename) { return 0; } + + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr) {} + + protected: + + /// Set the reader's name + void SetName(const std::string& s) { mName = s; } + private: + std::string mName; + }; + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOAbstractImageReader_h_INCLUDED__ diff --git a/src/creaImageIODicomImageReader.cpp b/src/creaImageIODicomImageReader.cpp new file mode 100644 index 0000000..05ea3b2 --- /dev/null +++ b/src/creaImageIODicomImageReader.cpp @@ -0,0 +1,162 @@ +#include + +#include + + + +#include +#include "boost/filesystem/path.hpp" + +#include +#include +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + + //===================================================================== + DicomImageReader::DicomImageReader() + { + mReader = vtkGdcmReader::New(); +//EED mReader->SetFlipY(false); + SetName ( "Dicom" ); + + }; + //===================================================================== + + //===================================================================== + DicomImageReader::~DicomImageReader() + { + mReader->Delete(); + } + //===================================================================== + + //===================================================================== + bool DicomImageReader::CanRead(const std::string& filename) + { + GDCM_NAME_SPACE::Document*doc; + GDCM_NAME_SPACE::File* file = GDCM_NAME_SPACE::File::New(); + file->SetLoadMode( GDCM_NAME_SPACE::LD_ALL); + file->SetFileName(filename.c_str()); + file->Load(); + bool ok = file->IsReadable(); + if(!ok) + { + doc = (GDCM_NAME_SPACE::Document*)file; + ok = doc->IsReadable(); + } + file->Delete(); + return ok; + } + //===================================================================== + + //===================================================================== + vtkImageData* DicomImageReader::ReadImage(const std::string& filename) + { + vtkImageData* im = 0; + try + { + mReader->SetFileName(filename.c_str()); + mReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + + //===================================================================== + void DicomImageReader::PushBackExtensions(std::vector& v) + { + v.push_back("dcm"); + v.push_back(""); + } + //===================================================================== + + //======================================================================== + std::string irclean(const std::string& str) + { + if(str.size() > 0) + { + if (str == "GDCM::Unfound") + { + return ""; + } + if (str[str.size()-1]==' ') + { + return irclean(str.substr(0,str.size()-1)); + } + if (str[str.size()-1]==0) + { + return irclean(str.substr(0,str.size()-1)); + } + } + + return str; + } + //======================================================================== + + //===================================================================== + void DicomImageReader::ReadAttributes(const std::string& filename, + std::map& attr) + { + GimmickMessage(2,"Reading attributes from DICOM file '" + < file(GDCM_NAME_SPACE::File::New());//, DicomImageReader::deleter()); + + GDCM_NAME_SPACE::Document *doc= GDCM_NAME_SPACE::File::New(); + doc->SetLoadMode( GDCM_NAME_SPACE::LD_ALL); + doc->SetFileName(filename.c_str()); + doc->Load(); + file->SetLoadMode( GDCM_NAME_SPACE::LD_ALL); + file->SetFileName(filename.c_str()); + file->Load(); + if (file->IsReadable())// ||((GDCM_NAME_SPACE::Document*) file)->IsReadable()) + { + std::map::iterator i; + for (i=attr.begin();i!=attr.end();++i) + { + if ( i->first == "D0004_1500" ) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + else if ( i->first == "FullFileName" ) + { + i->second = filename; + } + else if ( i->first == "FullFileDirectory" ) + { + std::string::size_type last_pos = filename.find_last_of("//"); + //find first separator + i->second = filename.substr(0, last_pos); + } + else + { + uint16_t el; + uint16_t gr; + + tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el); + if ( ( gr!=0 ) && ( el!=0 ) ) + { + std::string val = file->GetEntryString(gr,el); + i->second = irclean(val); + } + } + } + } + } + + //===================================================================== + +} // namespace creaImageIO + diff --git a/src/creaImageIODicomImageReader.h b/src/creaImageIODicomImageReader.h new file mode 100644 index 0000000..00f5cb0 --- /dev/null +++ b/src/creaImageIODicomImageReader.h @@ -0,0 +1,52 @@ +#ifndef __creaImageIODicomImageReader_h_INCLUDED__ +#define __creaImageIODicomImageReader_h_INCLUDED__ + + +#include +#include +// forward decl +class vtkGdcmReader; + +namespace creaImageIO +{ + + + /** + * \ingroup IO + */ + + //===================================================================== + /// Concrete image reader for DICOM images + class DicomImageReader : virtual public AbstractImageReader + { + public: + DicomImageReader(); + virtual ~DicomImageReader(); + + /// Add file extensions read by the reader + virtual void PushBackExtensions(std::vector&); + /// Test if file is read by this reader + virtual bool CanRead(const std::string& filename); + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename); + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + + private: + vtkGdcmReader* mReader; + struct deleter + { + + }; + friend struct deleter; + }; + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIODicomImageReader_h_INCLUDED__ diff --git a/src/creaImageIODicomImageReader2.cpp b/src/creaImageIODicomImageReader2.cpp new file mode 100644 index 0000000..3dd8120 --- /dev/null +++ b/src/creaImageIODicomImageReader2.cpp @@ -0,0 +1,235 @@ +#include + + + +#include +#include "boost/filesystem/path.hpp" + +#include +#include +#include + + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + + //===================================================================== + DicomImageReader::DicomImageReader() + { + mReader = vtkGDCMImageReader::New(); + SetName ( "Dicom" ); + + }; + //===================================================================== + + //===================================================================== + DicomImageReader::~DicomImageReader() + { + mReader->Delete(); + } + //===================================================================== + + //===================================================================== + bool DicomImageReader::CanRead(const std::string& filename) + { + gdcm::Reader reader; + reader.SetFileName( filename.c_str() ); + return reader.Read(); + + } + //===================================================================== + + //===================================================================== + vtkImageData* DicomImageReader::ReadImage(const std::string& filename) + { + vtkImageData* im = 0; + try + { + mReader->SetFileName(filename.c_str()); + mReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + + //===================================================================== + void DicomImageReader::PushBackExtensions(std::vector& v) + { + v.push_back("dcm"); + v.push_back(""); + } + //===================================================================== + + //======================================================================== + std::string irclean(const std::string& str) + { + if(str.size() > 0) + { + if (str == "GDCM::Unfound") + { + return ""; + } + if (str[str.size()-1]==' ') + { + return irclean(str.substr(0,str.size()-1)); + } + if (str[str.size()-1]==0) + { + return irclean(str.substr(0,str.size()-1)); + } + } + + return str; + } + //======================================================================== + + //===================================================================== + void DicomImageReader::ReadAttributes(const std::string& filename, + std::map& attr) + { + GimmickMessage(2,"Reading attributes from DICOM file '" + <::iterator i; + for (i=attr.begin();i!=attr.end();++i) + { + if ( i->first == "D0004_1500" ) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + else if ( i->first == "FullFileName" ) + { + i->second = filename; + } + else if ( i->first == "FullFileDirectory" ) + { + std::string::size_type last_pos = filename.find_last_of("//"); + //find first separator + i->second = filename.substr(0, last_pos); + } + else + { + uint16_t el; + uint16_t gr; + + tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el); + if ( ( gr!=0 ) && ( el!=0 ) ) + { + gdcm::DataElement de( gdcm::Tag(gr,el) ); + std::string val = GetStringValueFromTag(reader.GetFile().GetDataSet().GetDataElement(gdcm::Tag(gr,el))); + i->second = irclean(val); + } + } + } + } + } + +void DicomImageReader::ReadAttributes2(const std::string& filename, + std::map& attr) + { + + if(!b_loaded) + { + std::map::iterator i; + for (i=attr.begin();i!=attr.end();++i) + { + if ( i->first == "D0004_1500" || i->first == "FullFileName" || i->first == "FullFileDirectory" ) + { + + } + else + { + uint16_t el; + uint16_t gr; + + tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el); + mscan.AddTag(gdcm::Tag(gr,el) ); + } + } + b_loaded = true; + } + bool b = mscan.IsKey(filename.c_str()); + if( b ) + { + const gdcm::Scanner::TagToValue &mapping = mscan.GetMapping(filename.c_str()); + gdcm::Scanner::TagToValue::const_iterator it = mapping.begin(); + std::map::iterator i; + for (i=attr.begin();i!=attr.end();++i, ++it) + { + if ( i->first == "D0004_1500" ) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + else if ( i->first == "FullFileName" ) + { + i->second = filename; + } + else if ( i->first == "FullFileDirectory" ) + { + std::string::size_type last_pos = filename.find_last_of("//"); + //find first separator + i->second = filename.substr(0, last_pos); + } + else + { + const char *value = it->second; + i->second = irclean(it->second); + } + } + } +} + + + + + + + + + + + + + + + + + const std::string DicomImageReader::GetStringValueFromTag(const gdcm::DataElement& de) +{ + static std::string buffer; + buffer = ""; // cleanup previous call + + + const gdcm::ByteValue *bv = de.GetByteValue(); + if( bv ) // Can be Type 2 + { + buffer = std::string( bv->GetPointer(), bv->GetLength() ); + // Will be padded with at least one \0 + } + + + // Since return is a const char* the very first \0 will be considered + return buffer.c_str(); +} + //===================================================================== + +} // namespace creaImageIO + diff --git a/src/creaImageIODicomImageReader2.h b/src/creaImageIODicomImageReader2.h new file mode 100644 index 0000000..08b01f3 --- /dev/null +++ b/src/creaImageIODicomImageReader2.h @@ -0,0 +1,64 @@ +#ifndef __creaImageIODicomImageReader_h_INCLUDED__ +#define __creaImageIODicomImageReader_h_INCLUDED__ + + +#include +#if defined(USE_GDCM2) +#include +#include +#include +#endif + +class vtkGDCMImageReader; + +namespace creaImageIO +{ + + + /** + * \ingroup IO + */ + + //===================================================================== + /// Concrete image reader for DICOM images + class DicomImageReader : virtual public AbstractImageReader + { + public: + DicomImageReader(); + virtual ~DicomImageReader(); + + /// Add file extensions read by the reader + virtual void PushBackExtensions(std::vector&); + /// Test if file is read by this reader + virtual bool CanRead(const std::string& filename); + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename); + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + void ReadAttributes2(const std::string& filename, + tree::AttributeMapType& attr); + + private: + const std::string GetStringValueFromTag( const gdcm::DataElement& ds); + vtkGDCMImageReader *mReader; + gdcm::Scanner mscan; + bool b_loaded; + struct deleter + { + void operator()(gdcm::File* p) + { + delete p; + } + }; + friend struct deleter; + }; + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIODicomImageReader_h_INCLUDED__ diff --git a/src/creaImageIODicomScanner.cpp b/src/creaImageIODicomScanner.cpp new file mode 100644 index 0000000..43706c1 --- /dev/null +++ b/src/creaImageIODicomScanner.cpp @@ -0,0 +1,229 @@ +#include +#include "creaImageIOSystem.h" +#include +#include "boost/algorithm/string.hpp" + + + +#include +#include "boost/filesystem/path.hpp" + +#include +#include +#include +#include + +#include + + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + + //===================================================================== + DicomImageScanner::DicomImageScanner() + { + mReader = vtkGDCMImageReader::New(); + mscan.ClearTags(); + b_loaded = false; + }; + //===================================================================== + + //===================================================================== + DicomImageScanner::~DicomImageScanner() + { + mReader->Delete(); + } + //===================================================================== + + //===================================================================== + bool DicomImageScanner::addDirectory(std::string& filename, std::map& attr) + { + if(!b_loaded) + { + mscan.ClearTags(); + std::map::iterator i; + int j= 0; + for (i=attr.begin();i!=attr.end();++i, j++) + { + if ( i->first == "D0004_1500" || i->first == "FullFileName" || i->first == "FullFileDirectory" ) + { + + } + else + { + uint16_t el; + uint16_t gr; + + tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el); + mscan.AddTag(gdcm::Tag(gr,el) ); + + } + } + b_loaded = true; + } + gdcm::Directory d; + + boost::algorithm::replace_all(filename,"\\", "/"); + d.Load(filename.c_str(),true); + mscan.Scan(d.GetFilenames()); + + return true; + + } + //===================================================================== + + //===================================================================== + vtkImageData* DicomImageScanner::ReadImage(const std::string& filename) + { + vtkImageData* im = 0; + try + { + mReader->SetFileName(filename.c_str()); + mReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + + + //===================================================================== + + //======================================================================== + std::string DicomImageScanner::irclean(const std::string& str) + { + if(str.size() > 0) + { + if (str == "GDCM::Unfound") + { + return ""; + } + if (str[str.size()-1]==' ') + { + return irclean(str.substr(0,str.size()-1)); + } + if (str[str.size()-1]==0) + { + return irclean(str.substr(0,str.size()-1)); + } + } + + return str; + } + //======================================================================== + + //===================================================================== + +void DicomImageScanner::ReadAttributes(const std::string& filename, + std::map& attr) + { + std::string name = "E:\\data-images\\dicoms\\CD1\\DICOM\\09112417\\37390000/31582235"; + + bool b = mscan.IsKey(filename.c_str()); + if( b ) { + const gdcm::Scanner::TagToValue &mapping = mscan.GetMapping(filename.c_str()); + gdcm::Scanner::TagToValue::const_iterator it = mapping.begin(); + + std::map::iterator i; + int j= 0; + + + + for (;it != mapping.end(); ++it) + { + /*std::stringstream val; + val << "D"; + val << std::hex << it->first.GetGroup() ; + val << "_";; + val << std::hex << it->first.GetElement(); + var >> key*/ + char key[12] ; + + sprintf(key,"D%04x_%04x", it->first.GetGroup(), it->first.GetElement()); + attr[key] = irclean(it->second); + } + + + /* + + + for (i=attr.begin();i!=attr.end();++i, j++) + {*/ + if ( attr.find("D0004_1500") != attr.end()) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + attr["D0004_1500"] = f; + } + if ( attr.find("FullFileName")!= attr.end()) + { + attr["FullFileName"] = filename; + } + if ( attr.find("FullFileDirectory" )!= attr.end()) + { + std::string::size_type last_pos = filename.find_last_of("//"); + //find first separator + attr["FullFileDirectory" ] = filename.substr(0, last_pos); + } + // else + // { + + // uint16_t el; + // uint16_t gr; + + // tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el); + + // mscan.AddTag(gdcm::Tag(gr,el) ); + + // // const char *value = it->second; + // i->second = irclean(it->second); + // ++it; + // } + //} + } +} + + + + + + + + + + + + + + + + + const std::string DicomImageScanner::GetStringValueFromTag(const gdcm::DataElement& de) +{ + static std::string buffer; + buffer = ""; // cleanup previous call + + + const gdcm::ByteValue *bv = de.GetByteValue(); + if( bv ) // Can be Type 2 + { + buffer = std::string( bv->GetPointer(), bv->GetLength() ); + // Will be padded with at least one \0 + } + + + // Since return is a const char* the very first \0 will be considered + return buffer.c_str(); +} + //===================================================================== + +} // namespace creaImageIO + diff --git a/src/creaImageIODicomScanner.h b/src/creaImageIODicomScanner.h new file mode 100644 index 0000000..9209751 --- /dev/null +++ b/src/creaImageIODicomScanner.h @@ -0,0 +1,57 @@ +#ifndef __creaImageIODicomScanner_h_INCLUDED__ +#define __creaImageIODicomScanner_h_INCLUDED__ + +#include "creaImageIOTree.h" + +#if defined(USE_GDCM2) +#include +#include +#include +#endif + +class vtkGDCMImageReader; + +namespace creaImageIO +{ + + + /** + * \ingroup IO + */ + + //===================================================================== + /// Concrete image reader for DICOM images + class DicomImageScanner + { + public: + DicomImageScanner(); + virtual ~DicomImageScanner(); + + /// Add file extensions read by the reader + bool addDirectory(std::string& filename, std::map& attr); + + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename); + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + void ReadAttributes2(const std::string& filename, + tree::AttributeMapType& attr); + + std::string irclean(const std::string& str); + private: + const std::string GetStringValueFromTag( const gdcm::DataElement& ds); + vtkGDCMImageReader *mReader; + gdcm::Scanner mscan; + bool b_loaded; + + }; + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIODicomScanner_h_INCLUDED__ diff --git a/src/creaImageIOExternalGimmick.cpp b/src/creaImageIOExternalGimmick.cpp new file mode 100644 index 0000000..8201a81 --- /dev/null +++ b/src/creaImageIOExternalGimmick.cpp @@ -0,0 +1,79 @@ +#include + + +vtkImageData* getImageDataDialog() { + // wxApp::OnInit(); + #ifdef __WXGTK__ + //See http://www.wxwindows.org/faqgtk.htm#locale + setlocale(LC_NUMERIC, "C"); + #endif + wxInitAllImageHandlers(); + + creaImageIO::SetGimmickMessageLevel(5); + creaImageIO::SetGimmickDebugMessageLevel(0); + + int min_dim = GIMMICK_2D_IMAGE_SELECTION; + int max_dim = GIMMICK_3D_IMAGE_SELECTION; + int output_dim = NATIVE; + int threads = 1; + + creaImageIO::WxGimmickReaderDialog w( + 0, + -1, + "localdatabase_Descriptor.dscp", + "Local Database", + _T("Select image(s) - Gimmick! (c) CREATIS-LRMN 2008"), + wxDefaultPosition, + wxSize(1200,800), + min_dim, + max_dim, + output_dim, + threads); + w.ShowModal(); + + if (w.GetReturnCode() == wxID_OK) + { + std::cout << "$$$$ main : user clicked 'OK' $$$$"< s; + w.GetSelectedFiles(s); + std::vector::iterator i; + for (i=s.begin();i!=s.end();++i) + { + std::cout << *i << std::endl; + } + std::cout << "$$$$ "< images; + w.GetSelectedImages(images,output_dim); + std::cout<Delete(); + + + } + else if (w.GetReturnCode() == wxID_CANCEL) + { + w.OnExit(); + std::cout << "$$$$ main : user clicked 'CANCEL' $$$$"< + + extern "C" + { + CREAIMAGEIO_EXPORT vtkImageData* getImageDataDialog(); + } +#endif + diff --git a/src/creaImageIOGimmick.cpp b/src/creaImageIOGimmick.cpp new file mode 100644 index 0000000..5edb4ed --- /dev/null +++ b/src/creaImageIOGimmick.cpp @@ -0,0 +1,491 @@ + +#include + +#include +#include +#include +//#include "io.h" +#ifndef PATH_MAX // If not defined yet : do it +# define PATH_MAX 2048 +#endif +#include +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +namespace creaImageIO +{ + //============================================================== + Gimmick::Gimmick() + : mImageAdder(0) + { + RegisterGimmickMessageTypes(); + mSettings=0; + mSynchronizer=0; + mLocalDescpName = "localdatabase_Descriptor.dscp"; + mLocalDBName = "Local database"; + } + //============================================================== + + + //============================================================== + Gimmick::~Gimmick() + { + + if(mSettings!=0) + { + mSettings->writeSettingsFile(); + delete mSettings; + } + if(mSynchronizer!=0) + { + delete mSynchronizer; + } + } + //============================================================== + + //============================================================== + void Gimmick::Initialize(const std::string i_namedescp, const std::string i_namedb) + { + mLocalDescpName = i_namedescp; + mLocalDBName = i_namedb; + Initialize(); + } + + //============================================================== + void Gimmick::Initialize() + { + std::string i_nameDB = mLocalDBName; + // Create the UserSettings dir if does not exist + CreateUserSettingsDirectory(); + // Sets the current directory to the home dir + mCurrentDirectory = GetHomeDirectory(); + mSynchronizer= new Synchronizer(GetUserSettingsDirectory()+"Shared/gimmick/"); + + mSettings = new Settings(mCurrentDirectory); + + std::string dbpath = GetLocalDatabasePath(); + + // Create or open local database + std::string dpath= mCurrentDirectory + "/.gimmick/Shared/gimmick/" + mLocalDescpName; + + boost::algorithm::replace_all( dpath, + INVALID_FILE_SEPARATOR , + VALID_FILE_SEPARATOR); + mLocalDatabase = createDB(i_nameDB, dpath, dbpath); + // Add it to the TreeHandlerMap + mTreeHandlerMap[i_nameDB] = mLocalDatabase; + + //Add additional DB from user Settings + addDBSettings(); + } + + /////////////////////////////////////////////////////////////////////// + // add DB to TreeHandler Map // + // @param i_name : DB name // + // @param i_location : DB location // + // return : - // + //////////////////////////////////////////////////////////////////////// + void Gimmick::addDB(const std::string &i_name, + const std::string &i_location) + { + if(mTreeHandlerMap.find(i_name) == mTreeHandlerMap.end()) + { + mTreeHandlerMap[i_name] = new SQLiteTreeHandler(i_location); + mTreeHandlerMap[i_name]->Open(true); + mSettings->addDB(i_location); + } + } + + /////////////////////////////////////////////////////////////////////////// + // create a DB from a attributes descriptor file for medical images // + // @param i_name : DB name // + // @param i_locDesc : location of descriptor file // + // @param i_locDB : location of DB // + // return : the SQLiteTreeHandler object on DB // + ///////////////////////////////////////////////////////////////////////// + SQLiteTreeHandler* Gimmick::createDB(const std::string &i_name, + const std::string &i_locDesc, + const std::string &i_locDB) + { + SQLiteTreeHandler* sqlTreeH( new SQLiteTreeHandler(i_locDB) ); + // Create or open local database + if (! boost::filesystem::exists(i_locDB) ) + { + std::string mess = "Local database '"; + mess += i_locDB; + mess += "' does not exist : creating it"; + GimmickMessage(1,mess<GetTree().GetDescriptor().createDescriptorfromFile(i_locDesc); + if ( ! sqlTreeH->Create(true) ) + { + GimmickError("ERROR CREATING '"<SetAttribute(0,"Name",i_name); + } + else + { + /// Open and test it + + GimmickDebugMessage(1,"Opening local database '" <Open(true) ) + { + GimmickError("ERROR OPENING '"<0) + { + // delete SQLiteTreeHandler Object + for( TreeHandlerMapType::const_iterator it = mTreeHandlerMap.begin(); + it!= mTreeHandlerMap.end(); + ++it) + { + delete it->second; + } + } + } + //============================================================== + + //================================================================ + // file separator +#if defined(_WIN32) +#define VALID_FILE_SEPARATOR "\\" +#define INVALID_FILE_SEPARATOR "/" +#else +#define INVALID_FILE_SEPARATOR "\\" +#define VALID_FILE_SEPARATOR "/" +#endif + //================================================================ + + //================================================================ + const std::string& Gimmick::GetHomeDirectory() + { + if (mHomeDirectory.size()==0) + { +#if defined(__GNUC__) + mHomeDirectory = getenv("HOME"); +#elif defined(_WIN32) + mHomeDirectory = getenv("USERPROFILE"); +#endif + } + return mHomeDirectory; + } + //================================================================ + const std::string& Gimmick::GetUserSettingsDirectory() + { + if (mUserSettingsDirectory.size()==0) + { + mUserSettingsDirectory = GetHomeDirectory(); + mUserSettingsDirectory += "/.gimmick/"; + boost::algorithm::replace_all( mUserSettingsDirectory, + INVALID_FILE_SEPARATOR , + VALID_FILE_SEPARATOR); + } + return mUserSettingsDirectory; + } + //================================================================ + + + //================================================================ + const std::string& Gimmick::GetLocalDatabasePath() + { + if (mLocalDatabasePath.size()==0) + { + mLocalDatabasePath = GetUserSettingsDirectory(); + mLocalDatabasePath += "Shared/gimmick/"; + mLocalDatabasePath += mLocalDBName; + mLocalDatabasePath +=".sqlite3"; + boost::algorithm::replace_all( mLocalDatabasePath, + INVALID_FILE_SEPARATOR , + VALID_FILE_SEPARATOR); + } + return mLocalDatabasePath; + } + + //======================================================================== + + //======================================================================== + void Gimmick::CreateUserSettingsDirectory() + { + + // std::string st("C:/Documents and Settings/cervenansky/.gimmick/"); + // boost::algorithm::replace_all( st, + // INVALID_FILE_SEPARATOR , + // VALID_FILE_SEPARATOR); + //const boost::filesystem::path mpath(st); +//C:\Documents and Settings\cervenansky\.gimmick"); + //if ( !boost::filesystem::exists( path ) ) return ; + // boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + // for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr ) + // { + //// If is directory & recurse : do recurse + // if ( boost::filesystem::is_directory(itr->status()) ) + // return; + // } + + //JCP settings dir 02/10/2009 + const std::string settingsdirectory = GetUserSettingsDirectory(); + //boost::algorithm::replace_all( mUserSettingsDirectory, + // INVALID_FILE_SEPARATOR , + // VALID_FILE_SEPARATOR); +;//("E:\frederic"); + //("C:\\Documents and Settings\\cervenansky\\.gimmick\\"); // settingsdirectory ); + bool isdir = false; + isdir = boost::filesystem::is_directory(settingsdirectory); // settingsdirectory ); + if (! isdir ) + { + GimmickMessage(1,"Directory '"<second; + } + + //======================================================================== + /// Add the files to the tree handler + void Gimmick::AddFiles(const std::string& d, + const std::vector& filenames) + { + GimmickMessage(2,"Adding files to '"<i_files) { } + //======================================================================== + + void Gimmick::CopyFiles(const std::vector& filenames, const std::string& d ) + { + TreeHandler * handler=GetTreeHandler(d); + mImageAdder.SetCurrentDatabase(d); + mImageAdder.SetTreeHandler(handler); + mImageAdder.SetSynchronizer(mSynchronizer); + mImageAdder.CopyFiles(filenames, mSettings->getValue(SETTINGS_COPY_PATH)); + } + + //======================================================================== + + std::string Gimmick::Synchronize(const std::string& d, bool repair, bool checkAttributes) + { + TreeHandler * handler=GetTreeHandler(d); + mImageAdder.SetCurrentDatabase(d); + mImageAdder.SetTreeHandler(handler); + mImageAdder.SetSynchronizer(mSynchronizer); + return mImageAdder.Synchronize(repair, checkAttributes); + } + + //======================================================================== + /// + void Gimmick::Print(const std::string& d) + { + GetTreeHandler(d)->GetTree().Print(); + } + //======================================================================== + + void Gimmick::GetSetting(const std::string& name, std::string& value) + { + value = mSettings->getValue(name); + } + //======================================================================== + + //======================================================================== + + void Gimmick::GetAttributes(const std::string& d, + const std::string& filename, + const std::vector& params, + std::vector& results) + { + TreeHandler * handler=GetTreeHandler(d); + mImageAdder.SetCurrentDatabase(d); + mImageAdder.SetTreeHandler(handler); + mImageAdder.SetSynchronizer(mSynchronizer); + mImageAdder.GetAttributes(params, filename, results); + } + //======================================================================== + + //======================================================================== + + void Gimmick::UpdateSetting(const std::string& name, const std::string& value) + { + mSettings->updateSetting(name,value); + mSettings->writeSettingsFile(); + } + //======================================================================== + + void Gimmick::DeleteDrive(const std::string& drive) + { + for( TreeHandlerMapType::const_iterator it = mTreeHandlerMap.begin(); + it!= mTreeHandlerMap.end(); + ++it) + { + mImageAdder.SetTreeHandler(it->second); + mImageAdder.DeleteDriveFromMainDB(drive); + } + mImageAdder.SetSynchronizer(mSynchronizer); + mImageAdder.DeleteDriveFromOtherDB(drive); + } + + //======================================================================== + void Gimmick::EditField(tree::Node* node, const std::string& d, const std::string& name, const std::string& key, const std::string& val) + { + TreeHandler * handler=GetTreeHandler(d); + mImageAdder.SetCurrentDatabase(d); + mImageAdder.SetTreeHandler(handler); + mImageAdder.EditField(node,name,key,val); + } + //======================================================================== + + //////////////////////////////////////////////////////////////////////// + // add DB from Settings file // + // @param : - // + // return : - // + //////////////////////////////////////////////////////////////////////// + void Gimmick::addDBSettings() + { + + std::string pathSettings = mSettings->getValue(SETTINGS_DBPATH); + + // split to find all paths + std::vector paths; + std::string separator = ";"; + std::string::size_type last_pos = pathSettings.find_first_not_of(separator); + //find first separator + std::string::size_type pos = pathSettings.find_first_of(separator, last_pos); + while(std::string::npos != pos || std::string::npos != last_pos) + { + paths.push_back(pathSettings.substr(last_pos, pos - last_pos)); + last_pos = pathSettings.find_first_not_of(separator, pos); + pos = pathSettings.find_first_of(separator, last_pos); + } + + std::vector::iterator it_path = paths.begin(); + for(; it_path != paths.end(); ++it_path) + { + pos = it_path->find_last_of("\\"); + last_pos = it_path->find_last_of("."); + std::string name = it_path->substr(pos +1, last_pos -pos-1 ); + addDB(name, it_path->c_str()); + } + } +} diff --git a/src/creaImageIOGimmick.h b/src/creaImageIOGimmick.h new file mode 100644 index 0000000..017d153 --- /dev/null +++ b/src/creaImageIOGimmick.h @@ -0,0 +1,216 @@ +#ifndef __creaImageIOGimmick_h_INCLUDED__ +#define __creaImageIOGimmick_h_INCLUDED__ + +#include +#include +#include +#include + + + +#ifdef _DEBUG +#include +#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__) +#else +#define DEBUG_NEW new +#endif +// Only when asked +/* +#ifdef TRACKING_MEMORY_LEAKS +#ifdef WIN32 +#pragma warning(disable : 4291) +#endif + +void * operator new( size_t size, int line, const char *file ); +void * operator new[]( size_t size, int line, const char *file ); +void operator delete( void *p ); +void operator delete[]( void *p ); + +#ifdef OMISCID_NEW +#undef OMISCID_NEW +#endif + +#define OMISCID_NEW new( __LINE__, __FILE__ ) + +#else // TRACKING_MEMORY_LEAKS is not defined + +#define OMISCID_NEW new + +#endif*/ + + +namespace creaImageIO +{ + /** + * \defgroup Controller Controller + */ + /** + * \defgroup View View + */ + /** + * \defgroup Model Model + */ + /** + * \defgroup GUI Top level graphical user interfaces + */ + /** + * \defgroup IO Image I/O classes + */ + /** + * \defgroup Tree Attributed tree management + */ + /** + * \defgroup Previewer Preview related + */ + + /** + * \ingroup Controller + */ + + //======================================================================= + /// Central controler of the gimmick application + class CREAIMAGEIO_EXPORT Gimmick + { + public: + /// Ctor + Gimmick(); + /// Dtor + ~Gimmick(); + + /// + typedef TreeHandlerImageAdder::Progress AddProgress; + typedef TreeHandlerImageAdder::ProgressCallbackType AddProgressCallbackType; + + /// Adds the function f to the list of functions to call + /// when addition operations progres. + /// f is of type AddProgressCallbackType which is: + /// void (*AddProgressCallbackType)(AddProgress&) + /// To pass a member function 'f' of an instance 'c' of a class 'C' + /// as callback you have to 'bind' it, i.e. call: + /// ConnectAddProgressObserver ( boost::bind( &C::f , c, _1 ) ); + void ConnectAddProgressObserver( AddProgressCallbackType callback ) + { mImageAdder.ConnectProgressObserver(callback); } + + /// + const AddProgress& GetAddProgress() const { return mImageAdder.GetProgress(); } + + /// Initializes with default values (read/creates databases, etc.) + void Initialize(); + + /// Initializes with the local database descriptor in the path given (read/creates databases, etc.) + void Initialize(const std::string namedescp, const std::string namedb = "Local Database"); + + /// Finalize (closes databases, etc.) + void Finalize(); + + /// Sets level for messages "Gimmick!" + static void SetMessageLevel(int level); + /// Sets level for debug messages "Gimmick! DEBUG" + static void SetDebugMessageLevel(int level); + + /// Type of map from TreeHandler name to TreeHandler* + typedef std::map TreeHandlerMapType; + + typedef std::map::const_iterator ItTreeHandlerMap; + + /// Returns the TreeHandlerMap (ref) + TreeHandlerMapType& GetTreeHandlerMap() { return mTreeHandlerMap; } + + /// Returns the TreeHandlerMap (const ref) + const TreeHandlerMapType& GetTreeHandlerMap() const + { return mTreeHandlerMap; } + + /// Add the files to the given TreeHandler + void AddFiles(const std::string& handler, + const std::vector& filenames); + + /// Add a dir to the given TreeHandler + void AddDir(const std::string& handler, const std::string& path, + bool recurse); + + /// Removes a file from the given TreeHandler + void RemoveFile(const std::string& d, + tree::Node* filename); + + ///Deletes the given drive name from the databases + void DeleteDrive(const std::string& drive); + + + /// Copies the files into the local directory + void CopyFiles(const std::vector& filenames, const std::string& d ); + + + ///Synchronizes the loaded data with the database d. If repair is true the database will be updated, otherwise + ///only a warning sign will be issued + std::string Synchronize(const std::string& d, bool repair, bool checkAttributes); + + /// Prints the tree handled by the handler + void Print(const std::string& handler); + + ///create a DB from a given descriptor file and for a specific location + SQLiteTreeHandler* createDB(const std::string &i_name, + const std::string &i_locDesc, + const std::string &i_locDB); + + /// add an existent DB + void addDB(const std::string &i_nameDB, const std::string &i_locationDB); + + /// Returns the TreeHandler with a given name + TreeHandler* GetTreeHandler(const std::string& name) const; + + /// + SQLiteTreeHandler* GetLocalDatabase() { return mLocalDatabase; } + + const SQLiteTreeHandler* GetLocalDatabase() const { return mLocalDatabase; } + + ///Returns the given setting value for the given setting parameter + void GetSetting(const std::string& name, std::string& value); + + ///Updates the settings file + void UpdateSetting(const std::string& name, const std::string& value); + + /// add DB from Settings file + void addDBSettings(); + + ///Edits the field described by the name and key provided with the value given + void EditField(tree::Node* node, const std::string& d, const std::string& name, const std::string& key, const std::string& val); + + /// Returns the attributes in results described in params + void GetAttributes(const std::string& d, + const std::string& filename, + const std::vector& params, + std::vector& results); + + /// + const std::string& GetHomeDirectory(); + const std::string& GetUserSettingsDirectory(); + void CreateUserSettingsDirectory(); + const std::string& GetLocalDatabasePath(); + + + + //============================================= + private: + SQLiteTreeHandler* mLocalDatabase; + TreeHandlerMapType mTreeHandlerMap; + Synchronizer* mSynchronizer; + + std::string mCurrentDirectory; + std::string mHomeDirectory; + std::string mUserSettingsDirectory; + std::string mLocalDatabasePath; + Settings *mSettings; + TreeHandlerImageAdder mImageAdder; + std::string mLocalDBName; + std::string mLocalDescpName; + }; + // EO class Gimmick + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + + diff --git a/src/creaImageIOGimmickReaderDialog.cpp b/src/creaImageIOGimmickReaderDialog.cpp new file mode 100644 index 0000000..29c6bbf --- /dev/null +++ b/src/creaImageIOGimmickReaderDialog.cpp @@ -0,0 +1,53 @@ +#include +#include + +namespace creaImageIO +{ + + bool GimmickReaderDialog(std::vector& images, + const std::string i_namedescp , + const std::string i_namedb , + const std::string& title, + int posx, + int posy, + int sizex, + int sizey, + int image_min_type, + int image_max_type, + int image_out_dim, + int nb_threads) + { + + + creaImageIO::WxGimmickReaderDialog w(0, + -1, + i_namedescp, + i_namedb, + crea::std2wx(title), + wxPoint(posx,posy), + wxSize(sizex,sizey), + image_min_type, + image_max_type, + nb_threads); + w.ShowModal(); + + if (w.GetReturnCode() == wxID_OK) + { + w.GetSelectedImages(images,image_out_dim); + return true; + } + else if (w.GetReturnCode() == wxID_CANCEL) + { + return false; + } + else + { + std::cout << "!! ERROR : GimmickReaderDialog : dialog ended without return code !" + < +#include + + extern "C" + { + CREAIMAGEIO_EXPORT vtkImageData* getImageDataDialog(); + } + +namespace creaImageIO +{ + /** + * \ingroup GUI + * \brief Pops up a WxGimmickReaderDialog + * returns true if the user clicked 'Ok', false if 'Cancel' + * and fills the vector of images + */ + bool CREAIMAGEIO_EXPORT GimmickReaderDialog + (std::vector& images, + const std::string i_namedescp , + const std::string i_namedb = "Local Database", + const std::string& title = "Select images", + int posx = 0, int posy = 0, + int sizex = 1200, int sizey = 800, + int image_min_type = 2, + int image_max_type = 3, + int image_out_dim = 2, + int nb_threads = 1); + +} + +#endif diff --git a/src/creaImageIOGimmickView.cpp b/src/creaImageIOGimmickView.cpp new file mode 100644 index 0000000..3132aa6 --- /dev/null +++ b/src/creaImageIOGimmickView.cpp @@ -0,0 +1,643 @@ +#include +#include +#include "boost/filesystem.hpp" + +#if defined(USE_GDCM) +#include +#include +#include +#endif + +#if defined(USE_GDCM2) +#include +#endif + + +namespace fs = boost::filesystem; +namespace creaImageIO +{ + + ///Class used to represent the actual state of the image selected and to perform comparisons on its values + class ImageExtent + { + public: + ImageExtent(const std::string& x, const std::string& y, const std::string& z, const std::string& t) + { + sscanf(x.c_str(),"%d",&mExtent[0]); + sscanf(y.c_str(),"%d",&mExtent[1]); + sscanf(z.c_str(),"%d",&mExtent[2]); + sscanf(t.c_str(),"%d",&mExtent[3]); + if(x==""){mExtent[0]=1;} + if(y==""){mExtent[1]=1;} + if(z==""){mExtent[2]=1;} + if(t==""){mExtent[3]=1;} + + if (mExtent[3]>1) mDim=4; + else if (mExtent[2]>1) mDim=3; + else if (mExtent[1]>1) mDim=2; + else if (mExtent[0]>1) mDim=1; + else mDim=0; + } + + + ///Clears the extent + void Clear() { mExtent[0] = mExtent[1] = mExtent[2] = mExtent[3] = 1; } + + ///Returns true if the two extents are compatible + bool IsCompatible( const ImageExtent& ); + + ///Adds the extent passed as a parameter to the current extent + void Add ( const ImageExtent& ); + + ///Returns the ieth position of the extent + int Get(int i) { return mExtent[i]; } + + ///Returns the dimension of the current image + void SetDimension(int dim) { mDim=dim; } + + ///Returns the dimension of the current image + int GetDimension() { return mDim; } + + private: + int mExtent[4]; + int mDim; + }; + + //====================================================================== + + //====================================================================== + // CTor + GimmickView::GimmickView(boost::shared_ptr gimmick, int threads) + : mGimmick(gimmick), + mReader(threads) + { + GimmickDebugMessage(1,"GimmickView::GimmickView" + <GetTreeHandlerMap().begin(); + i!= mGimmick->GetTreeHandlerMap().end(); + ++i) + { + this->CreateTreeView(i->second); + } + } + + /// Create a tree view with a given name + void GimmickView::CreateSingleTreeView(std::string &i_name) + { + this->CreateTreeView(mGimmick->GetTreeHandlerMap()[i_name]); + + } + + + //====================================================================== + + //====================================================================== + /// Updates the TreeView of given name from level l to bottom + /// (calls the virtual method TreeView::Update()) + void GimmickView::UpdateTreeViewLevel(const std::string& t, int l) + { + TreeViewMapType::iterator i; + i = GetTreeViewMap().find(t); + if ( i == GetTreeViewMap().end() ) + { + GimmickError("INTERNAL ERROR : GimmickView::UpdateTreeView : '" + <second->UpdateLevel(l); + } + + //====================================================================== + /// Clears the status and begins a new selection process + void GimmickView::ResetExtent() + { + if(mImageExtent!=0) + { + mImageExtent.reset(); + } + valid=true; + } + + //====================================================================== + + //====================================================================== + bool ImageExtent::IsCompatible(const ImageExtent& ie) + { + bool compatible=true; + ImageExtent * extent= (ImageExtent*)&ie; + if((*extent).Get(0)!=Get(0) + || (*extent).Get(1)!=Get(1)) + { + compatible=false; + } + return compatible; + } + + //====================================================================== + + //====================================================================== + void ImageExtent::Add(const ImageExtent& ie) + { + ImageExtent * extent= (ImageExtent*)&ie; + mExtent[2]+=(*extent).Get(2); + if(mExtent[2]>1) + { + SetDimension(3); + } + } + + //====================================================================== + /// No selected image + bool GimmickView::NoValidateSelected () + { + GimmickDebugMessage(2,"Validating selected"< ie=boost::shared_ptr(new ImageExtent((*sel).GetAttribute("D0028_0010"), + (*sel).GetAttribute("D0028_0011"), + (*sel).GetAttribute("D0028_0012"), + "")); + if(mImageExtent==0) + { + mImageExtent=ie; + if((mImageExtent->Get(min_dim-1)<2)||(mImageExtent->Get(max_dim)>1)) + { + valid=false; + } + else + { + std::stringstream out; + out << mImageExtent->GetDimension() << "D image " << mImageExtent->Get(0) << "x"<< mImageExtent->Get(1) << "x"<< mImageExtent->Get(2) <<" selected"; + mMessage = out.str(); + mImageExtent->SetDimension(2); + valid=true; + } + } + else + { + if(mImageExtent->IsCompatible(*ie)) + { + if(mImageExtent->GetDimension()==max_dim && mImageExtent->Get(max_dim)>2) + { + std::stringstream out; + out<<"Cannot add this image to selection : would result in a "<GetDimension()+1<<"D image!"; + mMessage=out.str(); + valid=false; + } + else if(max_dim<3) + { + std::stringstream out; + out<<"Selecting "<GetDimension()<<"D images is not allowed !"; + mMessage=out.str(); + valid=false; + } + else if(min_dim==3 && (ie->Get(2)+mImageExtent->Get(2))<2) + { + std::stringstream out; + out << "Cannot build the selection as it would result in a "; + out << mImageExtent->GetDimension(); + out << "D image, and the minimum is "; + out << min_dim; + out << "D!"; + mMessage=out.str(); + valid=false; + } + else + { + mImageExtent->Add(*ie); + std::stringstream out; + out << mImageExtent->GetDimension() << "D image " << mImageExtent->Get(0) << "x"<< mImageExtent->Get(1) << "x"<< mImageExtent->Get(2) <<" selected"; + mMessage = out.str(); + } + } + else + { + mMessage="The selected images are not compatible."; + valid=false; + } + } + } + + modifyValidationSignal(valid); + SetMessage(mMessage); + return valid; + } + + //====================================================================== + void GimmickView::modifyValidationSignal(bool ivalid) + { + mValidationSignal(ivalid); + } + + //====================================================================== + ///Reads Images (Non Threaded) +void GimmickView::ReadImagesNotThreaded(std::vector& s, std::vector im, int dimension) +{ + mReader.Stop(); +/* remember! + +#define GIMMICK_NO_IMAGE_SELECTION 0 +#define GIMMICK_2D_IMAGE_SELECTION 2 +#define GIMMICK_3D_IMAGE_SELECTION 3 +#define GIMMICK_4D_IMAGE_SELECTION 4 + +#define NATIVE 0 +#define _2D 2 +#define _3D 3 + +*/ + // Create the output data + if (im.size()==1) + { + vtkImageData * out=vtkImageData::New(); + out->ShallowCopy(mReader.GetImage(im.front())); + s.push_back(out); + } + else if (im.size()>1) // Test inutile ? JPR + { + vtkImageData* first = mReader.GetImage( im.front()); + if (dimension == 2) + { + // n3D + std::vector::iterator it; + for (it=im.begin(); it!=im.end(); ++it) + { + vtkImageData* out = vtkImageData::New(); + out->ShallowCopy(mReader.GetImage(*it)); + s.push_back(out); + } + } + else + { + // n*2D to 3D + vtkImageData* out = vtkImageData::New(); +// out->CopyStructure(first); + out->SetScalarType(first->GetScalarType()); + out->SetNumberOfScalarComponents(first->GetNumberOfScalarComponents()); + int ext[6]; + //first->GetExtent(ext); // JPR + first->GetWholeExtent(ext); // renvoie egalement 0,0 en Z // JPR + + if(ext[5] == 0) + { + ext[5] = im.size()-1; + } + else + { + ext[5] = ext[5] * im.size()-1; // to deal with multiframes - JPR + } + out->SetExtent(ext); + + // LG : TODO : Z Spacing ? + + int dim[3]; + first->GetDimensions(dim); + + out->SetDimensions(dim[0], dim[1], im.size() ); + out->AllocateScalars(); + out->Update(); + + unsigned long imsize = dim[0] * dim[1]; + imsize = imsize * dim[2] ; // deal with multiframes // JPR + + +//EED 03-11-2009 + // differents formats char , short, etc... + // differents components 1..3 ex. jpg ->RGB 3 + imsize = imsize * first->GetScalarSize() * first->GetNumberOfScalarComponents(); + + + // Order the file name vector + + double spc[3]; + first->GetSpacing(spc); + spc[2]=OrderTheFileNameVector(im); + + out->SetSpacing(spc); + + int slice = 0; + std::vector::iterator it; + + for (it=im.begin(); it!=im.end(); ++it) + { + vtkImageData* cur = mReader.GetImage( (*it) ); + memcpy(out->GetScalarPointer(0,0,slice), cur->GetScalarPointer(0,0,0), imsize); + slice++; + } + s.push_back(out); + + } // dimension == 3 + + } // size >1 + +} + //====================================================================== + +#if defined(USE_GDCM) + double GimmickView::OrderTheFileNameVector(std::vector &im) +{ + double spacing=1; + typedef std::vector FileList; + FileList fileVector; + //GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New(); + GDCM_NAME_SPACE::SerieHelper *sh = GDCM_NAME_SPACE::SerieHelper::New(); + std::vector lstAux; + std::vector::iterator it; + for (it=im.begin(); it!=im.end(); ++it) + { + ///\TODO liberer les GDCM_NAME_SPACE::File a la fin! // JPR + GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New(); + f->SetFileName(*it); + f->Load(); + if (f->IsReadable()) + { + fileVector.push_back(f); + } else { + lstAux.push_back(*it); + } + } // for + + + if ((fileVector.size()>1) && (sh->IsCoherent( &fileVector ))) + { + sh->OrderFileList(&fileVector); + spacing= sh->GetZSpacing(); + im.clear(); + int i; + for (i=0; iGetFileName() ); + } + for (i=0; i &im) + { + return 1; + } +#endif +//====================================================================== + + +void GimmickView::ReadImagesNotThreadedInVector(std::vector& s, std::vector im, int dimension) +{ + // Create the output data + if (im.size()==1) + { + // Only one image : give it + vtkImageData* out = vtkImageData::New(); + GimmickDebugMessage(3, "State Check: Full Filename: " + <ShallowCopy(mReader.GetImage(im.front())); + s.push_back( out ); + } + else if (im.size()>1) // Test inutile ? JPR + { + vtkImageData* first = mReader.GetImage( im.front()); + if (dimension == 2) + { + // n3D + std::vector::iterator it; + for (it=im.begin(); it!=im.end(); ++it) + { + vtkImageData* out = vtkImageData::New(); + out->ShallowCopy(mReader.GetImage(*it)); + s.push_back(out); + } + } + else + { + // n2D to 3D // NO! + // n *2D + T in a vector : + + std::vector::iterator it; + for (it=im.begin(); it!=im.end(); ++it) + { + vtkImageData* out = mReader.GetImage( (*it)); + s.push_back(out); + } + } + } +} + //====================================================================== + + //====================================================================== + ///Requests the reading of an image + void GimmickView::RequestReading(tree::Node* n, + int prio, int selection_index, boost::shared_ptr p) + { + if(!mReaderStarted) + { + mReader.Start(); + mReaderStarted=true; + } + ImageEventType t(n,selection_index); + t.pointerHolder = p; + mImageEventMap[n->GetAttribute("FullFileName")] = t; + mReader.Request(this,n->GetAttribute("FullFileName"),prio); + } + //====================================================================== + + //====================================================================== + void GimmickView:: + OnMultiThreadImageReaderEvent(const std::string& filename, + MultiThreadImageReaderUser::EventType e, + vtkImageData* image) + { + GimmickDebugMessage(7, + "MultiThreadImageReader event : "< 0 + if(mImageEventMap.size()>0){ + i = mImageEventMap.find(filename); + if (i!=mImageEventMap.end()) + { + GimmickDebugMessage(5, + "Putting image of file '"<second); + ie.image = image; + ie.pointerHolder->Set(ie.image); + //mImageEventMap.erase(i); + } + } + } + else if (e==Error) + { + std::string mess="ERROR: MultiThreadImageReader: Cannot read image in file "; + mess+=filename; + mess+="\n"; + GimmickMessage(1,mess); + ImageEventTypeMap::iterator i; + i = mImageEventMap.find(filename); + if (i!=mImageEventMap.end()) + { + ImageEventType ie(i->second); + ie.image = image; + ie.pointerHolder->Set(GetDefaultImage()); + //mImageEventMap.erase(i); + } + } + + else if (e==ImageUnloaded) + { + std::string mess="Unloaded image in file "; + mess+=filename; + mess+="\n"; + GimmickMessage(1,mess); + ImageEventTypeMap::iterator i; + i = mImageEventMap.find(filename); + if (i!=mImageEventMap.end()) + { + ImageEventType ie(i->second); + ie.image = image; + ie.pointerHolder->Set(GetDefaultImage()); + //mImageEventMap.erase(i); + } + } + } + + //==================================================================== + + //==================================================================== + void GimmickView::ConnectValidationObserver(ValidationCallbackType callback) + { + mValidationSignal.connect(callback); + } + +} // EO namespace creaImageIO + +////////////////////////////////////////////////////////////////////// +//void GimmickView::Anonymize(std::vector i_filenames, int type) +// { + //if(type == 0) + //{ + // // Get private key/certificate + // gdcm::CryptographicMessageSyntax cms; + //if( !dumb_mode ) + // { + // if( !GetRSAKeys(cms, rsa_path.c_str(), cert_path.c_str() ) ) + // { + // return 1; + // } + // cms.SetCipherType( ciphertype ); + // } + + //// Setup gdcm::Anonymizer + //gdcm::Anonymizer anon; + //if( !dumb_mode ) + // anon.SetCryptographicMessageSyntax( &cms ); + + //if( dumb_mode ) + // { + // for(unsigned int i = 0; i < nfiles; ++i) + // { + // const char *in = filenames[i].c_str(); + // const char *out = outfilenames[i].c_str(); + // if( !AnonymizeOneFileDumb(anon, in, out, empty_tags, remove_tags, replace_tags_value) ) + // { + // //std::cerr << "Could not anonymize: " << in << std::endl; + // return 1; + // } + // } + // } + //else + // { + // for(unsigned int i = 0; i < nfiles; ++i) + // { + // const char *in = filenames[i].c_str(); + // const char *out = outfilenames[i].c_str(); + // if( !AnonymizeOneFile(anon, in, out) ) + // { + // //std::cerr << "Could not anonymize: " << in << std::endl; + // return 1; + // } + // } + // } +// } \ No newline at end of file diff --git a/src/creaImageIOGimmickView.h b/src/creaImageIOGimmickView.h new file mode 100644 index 0000000..beb7bf2 --- /dev/null +++ b/src/creaImageIOGimmickView.h @@ -0,0 +1,240 @@ +#ifndef __creaImageIOGimmickView_h_INCLUDED__ +#define __creaImageIOGimmickView_h_INCLUDED__ + +#include +#include +#include +#include + +//#include +#include +#include + +// Signal/slot mechanism for progress events +#include +#include + +#define GIMMICK_NO_IMAGE_SELECTION 0 +#define GIMMICK_2D_IMAGE_SELECTION 2 +#define GIMMICK_3D_IMAGE_SELECTION 3 +#define GIMMICK_4D_IMAGE_SELECTION 4 + +#define NATIVE 0 +#define _2D 2 +#define _3D 3 + +namespace creaImageIO +{ + /** + * \ingroup View + */ + + class ImageExtent; + //===================================================================== + + //===================================================================== + ///Abstract class that handles views, attributes and previews (GUI) for Gimmick. + class GimmickView: public MultiThreadImageReaderUser + { + public: + /// Ctor + GimmickView(boost::shared_ptr, int number_of_threads = 0 ); + /// Virtual destructor + virtual ~GimmickView(); + /// Initializes the view : + /// Creates the TreeViews for all the TreeHandler of the Controller + /// + virtual void Initialize(); + + /// Type of map from View name to TreeView* + /// (This map is equivalent for Views of the TreeHandlerMap of Gimmick) + typedef std::map TreeViewMapType; + + /// Returns the TreeViewMap (ref) + TreeViewMapType& GetTreeViewMap() { return mTreeViewMap; } + /// Returns the TreeViewMap (const ref) + const TreeViewMapType& GetTreeViewMap() const + { return mTreeViewMap; } + + /// Finalize + virtual void Finalize(); + + ///Returns the maximal priority + int GetMaximalPriority(){return mReader.GetMaximalPriority();} + + ///Adds the selected Images to the given vector + virtual void GetSelectedImagesInVector(std::vector& s, int dim) + { GimmickError("INTERNAL ERROR : GetSelectedImagesInVector not implemented"); } + + ///Adds the selected Images to the given vector and validates to see if they comply with the given parameter (4D) + virtual void GetSelectedImages(std::vector& s, int dim) + { GimmickError("INTERNAL ERROR : GetSelectedImages not implemented"); } + + virtual void GetSelectedFiles(std::vector& s) + { GimmickError("INTERNAL ERROR : GetSelectedFiles not implemented"); } + + virtual void GetImages(int dim, std::vector files, std::vector& s) + { GimmickError("INTERNAL ERROR : GetImages not implemented"); } + + virtual void OnSelectionChange(const std::vector& s, bool isSelection, int selection, bool mProcess) + { GimmickError("INTERNAL ERROR : OnSelectionChange not implemented"); } + + virtual void ClearSelection() + { GimmickError("INTERNAL ERROR : ClearSelection not implemented"); } + + ///Adds a file to ignore + virtual void AddIgnoreFile(tree::Node* toRemove) + { GimmickError("INTERNAL ERROR : AddIgnoreFile not implemented"); } + + ///Copies selected files + virtual void CopyFiles(const std::vector& filenames) + { GimmickError("INTERNAL ERROR : CopyFiles not implemented"); } + + ///Edits the fields of a given node + virtual void CreateEditFieldsDialog(tree::Node* node, std::vector names, std::vector keys) + { GimmickError("INTERNAL ERROR : EditFields not implemented"); } + + /// Anonymize or de-anonymize data + void Anonymize(std::vector i_filenames, int type); + + + /// Display all Dicom Tags + virtual void DumpTags(const std::string filename) + {GimmickError("INTERNAL ERROR : DumpTags not implemented"); } + + ///Edits the fields of a given node + virtual void ExportToStorage(const std::vector keys) + { GimmickError("INTERNAL ERROR : ExportToStorage not implemented"); } + + ///Copies selected files + virtual void SaveAs(const std::vector& filenames) + { GimmickError("INTERNAL ERROR : SaveAs not implemented"); } + + /// No selected image + bool NoValidateSelected(); + + ///Validates the dimension compliance of the images with the maximum and minimum given, and between their sizes + bool ValidateSelected (tree::Node* sel, int min_dim, int max_dim); + + ///Reads the vector of images, builds it in the dimension required and returns them in the supplied vector. + void ReadImagesNotThreaded(std::vector& s,std::vector files, int dim); + + ///Reads the vector of images, builds it in the dimension required and returns them in the supplied vector. + void ReadImagesNotThreadedInVector(std::vector& s,std::vector files, int dim); + +///Requests the reading of an image with priority and index in the + /// current selection (-1 if not in selection) + //void RequestReading(tree::Node* n, int prio, int selection_index , ImagePointerHolder *p); + void RequestReading(tree::Node* n, int prio, int selection_index , boost::shared_ptr p); + + ///Obtains the message of the state + std::string GetMessage(){return mMess;} + + ///Obtains the message of the state + void SetMessage(std::string mess){mMess=mess;} + + ///Resets the data of the extent and begins a new selection + void ResetExtent(); + + /// Create the tree views + void CreateTreeViews(); + + /// Create a tree view with a given name + void CreateSingleTreeView(std::string &i_name); + + /// Create the tree view for TreeHandler provided + virtual void CreateTreeView( TreeHandler* ) + { GimmickError("INTERNAL ERROR : CreateTreeView not implemented"); } + + /// Updates the TreeView of given name from level l to bottom + /// (calls the virtual method TreeView::UpdateLevel(l)) + virtual void UpdateTreeViewLevel(const std::string&, int l); + + // Multi-thread image reader callback + void OnMultiThreadImageReaderEvent(const std::string& filename, + MultiThreadImageReaderUser::EventType t, + vtkImageData* image); + + vtkImageData* GetDefaultImage() { return mReader.GetImage(""); } + + //============================================= + typedef boost::signal ValidationSignalType; + typedef ValidationSignalType::slot_function_type ValidationCallbackType; + //============================================= + + //================================================================== + /// Adds the function f to the list of functions to call + /// when the addition progresses. + /// f is of type ProgressCallbackType which is: + /// void (*ProgressCallbackType)(Progress&) + /// To pass a member function 'f' of an instance 'c' of a class 'C' + /// as callback you have to 'bind' it, i.e. call: + /// ConnectValidationObserver ( boost::bind( &C::f , c, _1 ) ); + void ConnectValidationObserver(ValidationCallbackType callback); + //================================================================== + + void modifyValidationSignal(bool ivalid); + + private: + + double OrderTheFileNameVector(std::vector &im); + + /// Controller which manages the interaction with the model + boost::shared_ptr< Gimmick> mGimmick; + + /// The views + TreeViewMapType mTreeViewMap; + + /// The message that results from the validation + std::string mMess; + + /// Multi-thread image reader + MultiThreadImageReader mReader; + + /// Internal type of image reading event + /// If the image pointer is non null then the image is available (loaded) + /// else it has been unloaded + struct ImageEventType + { + ImageEventType( tree::Node* no = 0, + int sel_index = -1) +// ImagePointerHolder* ph= 0) + : node(no), index(sel_index){}//, pointerHolder(ph){} + ImageEventType(vtkImageData* im ) + : image(im) {} + tree::Node* node; + vtkImageData* image; + int index; + boost::shared_ptr pointerHolder; + }; + typedef std::map ImageEventTypeMap; + /// Map of images' names to ImageEventType + /// Used to associated a filename to a the data of a request + ImageEventTypeMap mImageEventMap; + + // queue of image event + typedef std::deque ImageEventQueueType; + + //ImageEventQueueType mImageEventQueue; + + ///The current image extent + boost::shared_ptr mImageExtent; + + ///The validation signal + ValidationSignalType mValidationSignal; + + ///Boolean that determines if the selection is valid + bool valid; + + ///Boolean that determines if the reader has been started + bool mReaderStarted; + + }; + // EO class GimmickView + //===================================================================== + + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src/creaImageIOImagePointerHolder.h b/src/creaImageIOImagePointerHolder.h new file mode 100644 index 0000000..98251b6 --- /dev/null +++ b/src/creaImageIOImagePointerHolder.h @@ -0,0 +1,29 @@ +#ifndef __creaImageIOImagePointerHolder_INCLUDED__ +#define __creaImageIOImagePointerHolder_INCLUDED__ + +#include +#include +#include + + +namespace creaImageIO +{ + // The class that holds the pointer to the images to show + class ImagePointerHolder + { + public: + ImagePointerHolder(vtkImageData* im): + mImage(im) + {} + ImagePointerHolder(); + void Set(vtkImageData* im){boost::mutex::scoped_lock lock(mMutex);mImage=im;} + vtkImageData* Get(){boost::mutex::scoped_lock lock(mMutex);return mImage;} + private: + vtkImageData* mImage; + /// The mutex + boost::mutex mMutex; + }; +} +#endif + + diff --git a/src/creaImageIOImageReader.cpp b/src/creaImageIOImageReader.cpp new file mode 100644 index 0000000..4b67e04 --- /dev/null +++ b/src/creaImageIOImageReader.cpp @@ -0,0 +1,208 @@ +#include +#include +#include + +#include +#if defined (USE_GDCM) + #include +#endif +#if defined(USE_GDCM2) + #include +#endif +#include +#include +#include +#include +#include +#include +#include +//#include + + +#include "boost/filesystem/path.hpp" + +namespace creaImageIO +{ + + + + + + //===================================================================== + ImageReader::ImageReader() + : + mUnreadableImage(0), + mLastFilename("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + { + // std::cout << "#### ImageReader::ImageReader()"<(new VtkImageReader(vtkPNGReader::New(), "PNG", ".png"))); + Register(boost::shared_ptr(new VtkImageReader(vtkTIFFReader::New(), "JPEG", ".jpeg"))); + Register(boost::shared_ptr(new VtkImageReader(vtkJPEGReader::New()))); + Register(boost::shared_ptr(new VtkImageReader(vtkBMPReader::New()))); + Register(boost::shared_ptr(new VtkImageReader(vtkSLCReader::New()))); + Register(boost::shared_ptr(new VtkImageReader(vtkMetaImageReader::New(),"MHD",".mhd"))); + // Register(new VtkImageReader(vtkGESignalReader::New())); + Register(boost::shared_ptr(new DicomImageReader)); + Register(boost::shared_ptr(new UltrasonixImageReader)); + + UnRegister(".txt"); + + mUnreadableImage = vtkImageData::New(); + int dim[3]; + dim[0] = dim[1] = 128; + dim[2] = 1; + mUnreadableImage->SetDimensions ( dim ); + mUnreadableImage->SetScalarTypeToUnsignedChar(); + mUnreadableImage->AllocateScalars(); + for (int i=0;iSetScalarComponentFromFloat(i,j,0,0,0); + for (int i=0;iSetScalarComponentFromFloat(i,i,0,0,255); + mUnreadableImage->SetScalarComponentFromFloat(dim[0]-1-i,i,0,0,255); + } + + + + } + //===================================================================== + + //===================================================================== + ImageReader::~ImageReader() + { + + // for (i=mReader.begin(); i!=mReader.end(); i++) + // { + //delete (*i); + // } +// mReader.clear(); + if (mUnreadableImage!=0) + { + mUnreadableImage->Delete(); + mUnreadableImage = 0; + } + } + //===================================================================== + + //===================================================================== + void ImageReader::Register(boost::shared_ptr r) + { + mReader.push_back(r); + + } + + void ImageReader::UnRegister(const std::string i_val) + { + mUnReader.push_back(i_val); + + } + //===================================================================== + + //===================================================================== + // Returns true iff the file is readable + bool ImageReader::ShallNotRead( const std::string& filename ) + { + bool ok = true; + if(filename != "") + { + std::vector::iterator i ; + for (i=mUnReader.begin(); i!=mUnReader.end(); i++) + { + + if ( (*i).c_str() == filename) + { + ok = false; + break; + } + } + } + return ok; + + } + + + //===================================================================== + // Returns true iff the file is readable + bool ImageReader::CanRead( const std::string& filename ) + { + bool ok = false; + + if( !ShallNotRead(filename)) + { + return ok; + } + if(filename != "") + { + std::vector >::iterator i; + for (i=mReader.begin(); i!=mReader.end(); i++) + { + ok = (*i)->CanRead(filename); + if (ok) + { + mLastFilename = filename; + mLastReader = *i; + break; + } + } + } + return ok; + + } + //===================================================================== + + //===================================================================== + // Reads the file (CanRead must be called before : no test here) + vtkImageData* ImageReader::ReadImage( const std::string& filename) + { + if (mLastFilename!=filename) + { + if (!CanRead(filename)) + { + vtkImageData* im = vtkImageData::New(); + im->ShallowCopy(mUnreadableImage); + return im; + } + } + vtkImageData* i = mLastReader->ReadImage(mLastFilename); + if (i==0) + { + i = vtkImageData::New(); + i->ShallowCopy(mUnreadableImage); + } + return i; + } + //===================================================================== + + + //===================================================================== + void ImageReader::ReadAttributes(const std::string& filename, + std::map& attr) + { + if (mLastFilename!=filename) + { + if (!CanRead(filename)) + { + return; + } + } + mLastReader->ReadAttributes(mLastFilename,attr); + } + //===================================================================== + + + //===================================================================== + /// Pushes back all kwown extensions (without dot) in the vector given + void ImageReader::PushBackExtensions(std::vector& v) + { + std::vector >::iterator i; + for (i=mReader.begin(); i!=mReader.end(); i++) + { + (*i)->PushBackExtensions(v); + } + } + //===================================================================== + +} // namespace creaImageIO diff --git a/src/creaImageIOImageReader.h b/src/creaImageIOImageReader.h new file mode 100644 index 0000000..ab9fbe7 --- /dev/null +++ b/src/creaImageIOImageReader.h @@ -0,0 +1,70 @@ +#ifndef __creaImageIOImageReader_h_INCLUDED__ +#define __creaImageIOImageReader_h_INCLUDED__ + +#include +#include + +namespace creaImageIO +{ + + + /** + * \ingroup IO + * \brief Generic image reader which uses all the specific concrete image reader of the lib (tif, jpg, dicom, ...) + */ + class CREAIMAGEIO_EXPORT ImageReader : virtual public AbstractImageReader + { + public: + ImageReader(); + ~ImageReader(); + + /// Pushes back all kwown extensions (without dot) in the vector given + void PushBackExtensions(std::vector&); + + /// Returns true iff the file is readable + bool CanRead( const std::string& filename); + /// Reads and returns the image data. + /// Returns an "Unreadable image" picture if fails + vtkImageData* ReadImage( const std::string& filename); + + /// Reads the attributes of the image. + /// Requested attributes names are provided as keys + /// in a string to string map + /// On return, the values of the map are the values + /// of the attributes (empty string if not available). + void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + + /// Exclude specific readers + /// TO DO... + + + protected: + + /// Register a reader + void Register( boost::shared_ptr ); + + std::vector > mReader; + vtkImageData* mUnreadableImage; + + std::string mLastFilename; + boost::shared_ptr mLastReader; + + bool ShallNotRead( const std::string& filename ); + + void UnRegister(const std::string i_val); + + std::vector mUnReader; + + private: + + }; // class ImageReader + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOImageReader_h_INCLUDED__ diff --git a/src/creaImageIOIndexedHeap.h b/src/creaImageIOIndexedHeap.h new file mode 100644 index 0000000..8568b67 --- /dev/null +++ b/src/creaImageIOIndexedHeap.h @@ -0,0 +1,139 @@ +/* + +*/ +/*! \file + \brief Indexed priority queues handled by binary trees. +*/ +#ifndef __creaImageIOIndexedHeap_h_INCLUDED__ +#define __creaImageIOIndexedHeap_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + + + template */, + class Indexer/*=Index */> + class IndexedHeap ; + template < class T, + class C, + class I> + std::ostream& operator << (std::ostream&, const IndexedHeap& ); + + + //template , class Index=IndexIndex > class SlicedIndexedHeap; + + + //======================================================================== + /// \brief Indexed priority queues handled by binary trees. + /// + /// Heap Allows : + /// - log(n) insertion + /// - constant time acces to the first element + /// - log(n) removal of the first element + /// - log(n) priority change of a random element + /// Indexation Allows : + /// - constant time access to a random element (for priority change) + /// + /// The Indexer is an unary_function whose operator()(T& t) + /// returns a reference on an integer which + /// is maintained by the IndexedHeap in order to provide at any time + /// the position of the object t in the Heap + /// (hence allowing constant time random access to an object). + template */, + class Indexer /*=Index*/> + class IndexedHeap + { + // friend class SlicedIndexedHeap; + public : + + //====================================================================== + /// Constructor + IndexedHeap () {} + /// Constructor + IndexedHeap ( const Comparator& comp, const Indexer& ind ) ; + /// Destructor + ~IndexedHeap() { } + /// Sets the comparator + void set( const Comparator& comp ); + /// Sets the Index + void set( const Indexer& ind ); + //====================================================================== + + //====================================================================== + /// inserts an element in the Heap and returns its position + int insert(T); + /// return a reference on the first element of the Heap + T& top(); + /// return a constant reference on the first element of the Heap + const T& top() const; + /// removes and returns the first element of the Heap + T remove_top(); + /// removes and returns the nth element + T remove(int n); + /// returns the size of the Heap + inline int size() const {return m_p.size(); } + /// empties the Heap + void clear(); + //====================================================================== + + //====================================================================== + /// returns a constant on the stack of elements + const std::vector & stack() const {return m_p;} + /// returns a reference to the ith element of the stack + T& operator [] (int i) { return m_p[i];} + /// returns a constant reference to the ith element of the stack + const T& operator [] (int i) const { return m_p[i];} + /// returns the index (position) of t + inline int index(T& t) { return (*m_i)(t); } + //====================================================================== + + //====================================================================== + /// returns the position of the father of i + inline int father( int i ) const; + /// returns the position of the right son of i + inline int rightson( int i ) const; + /// returns the position of the leftson of i + inline int leftson( int i ) const; + //====================================================================== + /// swaps ith and jth elements + inline void swap(int i, int j); + /// remonte un element dans le tas tant qu'il n'est pas a sa place. + /// renvoie la position finale + inline int upsort(int); + /// descend un element dans le tas tant qu'il n'est pas a sa place. + /// renvoie la position finale + inline int downsort(int); + //====================================================================== + + protected : + /// binary tree handled by a vector + std::vector m_p; + /// comparator pointer + const Comparator* m_c; + /// Index pointer + const Indexer* m_i; + }; + //======================================================================== + // EO class IndexedHeap + //======================================================================== + + +#include "creaImageIOIndexedHeap.txx" + + +}; +//=========================================================================== +// EO namespace creaImageIO +//=========================================================================== + + + +//=========================================================================== +// EOF +//=========================================================================== +#endif diff --git a/src/creaImageIOIndexedHeap.txx b/src/creaImageIOIndexedHeap.txx new file mode 100644 index 0000000..b5b5753 --- /dev/null +++ b/src/creaImageIOIndexedHeap.txx @@ -0,0 +1,235 @@ +/* + +*/ +/*! \file + \brief Code of IndexedHeap +*/ +//============================================================================ +template +std::ostream& operator << (std::ostream& s, const IndexedHeap& t) +{ + s << "["; + for (int i=0; i +IndexedHeap::IndexedHeap ( const CT& comp, const IT& index ) + : m_c(&comp), m_i(&index) +{} +//=========================================================== + + +//=========================================================== +template +void IndexedHeap::set( const CT& comp ) +{ + m_c = ∁ +} +//=========================================================== + +//=========================================================== +template +void IndexedHeap::set ( const IT& index ) +{ + m_i = &index; +} +//=========================================================== + + +//=========================================================== +template +int IndexedHeap::insert(T t) +{ + m_p.push_back(t); + (*m_i)(t) = size()-1; + return upsort(size()-1); +} +//=========================================================== + +//=========================================================== +template +T& IndexedHeap::top() +{ + // lglASSERT( size() > 0) + return m_p.front(); +} +//=========================================================== + + +//=========================================================== +template +const T& IndexedHeap::top() const +{ + // lglASSERT( size() > 0) + return m_p.front(); +} +//=========================================================== + +//=========================================================== +template +T IndexedHeap::remove_top() +{ + // lglASSERT( size() > 0 ) + T f(m_p[0]); + (*m_i)(f) = -1; + T last = m_p.back(); + m_p.pop_back(); + if (m_p.size()>0) + { + m_p[0] = last; + (*m_i)(last) = 0; + downsort(0); + } + return f; +} +//============================================================================ + + + +//============================================================================ +template +T IndexedHeap::remove(int n) +{ + // lglASSERT ( (n>=0)&&(n0) + { + m_p[n] = last; + (*m_i)(last) = n; + downsort(n); + } + return f; +} +//============================================================================ + + +//============================================================================ +template +void IndexedHeap::clear() +{ + for (typename std::vector::iterator i=m_p.begin(); i!=m_p.end(); ++i) + { + (*m_i)(*i)=-1; + } + m_p.clear(); +} +//============================================================================ + + +//============================================================================ +template +int IndexedHeap::father( int i) const +{ + return ((i-1)/2); +} +//============================================================================ + +//============================================================================ +template +int IndexedHeap::rightson( int i) const +{ + return (i*2+2); +} +//============================================================================ + +//============================================================================ +template +int IndexedHeap::leftson( int i) const +{ + return (i*2+1); +} +//============================================================================ + +//============================================================================ +template +void IndexedHeap::swap(int i, int j) +{ + T tmp = m_p[i]; + m_p[i] = m_p[j]; + m_p[j] = tmp; + // update indices + (*m_i)(m_p[i]) = i; + (*m_i)(m_p[j]) = j; +} +//============================================================================ + + + +//============================================================================ +template +int IndexedHeap::upsort(int i) +{ + //if (i==0) return i; + int j = father(i); + while ((i>0)&&(*m_c)(m_p[i],m_p[j])) + { + swap(i,j); + i = j; + j = father(j); + } + return i; +} +//============================================================================ + + +//============================================================================ +template +int IndexedHeap::downsort(int i) +{ + do + { + + unsigned int ls = leftson(i); + if (ls +#include +#include "boost/filesystem.hpp" +#include +#include + +namespace fs = boost::filesystem; +using boost::filesystem::path; + +using namespace crea; + +namespace creaImageIO +{ + //===================================================================== + // CTor + Listener::Listener() + { + + boost::mutex::scoped_lock lock(mMutex); + GimmickDebugMessage(6,"Listener::Listener" + < +#include +#include +#include +// Signal/slot mechanism +#include +#include +#include + +namespace creaImageIO +{ + + class Listener : public wxThread + { + public: + /// Ctors + Listener(); + /// Dtor + virtual ~Listener(); + ///Thread method that is executed once create is called + void* Entry(); + ///Thread method called upon exiting + void OnExit(); + ///Sets the new state of adding files + void SetAddFilesState(bool addFiles){boost::mutex::scoped_lock lock(mMutex);mAddFiles=addFiles;} + ///Sets the new state of removing files + void SetRemoveFilesState(bool removeFiles){boost::mutex::scoped_lock lock(mMutex);mRemoveFiles=removeFiles;} + ///Sets the new monitored drive + void SetMonitoredDrive(const std::string& dr){boost::mutex::scoped_lock lock(mMutex);mDrive=dr;} + ///Puts the name of the monitored drive in the given string + void GetMonitoredDrive(std::string& drive){drive=mDrive;} + + ///Related with signals + //============================================= + typedef boost::signal MountingSignalType; + typedef MountingSignalType::slot_function_type MountingCallbackType; + //============================================= + + //================================================================== + void ConnectObserver(MountingCallbackType callback); + //================================================================== + + ///Sends a boost::signal to alert that the drive has changed its state (mounted/unmounted) + void SendSignal(bool ivalid); + + + private: + /// The mutex + boost::mutex mMutex; + /// Boolean that declares if the files that are read on CD mount should be added + bool mAddFiles; + /// Boolean that declares if, on CD unmount, the files that were in the drive should be removed + bool mRemoveFiles; + ///Boolean that declares if a unit has been mounted + bool mMounted; + ///The monitored drive + std::string mDrive; + ///The validation signal + MountingSignalType mMountingSignal; + }; + +} + +#endif diff --git a/src/creaImageIOMultiThreadImageReader.cpp b/src/creaImageIOMultiThreadImageReader.cpp new file mode 100644 index 0000000..3ee50de --- /dev/null +++ b/src/creaImageIOMultiThreadImageReader.cpp @@ -0,0 +1,638 @@ +#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) + { + wxMutexLocker lock(mMultiThreadImageReaderUserMutex); + + this->OnMultiThreadImageReaderEvent(filename,type,image); + } + //===================================================================== + + //===================================================================== + class ThreadedImageReader: public wxThread + { + public: + ThreadedImageReader(MultiThreadImageReader* tir) : + mMultiThreadImageReader(tir) + {} + + void* Entry(); + void OnExit(); + + vtkImageData* Read(const std::string& filename); + + struct deleter + { + void operator()(ThreadedImageReader* p) + { + p->Delete(); + } + }; + 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)->Create(); + if ( (*i)->Run() != wxTHREAD_NO_ERROR ) + { + std::cout << "ERROR starting a thread"<< std::endl; + return false; + } + else + { + std::cout << " ===> Thread "<<(*i)->GetCurrentId() + <<" successfully created"<< std::endl; + + } + } + wxMutexLocker locker(GetMultiThreadImageReaderUserMutex()); + // std::cout << "EO Start : #Threads running = " + // << mNumberOfThreadedReadersRunning< Thread "<<(*i)->GetCurrentId() + <<" successfully stopped"<< std::endl; + if((*i)->IsAlive()) + {(*i)->Pause(); + (*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()); + } + } + //===================================================================== + + //===================================================================== + void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user, + const std::string& filename, + int priority ) + { + wxMutexLocker 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 (!TestDestroy()) + { + //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/creaImageIOMultiThreadImageReader.h b/src/creaImageIOMultiThreadImageReader.h new file mode 100644 index 0000000..c5a9ef9 --- /dev/null +++ b/src/creaImageIOMultiThreadImageReader.h @@ -0,0 +1,254 @@ +#ifndef __creaImageIOThreadedImageReader_h_INCLUDED__ +#define __creaImageIOThreadedImageReader_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 wxMutex& GetMultiThreadImageReaderUserMutex() + { return mMultiThreadImageReaderUserMutex; } + private: + /// + void MultiThreadImageReaderSendEvent( const std::string& filename, + EventType type, + vtkImageData* image); + wxMutex 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); + + 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; } + 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 __creaImageIOThreadedImageReader_h_INCLUDED__ diff --git a/src/creaImageIOPACSConnection.cpp b/src/creaImageIOPACSConnection.cpp new file mode 100644 index 0000000..ffa6571 --- /dev/null +++ b/src/creaImageIOPACSConnection.cpp @@ -0,0 +1,49 @@ + +#include +#include +#include +#include +//#include + +//using boost::asio::ip::tcp; + +enum { max_length = 3086 }; +using namespace std; +namespace creaImageIO +{ + PACSConnection::PACSConnection(std::string command) + { + /* + try + { + + boost::asio::io_service io_service; + + tcp::resolver resolver(io_service); + tcp::resolver::query query(tcp::v4(), "localhost", "3306"); + tcp::resolver::iterator iterator = resolver.resolve(query); + + tcp::socket s(io_service); + s.connect(*iterator); + + size_t request_length = strlen(command.c_str()); + boost::asio::write(s, boost::asio::buffer(command.c_str(), request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + */ + } + +} + + + diff --git a/src/creaImageIOPACSConnection.h b/src/creaImageIOPACSConnection.h new file mode 100644 index 0000000..02e659a --- /dev/null +++ b/src/creaImageIOPACSConnection.h @@ -0,0 +1,17 @@ +#ifndef __creaImageIOPACSConnection_h_INCLUDED__ +#define __creaImageIOPACSConnection_h_INCLUDED__ +#include + +namespace creaImageIO +{ +class PACSConnection + { + public: + /// Ctor + PACSConnection(std::string command); + };// EO PACSConnection +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src/creaImageIOSQLiteTreeHandler.cpp b/src/creaImageIOSQLiteTreeHandler.cpp new file mode 100644 index 0000000..627db08 --- /dev/null +++ b/src/creaImageIOSQLiteTreeHandler.cpp @@ -0,0 +1,1017 @@ +#include +#include +#include +#include + +#include "CppSQLite3.h" + +#include + +#include + +#include +#include +using namespace crea; + + + +namespace creaImageIO +{ + using namespace tree; + + + //============================================================= + SQLiteTreeHandler::SQLiteTreeHandler(const std::string& filename) + : mFileName(filename) + { + mDB = new CppSQLite3DB; + mIsAdding=false; + //GimmickMessage(1,"SQLite version : " <SQLiteVersion())<< std::endl); + } + //============================================================= + + //============================================================= + SQLiteTreeHandler::~SQLiteTreeHandler() + { + delete mDB; + } + //============================================================= + + + //===================================================================== + + + //===================================================================== + bool SQLiteTreeHandler::Open(bool writable) + { + // std::cout << "***> SQLiteTreeHandler::Open('"< SQLiteTreeHandler::New('"<GetLevel()+1); + + } + //===================================================================== + + + //===================================================================== + bool SQLiteTreeHandler::Remove(tree::Node* node) + { + DBRecursiveRemoveNode(node); + + // std::cout << "DELETE"<GetParent(); + if (parent) + { + int nC = parent->RemoveChildrenFromList(node); + if(nC>0 && parent->GetLevel()>0) + { + std::stringstream out; + out <GetLevel()>0) + { + Remove(parent); + } + // std::cout << "DELETE OK"<execQuery(QUER.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite query '"<execDML(UP.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite update '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError("Opening '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError(e.errorCode() << ":" + << e.errorMessage() <0, i.e. not Root) + if (l>=0) + { + command = "CREATE TABLE "; + command += GetTree().GetLevelDescriptor(l).GetName(); + command += "\n(\nID INTEGER PRIMARY KEY"; + if (l>1) + { + command += ",\nPARENT_ID int not null"; + } + SQLAppendAttributesDefinition(l,command); + if (l>1) + { + command += ",\nconstraint FK_PARENT foreign key (PARENT_ID) references "; + command += GetTree().GetLevelDescriptor(l-1).GetName(); + command += "(ID) on delete restrict on update restrict"; + } + command += "\n)"; + UPDATEDB(command); + + + // Add Attribute 'ID' to Description + GetTree().GetDescriptor().Add + (AttributeDescriptor( "ID", + "Database Identifier", + 0,0, + AttributeDescriptor::PRIVATE + ),l); + + if (l>1) + { + // Add Attribute 'PARENT_ID' to Description + GetTree().GetDescriptor().Add + (AttributeDescriptor( "PARENT_ID", + "Database Parent Identifier", + 0,0, + AttributeDescriptor::PRIVATE + ),l); + } + + } + + // Create table *_ATTRIBUTES + + command = "CREATE TABLE "; + command += GetTree().GetLevelDescriptor(l).GetName(); + command += "_Attributes\n(\n"; + command += "Key text,\n"; + command += "Name text,\n"; + command += "DicomGroup int,\n"; + command += "DicomElement int,\n"; + command += "Flags int\n"; + command += "\n)"; + UPDATEDB(command); + + // Fill the table *_ATTRIBUTES + LevelDescriptor::AttributeDescriptorListType::const_iterator i; + for (i = GetTree().GetAttributeDescriptorList(l).begin(); + i != GetTree().GetAttributeDescriptorList(l).end(); + ++i) + { + + std::stringstream insert; + insert << "INSERT INTO " + << GetTree().GetLevelDescriptor(l).GetName() + << "_Attributes (Key,Name,DicomGroup,DicomElement,Flags) " + << "VALUES ('" + << i->GetKey() << "','" + << i->GetName() << "'," + << i->GetGroup() << "," + << i->GetElement() << "," + << i->GetFlags() << ");"; + UPDATEDB(insert.str()); + } + + } // For l=0... + + // Initialize the root attributes + GetTree().InitializeAttributeMap(); + // Insert the root in the level 0 table + DBInsert(GetTree().GetTree()); + + + GetTree().SetChildrenLoaded(true); + GimmickMessage(1,"Creating SQLite database '"<second.flags==1) continue; + s += ",\n"; + s += i->GetKey(); + s += " text"; + } + } + //===================================================================== + + + //===================================================================== + bool SQLiteTreeHandler::DBImportTreeDescription() + { + GimmickMessage(1,"Importing tree description for database ..." + <tableExists("LEVELS") ) + { + GimmickMessage(1,"!! ERROR : Table 'LEVELS' does not exist" + <tableExists(table.c_str()) ) + { + GimmickMessage(1,"!! ERROR : Table '"<GetKey(), "" ); + } + + // Reading Root attributes + // Query DB + query = "SELECT * FROM "; + query += GetTree().GetLevelDescriptor(0).GetName(); + QUERYDB(query,q); + + for (int fld = 0; fld < q.numFields(); fld++) + { + GetTree().UnsafeSetAttribute(q.fieldName(fld), + q.getStringField(fld)); + } + + GimmickMessage(1,"Importing tree description from database ... OK" + <GetAttributeMap().begin(); + i != n->GetAttributeMap().end(); + i++) + { + if (i->first=="ID") + { + continue; + } + + atts += "'" + i->first + "'"; + SQLformat(i->second, out); + values += "'" + out + "'"; + atts += ","; + values += ","; + GimmickMessage(4,"'"<first<<"' = '"<second<<"'"<GetChildrenList().begin(); + i!= parent->GetChildrenList().end(); + ++i) + { + if ( (*i)->Matches( attr ) ) + { + go_down = true; + parent = *i; + break; + } + } + } + while (go_down); + return parent; + } + //===================================================================== + + //===================================================================== + int SQLiteTreeHandler::DBLoadChildren(tree::Node* node, + int numberoflevels) + { + if (node->GetLevel()+1 >= node->GetTree()->GetNumberOfLevels() ) + return 0; + + GimmickMessage(2,"Loading children of '"<GetLabel() + <<"'"<GetChildrenLoaded()) + { + // Iterate the children + + tree::Node::ChildrenListType::iterator i; + for (i = node->GetChildrenList().begin(); + i!= node->GetChildrenList().end(); + ++i) + { + nbloaded += DBLoadChildren(*i,numberoflevels-1); + } + node->SetChildrenLoaded(true); + return nbloaded; + + } + else + { + /// If children not loaded : do it and recurse + + // Query DB + int level = node->GetLevel(); + std::string query = "SELECT * FROM "; + + query += GetTree().GetLevelDescriptor(level+1).GetName(); + if (level>0) + { + query += " WHERE PARENT_ID='" + node->GetAttribute("ID") + + "'"; + } +GimmickDebugMessage(1, "query : '" <UnsafeSetAttribute(q.fieldName(fld),q.getStringField(fld)); + } + + // recurse + if ( numberoflevels != 1 ) + { + // msw[2].Pause(); + nbloaded += DBLoadChildren(n, numberoflevels-1); + // msw[2].Resume(); + } + // next entry in db + q.nextRow(); + } + + node->SetChildrenLoaded(true); + + + // msw[2].Pause(); + return nbloaded; + } + } + //===================================================================== + + //====================================================================== + void SQLiteTreeHandler::DBInsert(tree::Node* n) + { + GimmickMessage(2,"Inserting in DB '"<GetLabel() + <<"'"<GetLevel()).GetName(); + insert += " " + val + ";"; + + UPDATEDB(insert); + + // Store DB id of newly created node; + long lastrow = mDB->lastRowId(); + std::stringstream ri; + ri << mDB->lastRowId(); + n->SetAttribute("ID",ri.str()); + } + //====================================================================== + + //====================================================================== + /// Graft the branch defined by the attributes to the parent + void SQLiteTreeHandler::DBGraftToParent( tree::Node* parent, + const AttributeMapType& attr) + { + // std::cout <<"Grafting to parent '"<GetLabel() + // <<"'"<GetLevel()+1; + level < GetTree().GetNumberOfLevels(); + level++) + { + // Create Node + tree::Node* child = new tree::Node(parent,attr); + child->SetChildrenLoaded(true); + if (level>1) + { + int nc = GetNumberOfChildren(parent)+1; + + // std::cout<<"Number of children "<GetNumberOfChildren()<GetLevel()>0 ) + child->SetAttribute("PARENT_ID",parent->GetAttribute("ID")); + + // Insert in DB + DBInsert(child); + + // Down one level + parent = child; + } + } + //====================================================================== + + + //===================================================================== + /// Sets an attribute of a Node + bool SQLiteTreeHandler::DBSetAttribute(tree::Node* n, + const std::string& key, + const std::string& value) + { + GimmickMessage(3,"Setting Attribute of '"<GetLabel()<< + "' "<SetAttribute(key,value); + std::string sql = "UPDATE "; + sql += GetTree().GetLevelDescriptor(n->GetLevel()).GetName(); + sql += " SET "; + sql += key; + sql += " = '"; + sql += value; + sql += "' WHERE ID = '"; + sql += n->GetAttribute("ID"); + sql +="'"; + // sql += " LIMIT 1"; + UPDATEDB(sql); + return true; + } + + //===================================================================== + /// Sets an attribute of a Node + void SQLiteTreeHandler::DBSetAttribute(const std::string& levelDescriptor, + const std::string& key, + const std::string& value, + const std::string& searchParam, + const std::string& searchVal) + { + + std::string sql = "UPDATE "; + sql += levelDescriptor; + sql += " SET "; + sql += key; + sql += " = '"; + sql += value; + sql += "' WHERE "; + sql += searchParam; + sql += " = '"; + sql += searchVal; + sql += "'"; + std::cout<GetLevel()).GetName(); + + query += " WHERE ID='"+ node->GetAttribute("ID") + "';"; + + UPDATEDB(query); + GimmickDebugMessage(2, + " Deleting '" + <GetLabel()<<"' with ID '" + <GetAttribute("ID") + <<"' in level "<< GetTree().GetLevelDescriptor(node->GetLevel()).GetName() + <GetNumberOfChildren()!=0) + { + Node::ChildrenListType::iterator i; + for (i = node->GetChildrenList().begin(); + i != node->GetChildrenList().end(); + i++) + { + DBRecursiveRemoveNode((*i)); + } + } + else if(node->GetLevel()GetLevel()+1,node->GetAttribute("ID")); + } + } + + //===================================================================== + void SQLiteTreeHandler::DBRecursiveRemoveNode(int level, std::string parentId) + { + std::stringstream out; + std::stringstream result; + out<<"SELECT ID FROM "<GetLevel(); + + if(level0) + { + std::string query = "SELECT NumberOfChildren FROM "; + query += GetTree().GetLevelDescriptor(level).GetName(); + if (level>0) + { + query += " WHERE ID='" + n->GetAttribute("ID") + + "'"; + } + CppSQLite3Query q; + QUERYDB(query,q); + + + while (!q.eof()) + { + for (int fld = 0; fld < q.numFields(); fld++) + { + nb=q.getIntField(fld); + } + q.nextRow(); + } + } + /* + if(nb==0) + { + nb=1; + } + */ + return nb; + } + + //===================================================================== + void SQLiteTreeHandler::GetTopLevelNodeId(const std::string& searchParam, const std::string& searchValue, std::string& parent_id) + { + int level=GetTree().GetNumberOfLevels()-1; + std::string sp=searchParam.c_str(); + std::string sv=searchValue.c_str(); + + while(level>1) + { + std::stringstream out; + std::stringstream results; + out<<"SELECT PARENT_ID FROM "< + +class CppSQLite3DB; + +namespace creaImageIO +{ + + + /** + * \ingroup Model + */ + //======================================================================= + /// Concrete TreeHandler which manages a Tree stored in a sqlite database + class SQLiteTreeHandler : virtual public TreeHandler + { + public: + //==================================================================== + /// Ctor with database file name + SQLiteTreeHandler(const std::string& filename); + /// Dtor + virtual ~SQLiteTreeHandler(); + //==================================================================== + + //==================================================================== + /// Returns the sqlite db file name + const std::string& GetFileName() const { return mFileName; } + //==================================================================== + + //==================================================================== + // QUERY METHODS + /// Is the 'source' readable ? + virtual bool IsReadable() { return true; } + /// Is the 'source' writable ? + virtual bool IsWritable() { return true; } + //==================================================================== + + + //==================================================================== + // INITIALIZATION / FINALIZATION + //==================================================================== + + //==================================================================== + /// Opens an existing 'source' + // Default mode is read only + // If IsWritable and writable==true then opens in read/write mode + virtual bool Open(bool writable = false); + /// Closes the 'source' + virtual bool Close(); + /// Creates a new 'source' + // Default mode is read only + // If IsWritable and writable==true then opens in read/write mode + virtual bool Create(bool writable = false); + /// Destroys the 'source' + virtual bool Destroy(); + /// Begins a transaction + virtual void BeginTransaction(); + ///Commits results and ends transaction + virtual void EndTransaction(); + //==================================================================== + + + //==================================================================== + // READ METHODS + //==================================================================== + + + //==================================================================== + /// Returns the number of children of the Node *WITHOUT LOADING THEM* + // REM : The Tree itself is a Node and asking for its number of + // children returns the number of children of level 1. + virtual unsigned int GetNumberOfChildren(tree::Node* n); + //==================================================================== + + //==================================================================== + /// Returns the attribute requested. Useful for synchronization. + virtual void GetAttribute(std::string levelDescriptor, + std::string searchParam, + std::string searchVal, + std::string key, + std::string& result); + //==================================================================== + + + //==================================================================== + /// Recursively loads the children of node 'parent' until maxlevel + // is reached. + // If parent == NULL or parent == tree then starts with the 'children' of + // the tree itself. + // Returns the total number of children loaded. + virtual int LoadChildren(tree::Node* parent, int maxlevel); + //==================================================================== + + //==================================================================== + /// Unloads the Node and its descendants + // WITHOUT altering the source, e.g. the database + virtual void UnLoad(tree::Node* n); + ///==================================================================== + + //==================================================================== + /// Returns the top level node id for the given search param and search value + virtual void GetTopLevelNodeId(const std::string& searchParam, + const std::string& searchValue, + std::string& parent_id); + ///==================================================================== + + //==================================================================== + // WRITE METHODS : WORK ONLY IN WRITE MODE + //==================================================================== + /// Adds a branch in the tree with the attributes provided + // returns the Level in the tree where the branch was connected + // (-1 for error, 0 for top level, etc. ) + // Of course the branch is loaded on exit + virtual int AddBranch( const AttributeMapType& attr ); + // Removes the node and its descendants + virtual bool Remove(tree::Node*); + // Sets an attribute of a Node + virtual bool SetAttribute(tree::Node*, + const std::string& key, + const std::string& value); + // Sets an attribute + virtual void SetAttribute(const std::string& levelDescriptor, + const std::string& key, + const std::string& value, + const std::string& searchParam, + const std::string& searchVal); + //Deletes the tuple that matches the parameters given + virtual void DeleteTuple(std::string levelDescriptor, std::string key, std::string value); + //Deletes the entries that match the parameters given + virtual void RemoveEntries(const std::string i_table, + const std::string i_attribute, + const std::string i_operand, + const std::string i_val); + + //==================================================================== + + + + protected: + //====================================================================== + /// Open the database + bool DBOpen(); + /// Import the Tree::Description from database (verifies the structure) + bool DBImportTreeDescription(); + //====================================================================== + //====================================================================== + // Creation + /// Creates a new database on disk and the tables + bool DBCreate(); + /// Appends to string s the SQL command to create the attributes of a given level + void SQLAppendAttributesDefinition(int level, std::string& s); + //====================================================================== + + //====================================================================== + + /// Returns the parent to which the branch defined by the attributes + // provided must be grafted + tree::Node* DBGetParent( const AttributeMapType& attr); + //====================================================================== + + //====================================================================== + + /// Loads the children of Node parent + // Can recurse to numberoflevels levels + // \return The total number of Node loaded (may be at different levels) + int DBLoadChildren( tree::Node* parent, int numberoflevels = 1); + //====================================================================== + + //====================================================================== + + /// Appends to string s the SQL command to set the attributes values + // of node n + void SQLAppendAttributesValues(tree::Node* n, std::string& s); + //====================================================================== + + //====================================================================== + + /// Graft the branch defined by the attributes to the parent + void DBGraftToParent( tree::Node* parent, const AttributeMapType& attr); + //====================================================================== + //====================================================================== + + /// Sets an attribute of a Node and updates the database + bool DBSetAttribute(tree::Node*, + const std::string& key, + const std::string& value); + //====================================================================== + //====================================================================== + /// Sets an attribute and updates the database + void DBSetAttribute(const std::string& levelDescriptor, + const std::string& key, + const std::string& value, + const std::string& searchParam, + const std::string& searchVal); + //====================================================================== + //====================================================================== + + /// Inserts the Node in the database + void DBInsert(tree::Node* n); + //====================================================================== + + + //====================================================================== + + /// Deletes the tuple that matches the value specified in the given key and that belongs to the given level + void DBDelete(std::string levelDescriptor, std::string key, std::string value); + //====================================================================== + + //====================================================================== + + /// Recursively Removes the nodes whose parent is given as a parameter + void DBRecursiveRemoveNode(tree::Node* node); + /// Recursively Removes the nodes found in the given level with the given parent id + void DBRecursiveRemoveNode(int level, std::string parentId); + + //====================================================================== + + + private: + /// The DB + CppSQLite3DB* mDB; + /// The physical location associated to the DicomDatabase (directory, db file...) + std::string mFileName; + /// Is the DB writable ? + bool mWritable; + void SetWritable(bool w) { mWritable = w; } + bool GetWritable() const { return mWritable; } + bool mIsAdding; + + + }; + // EO class SQLiteTreeHandler + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src/creaImageIOSettings.cpp b/src/creaImageIOSettings.cpp new file mode 100644 index 0000000..770a802 --- /dev/null +++ b/src/creaImageIOSettings.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +// Memory tracking allocation +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +using namespace boost; +namespace po = boost::program_options; + +namespace creaImageIO +{ + Settings::Settings(const std::string i_path) + { + //need to position path in user directory first. + m_SettingsFileName = i_path + "\\.gimmick\\Shared\\gimmick\\app.config"; + //Test if Settings File exist + if(!boost::filesystem::exists(m_SettingsFileName) ) + { + createFile(); + } + std::ifstream ifs(m_SettingsFileName.c_str()); + std::string line; + std::string sets; + if (ifs.is_open()) + { + while (! ifs.eof() ) + { + getline(ifs,line); + sets += line; + } + ifs.close(); + } + std::vector Keys; + Keys.push_back(SETTINGS_SYNC_EVENT); + Keys.push_back(SETTINGS_DBPATH); + Keys.push_back(SETTINGS_SYNC_FREQ); + Keys.push_back(SETTINGS_COPY_PATH); + Keys.push_back(SETTINGS_REMOVE_PATIENT_DISPLAY); + readSettings(Keys, sets); + + } + + Settings::~Settings() + { + + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // create the config file // + //@param : - // + // return : - // + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::createFile() + { + m_SettingsMap[SETTINGS_SYNC_FREQ] = "12"; + m_SettingsMap[SETTINGS_SYNC_EVENT] = "end"; + m_SettingsMap[SETTINGS_DBPATH] = ""; + m_SettingsMap[SETTINGS_DICOM_LIBRARY] = "gdcm"; + m_SettingsMap[SETTINGS_COPY_PATH] = m_SettingsFileName.substr(0,m_SettingsFileName.find_last_of('\\')+1)+"Copied files"; + m_SettingsMap[SETTINGS_REMOVE_PATIENT_DISPLAY] = "0"; + writeSettingsFile(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // read Settings from config file // + // @param i_keys : list of keys // + // @param i_file : text from config file // + // return : - + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::readSettings(std::vector &i_Keys, const std::string &i_file) + { + std::vector::iterator it_key = i_Keys.begin(); + for(; it_key< i_Keys.end(); ++it_key) + { + size_t fpos = i_file.find(it_key->c_str()); + size_t lpos = i_file.rfind(it_key->c_str()); + if(fpos != std::string::npos && lpos != std::string::npos) + { + m_SettingsMap[it_key->c_str()] = i_file.substr(fpos + it_key->size(),lpos-fpos - it_key->size()); + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Update settings in config file // + // @param key : Key to update // + // @param value: New value to set // + // return : - + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::updateSetting(const std::string& key, const std::string &val) + { + m_SettingsMap[key.c_str()] = val; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // add a path to a DB // + // @param i_path : DB path to add // + // return : - // + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::addDB(const std::string &i_path) + { + if(m_SettingsMap[SETTINGS_DBPATH].find(i_path) == std::string::npos) + { + m_SettingsMap[SETTINGS_DBPATH] += i_path + ";"; + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // remove a path to a DB // + // @param i_path : DB path to delete (don't exist anymore) // + // return : - + /////////////////////////////////////////////////////////////////////////////////////////////// + + void Settings::removeDB(const std::string &i_path) + { + boost::algorithm::replace_all(m_SettingsMap[SETTINGS_DBPATH],i_path + ";",""); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // write Settings buffer from // + // @param o_file : settings buffer // + // // + // return : - // + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::writeSettings(std::ofstream &o_file) + { + std::map::iterator it_map = m_SettingsMap.begin(); + std::stringstream st; + for(; it_map != m_SettingsMap.end(); ++it_map) + { + o_file << it_map->first.c_str(); + o_file << it_map->second.c_str(); + o_file << it_map->first.c_str(); + o_file << std::endl; + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // write Settings file // + // @param : - // + // return : - + /////////////////////////////////////////////////////////////////////////////////////////////// + void Settings::writeSettingsFile() + { + std::ofstream ofs(m_SettingsFileName.c_str()); + ofs.clear(); + writeSettings(ofs); + ofs.close(); + } +} + diff --git a/src/creaImageIOSettings.h b/src/creaImageIOSettings.h new file mode 100644 index 0000000..8d13d36 --- /dev/null +++ b/src/creaImageIOSettings.h @@ -0,0 +1,44 @@ +#include +#include + +#define SETTINGS_DICOM_LIBRARY "" +#define SETTINGS_SYNC_EVENT "" +#define SETTINGS_SYNC_FREQ "" +#define SETTINGS_DBPATH "" +#define SETTINGS_COPY_PATH "" +#define SETTINGS_REMOVE_PATIENT_DISPLAY "" + + +namespace creaImageIO +{ + class Settings{ + public : + Settings(const std::string i_path); + ~Settings(); + + //get the value for a given option + const std::string getValue(const std::string i_key){return m_SettingsMap[i_key];} + + void addDB(const std::string &i_path); + + void removeDB(const std::string &i_path); + + void updateSetting(const std::string& key, const std::string &val); + + //write configuration file + void writeSettingsFile(); + + private : + // Settings Key-Value Map + std::map m_SettingsMap; + + //read the configuration file + void readSettings(std::vector &i_Keys, const std::string &i_file); + // create the configuration file + void createFile(); + void writeSettings(std::ofstream &o_filebuf); + std::string m_SettingsFileName; + + + }; +} diff --git a/src/creaImageIOSynchron.cpp b/src/creaImageIOSynchron.cpp new file mode 100644 index 0000000..0955ffa --- /dev/null +++ b/src/creaImageIOSynchron.cpp @@ -0,0 +1,451 @@ +#include +#include +#include +#include + + +//namespace fs = boost::filesystem; + +//===================================================================== + + +namespace creaImageIO +{ + + //===================================================================== + #define QUERYSYNCDB(QUER,RES) \ + try \ + { \ + RES = mDB->execQuery(QUER.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite query '"<execDML(UP.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite update '"<open(pathDB.c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError("Opening '"<open(pathDB.c_str()); + // CREATING TABLES + std::string command; + command = "CREATE TABLE "; + command += "ADD_OPS"; + command += "\n(\nADD_KEY INTEGER PRIMARY KEY"; + command += ",\nPATH text"; + command += ",\nRECURSIVE boolean"; + command += ",\nFILES_ADDED int"; + command += ",\nREFERENCEDDB text"; + command += "\n)"; + UPDATESYNCDB(command); + + command = "CREATE TABLE "; + command += "IGNORED_FILES"; + command += "\n(\nID INTEGER PRIMARY KEY"; + command += ",\nADD_KEY integer"; + command += ",\nPATH text"; + command += ",\nREMOVE boolean"; + command += ",\nTIME datetext"; + command += "\n)"; + UPDATESYNCDB(command); + } + + //===================================================================== + void Synchronizer::CleanName(std::string& str) const + { + size_t pos; + do + { + pos = str.find('\\'); + if (pos!=-1) + { + str.replace(pos, 1, "/"); + } + } + while (pos!=-1); + } + + //===================================================================== + void Synchronizer::GetFileList(std::vector & list, const std::string& refdb) + { + CleanList(refdb); + list=mAddList; + } + + //===================================================================== + void Synchronizer::GetIgnoredFiles(const std::string& key, std::vector &ignoreList) + { + ignoreList=GetIgnoreList(key); + } + +//===================================================================== + void Synchronizer::UpdateAddList(const std::string& refdb) + { + std::string query = "SELECT * FROM ADD_OPS WHERE REFERENCEDDB = '"+refdb+"';"; + CppSQLite3Query res; + QUERYSYNCDB(query, res); + while (!res.eof()) + { + AddList temp = AddList(res); + mAddList.push_back(temp); + res.nextRow(); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // remove an entry of the DB + //@param i_table : table where to do the remove + // @param i_key : the add_key reference (one entry to remove for ADD_OP table, many for IGNORED_FILES table + //@result : - + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::RemoveEntry(const std::string i_table, const std::string i_key) + { + std::string query = "DELETE FROM " + i_table + " WHERE ADD_KEY = '" + i_key +"'"; + UPDATESYNCDB(query); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // remove several entries of the DB + // @param i_table : table where to do the remove + // @param i_attribute: attribute to match + // @param i_operand : operand to use + // @param i_val : the reference + //@result : - + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::RemoveEntries(const std::string i_table, + const std::string i_attribute, + const std::string i_operand, + const std::string i_val) + { + std::stringstream query; + query<<"DELETE FROM "<::iterator it_add = mAddList.begin(); + for(;it_add nbFiles == "0") + { + RemoveEntry("ADD_OPS", it_add->key); + RemoveEntry("IGNORED_FILES", it_add->key); + + } + } + mAddList.clear(); + UpdateAddList(refdb); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Inserts a new add operation in the database + // @param path: the path of the directory that was added + // @param recursive: shows if the action was called recursively or not + // @param nChildren: the number of files affected by the operation + // @param refdb: the referenced database + // @result : The operation has been added + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::InsertAddOp(const std::string& path, const std::string& recursive, const std::string& nChildren, const std::string& refdb) + { + std::string insert; + std::string pat=path.c_str(); + CleanName(pat); + insert="INSERT INTO ADD_OPS (PATH,RECURSIVE,FILES_ADDED,REFERENCEDDB) VALUES('"; + insert+=pat+"','"; + insert+=recursive+"',"; + insert+=nChildren+",'"; + insert+=refdb+"');"; + UPDATESYNCDB(insert); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Inserts a new ignored file in the database + // @param add_key: the key of the add_op to which it corresponds + // @param path: the path of the directory that was added + // @param remove: shows if the file was removed or not + // @param time: the time in which the file was removed + // @result : The file has been inserted + ///////////////////////////////////////////////////////////////////////////////////////////////// + + void Synchronizer::InsertIgnoreFile(const std::string& addKey, const std::string& path, const std::string& remove, const std::string& time, const std::string& refdb ) + { + std::string pat=path.c_str(); + CleanName(pat); + std::string id=GetAttribute("ID","IGNORED_FILES","PATH",pat,refdb); + if(id.compare("")==0) + { + std::string insert; + insert="INSERT INTO IGNORED_FILES (ADD_KEY,PATH,REMOVE,TIME) VALUES('"; + insert+=addKey+"','"; + insert+=pat+"','"; + insert+=remove+"',"; + insert+=time+");"; + UPDATESYNCDB(insert); + } + else + { + //Gets the add key + std::string ak=GetAttribute("ADD_KEY","IGNORED_FILES","ID",id,refdb); + //gets the parent database to check if the file has been added to the current database + std::string parentDB=GetAttribute("*","ADD_OPS","ADD_KEY",ak,refdb); + //If there is no such entry, add it + if(parentDB.compare("")==0) + { + std::string insert; + insert="INSERT INTO IGNORED_FILES (ADD_KEY,PATH,REMOVE,TIME) VALUES('"; + insert+=addKey+"','"; + insert+=pat+"','"; + insert+=remove+"',"; + insert+=time+");"; + UPDATESYNCDB(insert); + } + else + { + //Sets the new add key attribute for the file + SetAttribute("ADD_KEY","IGNORED_FILES",addKey,"ID", id,refdb); + //Sets the new remove attribute for the file + SetAttribute("REMOVE","IGNORED_FILES",remove,"ID", id,refdb); + //Sets the new time attribute for the file + SetAttribute("TIME","IGNORED_FILES",time,"ID", id,refdb); + } + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // get the files name to ignore for a add operation synchronization + // @param : the add key + //@result : list (path) of ignore files + ///////////////////////////////////////////////////////////////////////////////////////////////// + std::vector Synchronizer::GetIgnoreList(const std::string &i_key) + { + mIgnoreList.clear(); + std::vector i_names; + std::string query = "SELECT * FROM IGNORED_FILES WHERE ADD_KEY = "; + query+=i_key; + CppSQLite3Query res; + QUERYSYNCDB(query, res); + while (!res.eof()) + { + RemoveList temp = RemoveList(res); + if(temp.remove.compare("0")==0) + { + mIgnoreList.push_back(temp); + } + res.nextRow(); + } + std::vector::iterator it; + + for(it = mIgnoreList.begin();it != mIgnoreList.end(); ++it) + { + i_names.push_back((*it).path); + } + return i_names; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Gets the required attribute in the required table + // @param attribute: the attribute to look for + // @param table: the table to look in + // @param searchParam: the search parameter + // @param searchValue: the search value + // @result : required attribute + ///////////////////////////////////////////////////////////////////////////////////////////////// + std::string Synchronizer::GetAttribute(const std::string& attribute, + const std::string& table, + const std::string& searchParam, + const std::string& searchValue, + const std::string& refdb) + { + std::stringstream query; + std::string result; + std::string sVal=searchValue.c_str(); + CleanName(sVal); + query<<"SELECT "< i_names; + std::vector keys; + CppSQLite3Query res; + std::string query ="SELECT ADD_KEY, REFERENCEDDB FROM ADD_OPS"; + QUERYSYNCDB(query, res); + keys.clear(); + while (!res.eof()) + { + std::string key(res.getStringField(0)); + std::string db(res.getStringField(1)); + if (db == i_db) + { + keys.push_back(key); + } + res.nextRow(); + } + query = "SELECT PATH, REMOVE FROM IGNORED_FILES WHERE"; + if(keys.size() > 0) + { + for (int i=0; i < keys.size(); i++) + { + query += " ADD_KEY = " + keys[i]; + query += " AND"; + } + query = query.substr(0,query.size() - 4); + } + else + { + query += " ADD_KEY = -1"; + } + QUERYSYNCDB(query, res); + while (!res.eof()) + { + std::string file(res.getStringField(0)); + std::string ignore(res.getStringField(1)); + mList[file] = ignore == "0"? true : false; + res.nextRow(); + } + } + + bool Synchronizer::isIndexed(const std::string filename) + { + bool valid = true; + std::string name(filename); + boost::algorithm::replace_all( name,"\\" , "/"); + std::map ::iterator it_list = mList.begin(); + for(;it_list != mList.end(); it_list++) + { + if(it_list->first == name) + { + valid = false; + break; + } + } + return valid; + } +} + + diff --git a/src/creaImageIOSynchron.h b/src/creaImageIOSynchron.h new file mode 100644 index 0000000..ab101f6 --- /dev/null +++ b/src/creaImageIOSynchron.h @@ -0,0 +1,139 @@ +#ifndef __creaImageIOSynchron_h_INCLUDED__ +#define __creaImageIOSynchron_h_INCLUDED__ + +#include +#include +#include +#include +#include "CppSQLite3.h" + +namespace creaImageIO +{ + using namespace std; + //================================================================================================================ + ///Represents the list of currently added files + class AddList + { + public : + ///Key to be added into the database + std::string key; + ///Path of the directory + std::string path; + /// Defines if the operation was recursive or not + std::string recursive; + ///Number of added files + std::string nbFiles; + ///Ctor + AddList(CppSQLite3Query& res): + key(res.getStringField(0)), + path(res.getStringField(1)), + recursive(res.getStringField(2)), + nbFiles(res.getStringField(3)) + {} + }; + //================================================================================================================ + + //================================================================================================================ + ///Represents the list of currently removed files + class RemoveList + { + public : + ///Key to be added into the database + std::string key; + ///Path of the remove file + std::string path; + ///Defines if the file was removed or not + std::string remove; + ///Time of the last change of the file + std::string time; + ///Ctor + RemoveList(CppSQLite3Query& res): + key(res.getStringField(1)), + path(res.getStringField(2)), + remove(res.getStringField(3)), + time(res.getStringField(4)) + {} + }; + //================================================================================================================ + + //================================================================================================================ + ///In charge of the synchronization of the database and the disk state. + class Synchronizer + { + public: + ///Ctor + Synchronizer(const std::string& path); + ///Dtor + virtual ~Synchronizer(); + ///Initializes the database + void Initialize(); + ///Inserts an add operation to the database + void InsertAddOp(const std::string& path, + const std::string& recursive, + const std::string& nChildren, + const std::string& refdb); + ///Inserts a file to be ignored + void InsertIgnoreFile(const std::string& addKey, + const std::string& path, + const std::string& remove, + const std::string& time, + const std::string& refdb); + ///Removes an entry that matches the given parameter + void RemoveEntry(const std::string i_table, const std::string i_key); + ///Removes several entries + void RemoveEntries(const std::string i_table, + const std::string i_attribute, + const std::string i_operand, + const std::string i_key); + ///Gets the list of AddFiles + void GetFileList(std::vector& files , const std::string& refdb); + ///Gets the list of ignored files + void GetIgnoredFiles(const std::string& key, std::vector &ignoreList); + ///Gets the attribute that matches the parameters + std::string GetAttribute(const std::string& attribute, + const std::string& table, + const std::string& searchParam, + const std::string& searchValue, + const std::string& refdb); + ///Sets an attribute to an entry that matches the given parameters + void SetAttribute(const std::string& attribute, + const std::string& table, + const std::string& value, + const std::string& searchParam, + const std::string& searchValue, + const std::string& refdb); + // Get the List of indexed files (removed or not) + void GetList(const std::string i_db); + // Test to not if a file is indexed on db or not + bool isIndexed(const std::string filename); + // List of all indexed files + std::map mList; + ///The current AddList + std::vector mAddList; + ///The current RemoveList + std::vector mIgnoreList; + private : + + /// The DB + CppSQLite3DB* mDB; + ///Path of the current database + std::string pathDB; + ///Creates a new database + void CreateDB(); + ///Updates the AddList + void UpdateAddList(const std::string& refdb); + ///Cleans the list in case operations are no longer useful (0 added files) + void CleanList(const std::string& refdb); + ///Cleans the name (changes slashes and backslashes according to the system) + void CleanName(std::string& str) const; + ///Gets the ignore list + std::vector GetIgnoreList(const std::string &i_key); + + }; + //================================================================================================================ + + +} +// EOF +#endif + diff --git a/src/creaImageIOSynchronizer.cpp b/src/creaImageIOSynchronizer.cpp new file mode 100644 index 0000000..8ab3009 --- /dev/null +++ b/src/creaImageIOSynchronizer.cpp @@ -0,0 +1,114 @@ +#include +#include +#include "boost/filesystem.hpp" + +namespace fs = boost::filesystem; + +namespace creaImageIO +{ + + //============================================================== + Synchronizer::Synchronizer(TreeHandler * th) + : mHandler(th) + { + + } + //============================================================== + + //============================================================== + Synchronizer::~Synchronizer() + { + + } + //============================================================== + + //============================================================== + std::string Synchronizer::Synchronize(bool update) + { + GimmickMessage(1,"Synchronizing "<GetAttribute("Image","","","FullFileName",file); + size_t ini=0; + size_t fin=0; + while(finDeleteTuple("Image","FullFileName",file); + mess< attr; + mHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + mReader.ReadAttributes(file,attr); + tree::LevelDescriptor::AttributeDescriptorListType adl= mHandler->GetTree().GetAttributeDescriptorList(mHandler->GetTree().GetNumberOfLevels()-1); + tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = adl.begin();a!=adl.end();++a) + { + std::string databaseVal; + mHandler->GetAttribute("Image","FullFileName",file,a->GetKey(),databaseVal); + std::string fileVal=attr.find(a->GetKey())->second; + if ( a->GetFlags()==0 && databaseVal.compare(fileVal)!=0 ) + { + if(update) + { + mHandler->SetAttribute("Image",a->GetKey(),fileVal,"FullFileName", file); + mess< +#include + +namespace creaImageIO +{ + +//======================================================================= +/// Synchronizes a given database with disk +class Synchronizer + { + public: + /// Ctor + Synchronizer(TreeHandler* th); + /// Dtor + ~Synchronizer(); + ///Sets the tree handler to use in order to synchronize with a given database + void SetTreeHandler(TreeHandler * handler){mHandler=handler;} + ///Synchronizes the database in the current tree handler with disk by doing the passed action. If it is true, + ///the database will be updated, otherwise a warning message will be returned. + std::string Synchronize(bool update); + ///Checks if the file given as a parameter exists in the drive + bool FileExists(std::string file); + ///Checks if the attributes of the node given as a parameter matchwith it correspondent file in disk + ///NB: This method doesn't check the existence of the file, so FileExists should be called before. + void AttributesMatch(bool update, std::string file, std::stringstream& mess); + + + private: + ///The tree handler + TreeHandler* mHandler; + ///The image reader + ImageReader mReader; + ///Synchronizes the given file, doing the action required and returning the result on the string supplied + void SynchronizeFile(bool update, std::string file, std::stringstream& message); + + }; +} // EO namespace creaImageIO + +// EOF +#endif \ No newline at end of file diff --git a/src/creaImageIOSystem.cpp b/src/creaImageIOSystem.cpp new file mode 100644 index 0000000..8a9662d --- /dev/null +++ b/src/creaImageIOSystem.cpp @@ -0,0 +1,7 @@ +#include "creaImageIOSystem.h" + +namespace creaImageIO +{ + + +} // namespace \ No newline at end of file diff --git a/src/creaImageIOSystem.h b/src/creaImageIOSystem.h new file mode 100644 index 0000000..1ecda73 --- /dev/null +++ b/src/creaImageIOSystem.h @@ -0,0 +1,67 @@ +#ifndef __creaImageIOSystem_INCLUDED__ +#define __creaImageIOSystem_INCLUDED__ + +#include "creaMessageManager.h" + + + +#if defined(_WIN32) + + #ifdef CREAIMAGEIO_EXPORT_SYMBOLS + #define CREAIMAGEIO_EXPORT __declspec( dllexport ) +#else + #define CREAIMAGEIO_EXPORT __declspec( dllimport ) + #endif + #define CREAIMAGEIO_CDECL __cdecl +#else + #define CREAIMAGEIO_EXPORT + #define CREAIMAGEIO_CDECL +#endif // defined(_WIN32) + + +namespace creaImageIO +{ + //============================================================== + inline void RegisterGimmickMessageTypes() + { + static bool first_time = true; + if (first_time) + { + crea::MessageManager::RegisterMessageType("Gimmick!", + "Gimmick",1); + crea::MessageManager::RegisterMessageType("Gimmick! DEBUG", + "Gimmick",0); + first_time = false; + } + } + //============================================================== + inline void SetGimmickMessageLevel(int l) + { + RegisterGimmickMessageTypes(); + crea::MessageManager::SetMessageLevel("Gimmick!",l); + } + //============================================================== + inline void SetGimmickDebugMessageLevel(int l) + { + RegisterGimmickMessageTypes(); + crea::MessageManager::SetMessageLevel("Gimmick! DEBUG",l); + } + //============================================================== + + inline void deleteGimmickDebugMessage() + { + delete crea::MessageManager::GetInstance(); + } + + +#define GimmickMessage(LEV,MESS) \ + creaMessage("Gimmick!",LEV,"[Gimmick!] "< +#include + +#include "CppSQLite3.h" + +#include + +#include + +#include +#include + +namespace creaImageIO +{ + using namespace tree; + //============================================================= + TimestampDatabaseHandler::TimestampDatabaseHandler(const std::string& filename) + : mFileName(filename) + { + mDB = new CppSQLite3DB; + GimmickMessage(1,"SQLite version : " + <SQLiteVersion())<< std::endl); + } + //============================================================= + + //============================================================= + TimestampDatabaseHandler::~TimestampDatabaseHandler() + { + delete mDB; + } + //============================================================= + //===================================================================== + bool TimestampDatabaseHandler::Open() + { + return DBOpen(); + } + + //===================================================================== + bool TimestampDatabaseHandler::Create() + { + return DBCreate(); + } + //===================================================================== + + + //===================================================================== + bool TimestampDatabaseHandler::Close() + { + return true; + } + //===================================================================== + + + //===================================================================== + bool TimestampDatabaseHandler::Destroy() + { + return false; + } + + + + + + + //===================================================================== + // SQLite DB specific methods + //===================================================================== + //===================================================================== +#define QUERYTIMESTAMPDB(QUER,RES) \ + try \ + { \ + GimmickMessage(2,"SQL query: '"<execQuery(QUER.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite query '"<execDML(UP.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite update '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError("Opening '"<open(GetFileName().c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError(e.errorCode() << ":" + << e.errorMessage() <GetNumberOfChildren(); + if(n>0) + { + std::vector children=node->GetChildrenList(); + std::vector::iterator it; + for(it=children.begin();it!=children.end();++it) + { + RemoveNode(searchAtt,(*it),refdb); + } + } + else if(node->GetLevel()==3) + { + RemoveFile(searchAtt,node->GetAttribute("FullFileName"),refdb); + } + else + { + DBRemove("TopLevelNodeId",node->GetAttribute("ID"),refdb); + } + + + } + //===================================================================== + void TimestampDatabaseHandler::RemoveFile(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb ) + { + + std::stringstream result; + std::string sel="SELECT PARENT_ID FROM FILES WHERE "+searchAtt+"='"+searchVal+"' AND REFERENCEDDB='"+refdb+"';"; + + CppSQLite3Query q; + QUERYTIMESTAMPDB(sel,q); + + while (!q.eof()) + { + for (int fld = 0; fld < q.numFields(); fld++) + { + result< +#include +#include +class CppSQLite3DB; + +namespace creaImageIO +{ + using namespace std; +//======================================================================= + /// Concrete TreeHandler which manages a Tree stored in a sqlite database + class TimestampDatabaseHandler + { + public: + //==================================================================== + /// Ctor with database file name + TimestampDatabaseHandler(const std::string& filename); + /// Dtor + virtual ~TimestampDatabaseHandler(); + //==================================================================== + + //==================================================================== + /// Returns the sqlite db file name + const std::string& GetFileName() const { return mFileName; } + //==================================================================== + + //==================================================================== + // INITIALIZATION / FINALIZATION + //==================================================================== + + //==================================================================== + /// Opens an existing 'source' + bool Open(); + /// Closes the 'source' + bool Close(); + /// Creates a new 'source' + bool Create(); + /// Destroys the 'source' + bool Destroy(); + //==================================================================== + + //==================================================================== + // READ / WRITE + //==================================================================== + //==================================================================== + ///Returns the id of the path if it's indexed, blank otherwise + std::string IsIndexed(const std::string& path, const std::string& refdb); + ///Sets the current path's parent + bool AddDirectory(const std::string& parent, + const std::string& path, + const time_t lastModif, + const time_t lastRead, + const std::string& refdb); + ///Adds a new file to the database without a parent + void AddFile(const std::string& path, const time_t lastModif, const time_t lastRead, const std::string& refdb); + ///Adds a new file to the database with a parent + void AddFile(const std::string& parentId,const std::string& path, const time_t lastModif, const time_t lastRead, const std::string& refdb); + ///Sets the attribute to the value passed as parameter where the searchParameter is searchValue + void SetAttribute(const std::string& attName, + const std::string& attValue, + const std::string& searchParam, + const std::string& searchValue); + ///Removes the given node + void RemoveNode(const std::string& searchAtt, const tree::Node* node, const std::string& refdb); + ///Removes the filename with the given pathname + void RemoveFile(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb); + ///Cleans the path name + void CleanPath(std::string& str) const; + ///Checks the timestamp in the database and compares it with the given one. + //If there is a difference, it will return false, otherwise it will return true. + bool CheckTimestamp(const std::string pathId, const time_t lastModif, const std::string& refdb); + ///Removes the entries that match the given parameters + void RemoveEntries(const std::string i_table, + const std::string i_attribute, + const std::string i_operand, + const std::string i_val); + + //==================================================================== + + + protected: + //====================================================================== + /// Open the database + bool DBOpen(); + //====================================================================== + //====================================================================== + // Creation + /// Creates a new database on disk and the tables + bool DBCreate(); + //====================================================================== + //====================================================================== + // Removes a file from the database + void DBRemove(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb); + + private: + /// The DB + CppSQLite3DB* mDB; + /// The physical location associated to the DicomDatabase (directory, db file...) + std::string mFileName; + + }; + // EO class + //======================================================================= + + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src/creaImageIOTree.cpp b/src/creaImageIOTree.cpp new file mode 100644 index 0000000..c2456a0 --- /dev/null +++ b/src/creaImageIOTree.cpp @@ -0,0 +1,34 @@ +#include +#include + + +namespace creaImageIO +{ + namespace tree + { + + Tree::Tree() + : Node(0) + { + GimmickMessage(5,"Default Tree constructor" + << std::endl); + + } + + Tree::~Tree() + { + + } + + void Tree::Print() const + { + GimmickMessage(1,GetLabel()<Print(); + } + + } + } +} diff --git a/src/creaImageIOTree.h b/src/creaImageIOTree.h new file mode 100644 index 0000000..de5b94a --- /dev/null +++ b/src/creaImageIOTree.h @@ -0,0 +1,76 @@ +#ifndef __creaImageIOTree_h_INCLUDED__ +#define __creaImageIOTree_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + namespace tree + { + /** + * \ingroup Tree + */ + //===================================================================== + /// Abstract class to store user data on a Tree + struct TreeData + { + TreeData() {} + virtual ~TreeData() {} + }; + //===================================================================== + + //===================================================================== + /// An attributed Tree structure + /** \ingroup Tree + */ + class Tree : public Node + { + public: + /// Ctor + Tree(); + /// Virtual destructor + virtual ~Tree(); + + /// Returns the descriptor of the tree + + /// Returns the tree to which the node belongs + virtual Tree* GetTree() { return this; } + /// Returns the tree to which the node belongs + virtual const Tree* GetTree() const { return this; } + /// Returns the level of the node in the tree + virtual int GetLevel() const { return 0; } + + /// Returns the Descriptor of the tree (const) + const Descriptor& GetDescriptor() const { return mDescriptor; } + /// Returns the descriptor of the tree + Descriptor& GetDescriptor() { return mDescriptor; } + + /// Returns the number of levels of the tree + unsigned int GetNumberOfLevels() + { return GetDescriptor().GetNumberOfLevels(); } + + /// Returns the LevelDescriptor of a given level (const ref) + const LevelDescriptor& GetLevelDescriptor(int level) const + { return GetDescriptor().GetLevelDescriptor(level); } + + /// Returns the AttributeDescriptorList of a given level (const ref) + const LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) const + { return GetDescriptor().GetAttributeDescriptorList(level); } + + virtual void Print() const; + + private: + Descriptor mDescriptor; + + }; + // EO class Tree + //===================================================================== + + } // EO namespace tree + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src/creaImageIOTreeAttributeDescriptor.cpp b/src/creaImageIOTreeAttributeDescriptor.cpp new file mode 100644 index 0000000..3e09a4f --- /dev/null +++ b/src/creaImageIOTreeAttributeDescriptor.cpp @@ -0,0 +1,274 @@ +#include +#include + + +#if defined(USE_GDCM) +#include +#include +#endif + +#if defined(USE_GDCM2) +#include +#include +#include +#endif + +#include + +namespace creaImageIO +{ + + namespace tree + { + + //======================================================================== + void AttributeDescriptor::CleanName(std::string& str) const + { + // quote must be doubled for SQL + // crea::Utils::Replace( str, "'", "''" ); + boost::algorithm::replace_all(str,"'","''"); + // Found strange strings which contained NULL char INSIDE string + int i,size=str.size(); + for (i=0;iGetDefaultPubDict()->GetEntry(mGroup,mElement); + + if (entry) + { + mName = entry->GetName(); + CleanName(mName); + GimmickDebugMessage(3,"='"<GetDefaultPubDict()->GetEntry(GetGroup(),GetElement()); + if( entry != 0) + { + if( entry->GetVR().str() == "DA" ) + { + btest = true; + } + } +#endif +#if defined(USE_GDCM2) + const gdcm::Global& g = gdcm::Global::GetInstance(); + const gdcm::Dicts &dicts = g.GetDicts(); + const gdcm::Dict &dict = dicts.GetPublicDict(); + if(mGroup != 0 && mElement != 0) + { + gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(GetGroup(), GetElement())); + if( gdcm::VR::GetVRString(dictentry.GetVR()) == "DA") + { + btest = true; + } + } +#endif + return btest; + } + + //===================================================================== + /// test if the type is a time + bool AttributeDescriptor::isTimeEntry() const + { + + bool btest = false; +#if defined(USE_GDCM) + // Retrieve the name from gdcm dict + GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(GetGroup(),GetElement()); + if( entry != 0) + { + if( entry->GetVR().str() == "TM" ) + { + btest = true; + } + } +#endif + +#if defined(USE_GDCM2) + const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge ! + const gdcm::Dicts &dicts = g.GetDicts(); + const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6 + if(mGroup != 0 && mElement != 0) + { + gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(mGroup, mElement)); + if(gdcm::VR::GetVRString(dictentry.GetVR()) == "TM") + { + btest = true; + } + } +#endif + + return btest; + } + + + //===================================================================== + /// Decodes the type of the attribute + void AttributeDescriptor::DecodeType(unsigned int& typ) const + { + std::string type=""; +#if defined(USE_GDCM) + // Retrieve the name from gdcm dict + GDCM_NAME_SPACE::DictEntry* entry = + GDCM_NAME_SPACE::Global::GetDicts() + ->GetDefaultPubDict()->GetEntry(GetGroup(),GetElement()); + + if (entry==0) + { + typ = 2; + return; + } + type = entry->GetVR().str(); +#endif +#if defined(USE_GDCM2) + const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge ! + const gdcm::Dicts &dicts = g.GetDicts(); + const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6 + gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(mGroup, mElement)); + type = gdcm::VR::GetVRString(dictentry.GetVR()); +#endif + + GimmickDebugMessage(3,"VR Value is "< +//#include + +namespace creaImageIO +{ + + namespace tree + { + /** + * \ingroup Tree + */ + //===================================================================== + /// Descriptor of an attribute of a node of a Tree (name, dicom group/element) + class AttributeDescriptor + { + public: + /// Flags + /// The attribute is hidden (not visible to user) + static const unsigned int PRIVATE; + /// The attribute enters in unique identifier constitution + static const unsigned int IDENTIFIER; + /// The attribute enters in label constitution (for printing) + static const unsigned int LABEL; + /// The attribute can be edited + static const unsigned int EDITABLE; + + /// Types + /// The attribute is of numeric type + static const int NUMBER=1; + /// The attribute is of string type + static const int STRING=2; + /// The attribute's type is unknown + static const int UNKNOWN=0; + + /// Default ctor + AttributeDescriptor() + : mKey(""), mName(""), mGroup(0), mElement(0), mFlags(0) + { + } + /// Ctor with all explicitely + AttributeDescriptor(const std::string& key, + const std::string& name, + unsigned short group, + unsigned short element, + unsigned int flags) + : mKey(key), mName(name), mGroup(group), mElement(element), + mFlags(flags) + { + } + + // Ctor with key, name and flags + AttributeDescriptor(const std::string& key, + const std::string& name, + unsigned int flags = 0); + // Ctor with dicom group, elem and flags + // The key is built as 'Dgroup_elem' + // The user name is retreived from dicom dictionnary + AttributeDescriptor(unsigned short group, + unsigned short element, + unsigned int flags = 0); + /// Returns the key of the attribute + const std::string& GetKey() const { return mKey; } + /// Returns the name of the attribute + const std::string& GetName() const { return mName; } + /// Returns the DICOM group code of the attribute + unsigned short GetGroup() const { return mGroup; } + /// Returns the DICOM element code of the attribute + unsigned short GetElement() const { return mElement; } + /// Returns the flags of the attribute + unsigned int GetFlags() const { return mFlags; } + + /// Extracts group and element from a key of the form "Dgroup_elem" + static void GetDicomGroupElementFromKey(const std::string& key, + unsigned short& group, + unsigned short& elem); + /// Cleans the name: + /// Replace simple quote by double quotes + /// Cut string at NULL chars + void CleanName(std::string& str) const; + ///Decodes the type of attribute into the existing ones + void DecodeType(unsigned int& type) const; + + /// Determines if Attribute is a date + bool isDateEntry() const; + + /// Determines if Attribute is a time + bool isTimeEntry() const; + + private: + std::string mKey; + std::string mName; + unsigned short mGroup; + unsigned short mElement; + unsigned int mFlags; + }; + // EO class AttributeDescriptor + //===================================================================== + + + + } // EO namespace tree + +} // EO namespace creaImageIO + + + + + +#endif // #ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__ diff --git a/src/creaImageIOTreeAttributeMapType.h b/src/creaImageIOTreeAttributeMapType.h new file mode 100644 index 0000000..16cac8c --- /dev/null +++ b/src/creaImageIOTreeAttributeMapType.h @@ -0,0 +1,39 @@ +#ifndef __creaImageIOTreeAttributeMapType_h_INCLUDED__ +#define __creaImageIOTreeAttributeMapType_h_INCLUDED__ + +#include +#include +#include + +namespace creaImageIO +{ + + /** + * \ingroup Tree + */ + namespace tree + { + typedef std::map AttributeMapType; + + + + + } + +} + +//===================================================================== +inline std::ostream& operator<<(std::ostream& s, + const creaImageIO::tree::AttributeMapType& d) +{ + creaImageIO::tree::AttributeMapType::const_iterator i; + for (i=d.begin();i!=d.end();++i) + { + s << "'" << i->first << "'='" << i->second << "'" << std::endl; + } + return s; +} +//===================================================================== + + +#endif diff --git a/src/creaImageIOTreeComparators.cpp b/src/creaImageIOTreeComparators.cpp new file mode 100644 index 0000000..7d64496 --- /dev/null +++ b/src/creaImageIOTreeComparators.cpp @@ -0,0 +1 @@ +#include diff --git a/src/creaImageIOTreeComparators.h b/src/creaImageIOTreeComparators.h new file mode 100644 index 0000000..f606470 --- /dev/null +++ b/src/creaImageIOTreeComparators.h @@ -0,0 +1,225 @@ +#ifndef __creaImageIOTreeNodeComparators_h_INCLUDED__ +#define __creaImageIOTreeNodeComparators_h_INCLUDED__ + +#include +#include + +namespace creaImageIO +{ + + namespace tree + { + + + class Node; + + + /** + * \ingroup Tree + */ + //===================================================================== + /// Abstract definition of a comparator of Node + struct Comparator + { + virtual ~Comparator() {} + virtual bool operator() (Node* const & x, Node* const & y) = 0; + }; + //===================================================================== + + + + //===================================================================== + /// Abstract Comparator whose order can be reversed + struct ComparatorWithOrder : public Comparator + { + ComparatorWithOrder(bool reverse_order = false) + : mReverseOrder(reverse_order) {} + virtual ~ComparatorWithOrder() {} + + + virtual bool compare(Node* const &, Node* const &) = 0; + + virtual bool operator() (Node* const & x, Node* const & y) + { + if (mReverseOrder) return this->compare(y,x); + return this->compare(x,y); + }; + + bool mReverseOrder; + }; + //===================================================================== + + + + //===================================================================== + /// A Comparator which stores a vector of Comparators and + /// which performs lexicographical comparison + class LexicographicalComparator : public Comparator + { + public: + LexicographicalComparator(const std::string& name) + : mName(name) {} + ~LexicographicalComparator() {} + + const std::string& GetName() const { return mName; } + void SetName(const std::string& s) { mName = s; } + void Clear() { mComparator.clear(); } + void DeleteComparators() + { + std::vector::iterator i; + for (i =mComparator.begin(); + i!=mComparator.end(); + ++i) + { + delete *i; + } + mComparator.clear(); + } + void Add(Comparator* c) { mComparator.push_back(c); } + + bool operator() (Node* const & x, Node * const & y); + + private: + std::string mName; + std::vector mComparator; + }; + //===================================================================== + + + + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an int value + struct IntComparator : + public ComparatorWithOrder + { + IntComparator(const std::string& key, + bool reverse_order) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an float value + struct FloatComparator : + public ComparatorWithOrder + { + FloatComparator(const std::string& key, + bool reverse_order ) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== + /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as a string value + struct StringComparator : + public ComparatorWithOrder + { + StringComparator(const std::string& key, + bool reverse_order ) + : + ComparatorWithOrder(reverse_order), + mKey(key) + {} + + virtual bool compare(Node* const & x, Node* const & y); + + private: + std::string mKey; + }; + //=================================================================== + + //=================================================================== +#define INT_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public IntComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + IntComparator(FIELD,o) \ + {} \ + } + //================================================================== + + //=================================================================== +#define FLOAT_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public FloatComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + FloatComparator(FIELD,o) \ + {} \ + } + //=================================================================== + + //=================================================================== +#define STRING_FIELD_COMP(NAME,FIELD) \ + struct Node##NAME##Comparator : \ + public StringComparator \ + { \ + Node##NAME##Comparator(bool o = false) : \ + StringComparator(FIELD,o) \ + {} \ + } + //=================================================================== + + + + //=================================================================== + // Patient comparators + ///Compares the names of the patients + STRING_FIELD_COMP(PatientName,"A0010_0010"); + ///Compares the sex of the patients + STRING_FIELD_COMP(PatientSex, "A0010_0040"); + ///Compares the birthdays of the patients + STRING_FIELD_COMP(PatientBirthday, "A0010_0030"); + //=================================================================== + + //=================================================================== + // Study comparators + ///Compares the dates of the studies + STRING_FIELD_COMP(StudyDate,"A0008_0020"); + ///Compares the description of the studies + STRING_FIELD_COMP(StudyDescription,"A0008_1030"); + //=================================================================== + + //=================================================================== + // Series comparators + ///Compares the modality of the series + STRING_FIELD_COMP(Modality,"A0008_0060"); + ///Compares the description of the series + STRING_FIELD_COMP(SeriesDescription,"A0008_103E"); + ///Compares the date of the series + STRING_FIELD_COMP(SeriesDate,"A0008_0021"); + //=================================================================== + + //=================================================================== + // Image comparators + ///Compares the number of the images + INT_FIELD_COMP(ImageNumber,"A0020_0013"); + ///Compares the location of the images + FLOAT_FIELD_COMP(SliceLocation,"A0020_1041"); + ///Compares the filename of the images + STRING_FIELD_COMP(FullFileName,"FullFileName"); + //=================================================================== + } // namespace tree + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOComparators_h_INCLUDED__ diff --git a/src/creaImageIOTreeDescriptor.cpp b/src/creaImageIOTreeDescriptor.cpp new file mode 100644 index 0000000..8524794 --- /dev/null +++ b/src/creaImageIOTreeDescriptor.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include + + +#include + + +namespace creaImageIO +{ + + namespace tree + { + + //================================================================== + /// The attribute is hidden (not visible to user) + const unsigned int AttributeDescriptor::PRIVATE = 1; + /// The attribute enters in unique identifier constitution + const unsigned int AttributeDescriptor::IDENTIFIER = 2; + /// The attribute can be edited + const unsigned int AttributeDescriptor::EDITABLE = 3; + /// the attribute describes the node + const unsigned int AttributeDescriptor::LABEL = 4; + //================================================================== + + //================================================================== + Descriptor::Descriptor() + { + CreateLevel0Descriptor(); + } + //================================================================== + + //================================================================== + Descriptor::~Descriptor() + { + } + //================================================================== + + //================================================================== + void Descriptor::CreateLevel0Descriptor() + { + Add(LevelDescriptor("Root")); + } + //================================================================== + + //================================================================== + /// Creates the default descriptor + void Descriptor::CreateDefault() + { + // clears the existing one + Clear(); + + // Creates the level 0 descriptor + CreateLevel0Descriptor(); + // Creates the attribute "Name" + Add(AttributeDescriptor("Name","Name", + AttributeDescriptor::LABEL),0); + + // Patient level + Add(LevelDescriptor("Patient")); + Add(AttributeDescriptor("NumberOfChildren","#Series",0),1); // Number of Series + Add(AttributeDescriptor(0x0010,0x0010, // Patient name + AttributeDescriptor::LABEL),1); + Add(AttributeDescriptor(0x0010,0x0040),1); // Patient sex + Add(AttributeDescriptor(0x0010,0x0030),1); // Patient birthday + Add(AttributeDescriptor(0x0010,0x0020, // Patient ID + AttributeDescriptor::IDENTIFIER),1); + + // Study-series level + Add(LevelDescriptor("Series")); + Add(AttributeDescriptor("NumberOfChildren","#Images",0),2); // Number of images + Add(AttributeDescriptor(0x0008,0x0060, // Modality + AttributeDescriptor::LABEL),2); + Add(AttributeDescriptor(0x0008,0x1030),2); // Study Description + Add(AttributeDescriptor(0x0008,0x103E),2); // Description + Add(AttributeDescriptor(0x0008,0x0080),2); // Institution Name + Add(AttributeDescriptor(0x0008,0x0081),2); // Institution Adress + Add(AttributeDescriptor(0x0008,0x1010),2); // Station Name + Add(AttributeDescriptor(0x0008,0x1048),2); // Physician of Record + Add(AttributeDescriptor(0x0008,0x1050),2); // Performing Physician's Name + Add(AttributeDescriptor(0x0018,0x1030),2); // Protocol Name + + Add(AttributeDescriptor(0x0020,0x0010),2); // Study ID + Add(AttributeDescriptor(0x0008,0x0020),2); // Study Date + Add(AttributeDescriptor(0x0008,0x0030),2); // Study Time + Add(AttributeDescriptor(0x0008,0x0050),2); // Study Accession Number + Add(AttributeDescriptor(0x0008,0x0005),2); // Specific character set + Add(AttributeDescriptor(0x0008,0x0021),2); // Series Date + Add(AttributeDescriptor(0x0008,0x0031),2); // Series time + + Add(AttributeDescriptor(0x0020,0x000D // Study Instance UID + ),2);//AttributeDescriptor::IDENTIFIER),2); + Add(AttributeDescriptor(0x0020,0x000E, // Series Instance UID + AttributeDescriptor::IDENTIFIER),2); + // | + // AttributeDescriptor::LABEL),2); + + + // Image level + Add(LevelDescriptor("Image")); + + Add(AttributeDescriptor(0x0020,0x0013),3); // Image Number + + Add(AttributeDescriptor(0x0028,0x0010),3); // Rows + Add(AttributeDescriptor(0x0028,0x0011),3); // Columns + Add(AttributeDescriptor(0x0028,0x0012),3); // Planes + Add(AttributeDescriptor(0x0028,0x0002),3); // Sample per pixels + Add(AttributeDescriptor(0x0028,0x0008),3); // Number of Frames + Add(AttributeDescriptor(0x0028,0x0004),3); // Photometric Interpretation + Add(AttributeDescriptor(0x0028,0x0103),3); // Pixel Representation + + Add(AttributeDescriptor(0x0020,0x0032),3); // Image Position Patient + Add(AttributeDescriptor(0x0020,0x0037),3); // Image Orientation Patient + Add(AttributeDescriptor(0x0020,0x1041),3); // Slice Location + Add(AttributeDescriptor(0x0028,0x0006),3); // Planar Configuration + + Add(AttributeDescriptor(0x0028,0x0030),3); // Pixel Spacing + Add(AttributeDescriptor(0x0028,0x0100),3); // AlocatedBits + Add(AttributeDescriptor(0x0028,0x0101),3); // StoredBits + + Add(AttributeDescriptor(0x0008,0x0008),3); // Image Type + Add(AttributeDescriptor(0x0008,0x0023),3); // Content Date + Add(AttributeDescriptor(0x0008,0x0033),3); // Content Time + + Add(AttributeDescriptor(0x0020,0x4000),3); // Image Comments + + Add(AttributeDescriptor(0x0004,0x1500, // File Name + AttributeDescriptor::LABEL),3); + Add(AttributeDescriptor(0x0028,0x1052),3); // Rescale Intercept + Add(AttributeDescriptor(0x0028,0x1053),3); // Rescale Slope + + Add(AttributeDescriptor(0x0050,0x0004),3); // Calibration Image + + Add(AttributeDescriptor(0x0020,0x0052 // Frame Reference UID + ),3); + Add(AttributeDescriptor(0x0008,0x0016),3); // SOP Class UID + Add(AttributeDescriptor("FullFileName", // Full file name + "Full file name", + AttributeDescriptor::IDENTIFIER),3); + + } + + ////////////////////////////////////////////////////////////// + // create a descriptor (name, attributes...) from a file) // + // @param : file path // + // return : - // + ////////////////////////////////////////////////////////////// + void Descriptor::createDescriptorfromFile(const std::string &i_name) + { + Clear(); + + // read file and put in buffer + std::ifstream i_file(i_name.c_str()); + std::stringstream buffer; + buffer << i_file.rdbuf(); + std::string line; + bool bname; + int ilevel = -1; + + + while(std::getline(buffer, line)) + { + if(line =="") + { //increment levels. + ilevel++; + bname = true; + } + else if(bname) + { + // For each level, a name to describe it + Add(LevelDescriptor(line)); + bname = false; + } + else if(line.empty()) // to avoid end line + { + return; + } + else + { + // split line to find all tags + std::vector descriptors; + std::string separator = " "; + std::string::size_type last_pos = line.find_first_not_of(separator); + //find first separator + std::string::size_type pos = line.find_first_of(separator, last_pos); + while(std::string::npos != pos || std::string::npos != last_pos) + { + descriptors.push_back(line.substr(last_pos, pos - last_pos)); + last_pos = line.find_first_not_of(separator, pos); + pos = line.find_first_of(separator, last_pos); + } + + // By default, the last tag is at zero and not recorded but if take in count + unsigned int flag = 0; + if(descriptors.size() == 4) + { + std::stringstream val; + val << std::dec << descriptors[3]; + val>> flag; + } + + // if Dicom tag, use "group" and "element" descriptor + if(descriptors[0] == "D") + { std::stringstream val, val2; + unsigned short group; + unsigned short element; + val << std::dec << descriptors[1] ; + val >> std::hex >> group; + val2 << std::dec << descriptors[2]; + val2 >> std::hex >> element; + Add(AttributeDescriptor( group,element,flag), ilevel); + } + + else if(descriptors[0].find("#") != -1) + { + // commented line continue to next line + } + else + { boost::algorithm::replace_all(descriptors[2],"_"," "); + Add(AttributeDescriptor( descriptors[1].c_str(),descriptors[2].c_str(),flag), ilevel); + } + } + } + } + + //================================================================== + + //================================================================== + /// Adds a LevelDescriptor at the end of the list + void Descriptor::Add(const LevelDescriptor& d) + { + mLevelDescriptorList.push_back(d); + } + //================================================================== + + //================================================================== + /// Adds an AttributeDescriptor to level l + void Descriptor::Add(const AttributeDescriptor& d, int l) + { + mLevelDescriptorList[l].Add(d); + // TO DO : update DicomTagToName and NameToDicomTag map + } + //================================================================== + + //================================================================== + /// Clears the Descriptor + void Descriptor::Clear() + { + mLevelDescriptorList.clear(); + } + + //================================================================== + + //================================================================== + /// Builds the key to value map of all the attributes of the tree + void Descriptor::BuildAttributeMap( std::map& map ) const + { + map.clear(); + LevelDescriptorListType::const_iterator l; + for (l = GetLevelDescriptorList().begin(); + l!= GetLevelDescriptorList().end(); + ++l) + { + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = l->GetAttributeDescriptorList().begin(); + a!= l->GetAttributeDescriptorList().end(); + ++a) + { + map[a->GetKey()]=""; + } + } + } + } +} diff --git a/src/creaImageIOTreeDescriptor.h b/src/creaImageIOTreeDescriptor.h new file mode 100644 index 0000000..a7e5b94 --- /dev/null +++ b/src/creaImageIOTreeDescriptor.h @@ -0,0 +1,84 @@ +#ifndef __creaImageIOTreeDescriptor_h_INCLUDED__ +#define __creaImageIOTreeDescriptor_h_INCLUDED__ + +#include +#include + + +namespace creaImageIO +{ + using namespace std; + namespace tree + { + + /** + * \ingroup Tree + */ + + ///Descriptor of the structure of a Tree (number of levels, descriptors of each level, ...). + //Any Tree has at least one level (level 0) of name "Root" + + class Descriptor + { + public: + /// Ctor : creates the mandatory level 0 descriptor called "Root" + Descriptor(); + /// Destructor + ~Descriptor(); + + /// Loads from a xml description file + void LoadXML(const std::string& filename); + /// Creates the default descriptor + void CreateDefault(); + + /// Returns the number of levels of the tree + unsigned int GetNumberOfLevels() + { return mLevelDescriptorList.size(); } + + /// Returns the LevelDescriptor of a given level (const ref) + const LevelDescriptor& GetLevelDescriptor(int level) const + { return mLevelDescriptorList[level]; } + + /// Returns the AttributeDescriptorList of a given level (const ref) + const LevelDescriptor::AttributeDescriptorListType& + GetAttributeDescriptorList(int level) const + { return mLevelDescriptorList[level].GetAttributeDescriptorList(); } + + + /// Adds a LevelDescriptor at the end of the list + void Add(const LevelDescriptor&); + + /// Adds an AttributeDescriptor to level l + void Add(const AttributeDescriptor&, int l); + + + /// Builds the key to value map of all the attributes of the tree + void BuildAttributeMap( AttributeMapType& ) const; + + /// The type of LevelDescriptor container + typedef std::vector LevelDescriptorListType; + + /// Returns the list of tree levels (const) + const LevelDescriptorListType& GetLevelDescriptorList() const { return mLevelDescriptorList; } + + /// Clears the Descriptor + void Clear(); + + //Create Attribute Descriptors from a file + void createDescriptorfromFile(const std::string &i_file); + + private: + LevelDescriptorListType mLevelDescriptorList; + /// Creates the mandatory level 0 descriptor called "Root" + /// (assumes the list is empty) + void CreateLevel0Descriptor(); + + }; + // EO class Descriptor + //===================================================================== + } // EO namespace tree + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src/creaImageIOTreeHandler.h b/src/creaImageIOTreeHandler.h new file mode 100644 index 0000000..b84d0f9 --- /dev/null +++ b/src/creaImageIOTreeHandler.h @@ -0,0 +1,196 @@ +#ifndef __creaImageIOTreeHandler_h_INCLUDED__ +#define __creaImageIOTreeHandler_h_INCLUDED__ + +#include + +namespace creaImageIO +{ + + + //======================================================================= + //class TreeHandlerStatistics; + //======================================================================= + /** + * \ingroup Model + */ + //======================================================================= + + /// Abstract class which 'handles' a Tree structure + class TreeHandler + { + public: + + //==================================================================== + // typedef TreeHandlerStatistics Statistics; + //==================================================================== + + //==================================================================== + /// Ctor + TreeHandler() {} + /// Virtual dtor + virtual ~TreeHandler() {} + //==================================================================== + + //==================================================================== + /// Returns the Tree handled + tree::Tree& GetTree() { return mTree; } + /// Returns the Tree handled (const) + const tree::Tree& GetTree() const { return mTree; } + //==================================================================== + + //==================================================================== + // QUERY METHODS + /// Is the 'source' readable ? + virtual bool IsReadable() { return false; } + /// Is the 'source' writable ? + virtual bool IsWritable() { return false; } + //==================================================================== + + + //==================================================================== + // INITIALIZATION / FINALIZATION + //==================================================================== + + //==================================================================== + /// Opens an existing 'source' + // Default mode is read only + // If IsWritable and writable==true then opens in read/write mode + virtual bool Open(bool writable = false) { return false; } + /// Closes the 'source' + virtual bool Close() { return false; } + /// Creates a new 'source' + // Default mode is read only + // If IsWritable and writable==true then opens in read/write mode + virtual bool Create(bool writable = false) { return false; } + /// Destroys the 'source' + virtual bool Destroy() { return false; } + /// Begins a transaction + virtual void BeginTransaction(){} + ///Commits results and ends transaction + virtual void EndTransaction(){} + //==================================================================== + + + //==================================================================== + // READ METHODS + //==================================================================== + + + //==================================================================== + /// Returns the number of children of the Node *WITHOUT LOADING THEM* + // REM : The Tree itself is a Node and asking for its number of + // children returns the number of children of level 1. + virtual unsigned int GetNumberOfChildren(tree::Node* n) { return 0; } + //==================================================================== + + //==================================================================== + /// Returns the attribute requested. Useful for synchronization. + virtual void GetAttribute(std::string levelDescriptor, + std::string searchParam, + std::string searchVal, + std::string key, + std::string& result){} + //==================================================================== + + //==================================================================== + /// Recursively loads the children of node 'parent' until maxlevel + // is reached. + // If maxlevel <= 0 then loads all the sub-tree rooted at parent + // If parent == NULL or parent == tree then starts with the 'children' of + // the tree itself. + // Returns the total number of children loaded. + virtual int LoadChildren(tree::Node* parent, int maxlevel) + { return 0; } + //==================================================================== + + //==================================================================== + /// Unloads the Node and its descendants + // WITHOUT altering the source, e.g. the database + virtual void UnLoad(tree::Node* n) { return; } + //==================================================================== + + //==================================================================== + /// Returns the top level node id for the given search param and search value + virtual void GetTopLevelNodeId(const std::string& searchParam, + const std::string& searchValue, + std::string& parent_id){ return; } + ///==================================================================== + + + //==================================================================== + // WRITE METHODS : WORK ONLY IN WRITE MODE + //==================================================================== + typedef tree::Node::AttributeMapType AttributeMapType; + /// Adds a branch in the tree with the attributes provided + // returns the Level in the tree where the branch was connected + // (-1 for error, 0 for top level, etc. ) + // Of course the branch is loaded on exit + virtual int AddBranch( const AttributeMapType& ) { return -1; } + /// Removes the node and its descendants + virtual bool Remove(tree::Node*) { return false; } + /// Sets an attribute of a Node + virtual bool SetAttribute(tree::Node*, + const std::string& key, + const std::string& value) { return false; } + // Sets an attribute + virtual void SetAttribute(const std::string& levelDescriptor, + const std::string& key, + const std::string& value, + const std::string& searchParam, + const std::string& searchVal){} + //Deletes the tuple that matches the parameters given + virtual void DeleteTuple(std::string levelDescriptor, std::string key, std::string value){} + //Deletes the entries that match the parameters given + virtual void RemoveEntries(const std::string i_table, + const std::string i_attribute, + const std::string i_operand, + const std::string i_val){} + + //==================================================================== + + + private: + /// The handled tree + tree::Tree mTree; + + }; + // EO class TreeHandler + //======================================================================= + /* + //======================================================================= + /// Memorizes statistics on operations done by a tree handler + // (nodes created, removed, ...) + class TreeHandlerStatistics + { + public: + //==================================================================== + /// Ctor + TreeHandler(TreeHandler* tree) : mTreeHandler(tree) { Reset(); } + /// Dtor + ~TreeHandler() {} + /// Resets the stats + void Reset(); + /// Prints the stats + void Print(); + + /// + void CreateNode(int level) { mNumberCreatedNode[level]++; } + void DeleteNode(int level) { mNumberDeletedNode[level]++; } + + protected: + TreeHandler* mTreeHandler; + std::vector mNumberCreatedNode; + std::vector mNumberDeletedNode; + + + ///==================================================================== + }; + // EO class TreeHandlerStatistics + //======================================================================= + */ + +} // EO namespace creaImageIO + +// EOF +#endif + diff --git a/src/creaImageIOTreeHandlerImageAdder.cpp b/src/creaImageIOTreeHandlerImageAdder.cpp new file mode 100644 index 0000000..76e8530 --- /dev/null +++ b/src/creaImageIOTreeHandlerImageAdder.cpp @@ -0,0 +1,692 @@ +#include +#include +#include +#include +#include + + +namespace fs = boost::filesystem; +using boost::filesystem::path; +using boost::next; +using boost::prior; + + +using namespace crea; +using namespace boost; + +namespace creaImageIO +{ + //==================================================================== + // Ctor + TreeHandlerImageAdder::TreeHandlerImageAdder(TreeHandler* tree) + : mTreeHandler(tree) + { + } + // Dtor + TreeHandlerImageAdder::~TreeHandlerImageAdder() + { + } + //==================================================================== + + //==================================================================== + void TreeHandlerImageAdder::ConnectProgressObserver(ProgressCallbackType callback) + { + mProgressSignal.connect(callback); + } + //==================================================================== + + //===================================================================== + bool TreeHandlerImageAdder::IsHandledFile( const std::string& filename) + { + return (mReader.CanRead(filename)); + } + //===================================================================== + + //===================================================================== + void TreeHandlerImageAdder::AddFiles( const std::vector& filenames) + { + mProgress.Reset(); + + unsigned int nbf = filenames.size(); + std::vector::const_iterator i; + mSynchronizer->GetList(mCurrentDB); + for (i=filenames.begin();i!=filenames.end();++i) + { + + mProgress.IncNumberScannedFiles(); + if (IsHandledFile(*i)) + { + mProgress.IncNumberHandledFiles(); + if(mSynchronizer->isIndexed(*i)) + { + mSynchronizer->InsertAddOp((*i),"0","1",mCurrentDB); + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",(*i),mCurrentDB); + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey,(*i),"0",removedOn.str(),mCurrentDB); + AddFile(*i); + } + } + mProgressSignal(mProgress); + if (mProgress.GetStop()) break; + } + } + //===================================================================== + + //===================================================================== + void TreeHandlerImageAdder::AddDirectory( const std::string& directory, + bool recurse) + { + mProgress.Reset(); + std::stringstream files; + + std::stringstream rec; + rec<InsertAddOp(directory,rec.str(),"0",mCurrentDB); + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",directory,mCurrentDB); + mTreeHandler->BeginTransaction(); + mSynchronizer->GetList(mCurrentDB); + //AddDirectoryRecursor( directory, recurse, addKey ); + DicomImageScanner sc; + AddDirectoryRecursorScanner(directory, recurse, addKey, sc, false ); + + int nFiles=GetProgress().GetNumberAddedFiles(); + files<SetAttribute("FILES_ADDED","ADD_OPS",files.str(),"ADD_KEY",addKey,mCurrentDB); + mTreeHandler->EndTransaction(); + GimmickDebugMessage(3,mProgress< attr; + mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + + + mReader.ReadAttributes(filename,attr); + //// TO DO Create a function to test if the SOP Instance ID (0008,0018) is not already on DB + //// test befor if this attr is present on DB if not don't perform the test!!! + //bool bSOPIID = false; + //std::map::iterator it_att = attr.begin(); + //for(; it_att != attr.end(); it_att++) + //{ + // if (it_att->first == "D0008_0018") + // { + // bSOPIID = mTreeHandler->TestSOPIID(it_attr->second); + // break; + // } + //} + //if(bSOPIID) + // return; + + int lev = mTreeHandler->AddBranch(attr); + + // update the progress according to lev + if (levGetTree().GetNumberOfLevels()) + mProgress.IncNumberAddedFiles(); + } + //===================================================================== + + void TreeHandlerImageAdder::RemoveFile( tree::Node* node) + { + int n=node->GetNumberOfChildren(); + if(n>0) + { + RemoveFiles(node->GetChildrenList()); + } + else + { + std::string path=node->GetAttribute("FullFileName"); + //Gets the add key + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str()); + files=files-1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey,mCurrentDB); + //Sets the file as removed + mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",addKey,mCurrentDB); + } + } + + //===================================================================== + + void TreeHandlerImageAdder::RemoveFiles(const std::vector& nodes) + { + std::vector::const_iterator it; + for(it=nodes.begin();it!=nodes.end();++it) + { + int n=(*it)->GetNumberOfChildren(); + if(n>0) + { + RemoveFiles((*it)->GetChildrenList()); + } + else + { + std::string path=(*it)->GetAttribute("FullFileName"); + //Gets the add key + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str()); + files=files-1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey,mCurrentDB); + //Sets the file as removed + mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",addKey,mCurrentDB); + } + + } + } + + + + //======================================================================= + //===================================================================== +#if defined(USE_GDCM2) + + void TreeHandlerImageAdder::AddDirectoryRecursorScanner(const std::string &dirpath, + bool recursive,const std::string &addKey, DicomImageScanner i_sc, bool b_loaded) + { + GimmickDebugMessage(4,"Scanning '"< attr; + mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + std::string path = dirpath.c_str(); + i_sc.addDirectory(path, attr); + + + fs::directory_iterator end_itr; // default construction yields past-the-end + for ( fs::directory_iterator itr( dirpath ); + itr != end_itr; + ++itr ) + { + + // If is directory & recurse : do recurse + if ( fs::is_directory(itr->status()) ) + { + if (recursive) + { + AddDirectoryRecursorScanner( itr->string(), recursive, addKey, i_sc, true); + } + } + else + { + std::string parent_id; + // tTest if directory (and only it) exists or not. + bool valid = mSynchronizer->isIndexed(itr->string());//true;//=mTimestampHandler->AddDirectory(dirpath, itr->string(), lastModif, time(0),mCurrentDB); + if(valid) + { + + mProgress.IncNumberScannedFiles(); + boost::algorithm::replace_all( itr->string(), + INVALID_FILE_SEPARATOR , + VALID_FILE_SEPARATOR); + i_sc.ReadAttributes(itr->string(),attr); + mTreeHandler->GetTopLevelNodeId("FullFileName",itr->string(),parent_id); + mTreeHandler->AddBranch(attr); + mProgress.IncNumberHandledFiles(); + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey, itr->string(),"0",removedOn.str(),mCurrentDB); + + + mProgressSignal(mProgress); + if (mProgress.GetStop()) + { + //itr = end_itr; + break; + } + } + + } + } + } +#endif + //===================================================================== + void TreeHandlerImageAdder::AddDirectoryRecursor(const std::string &dirpath, + bool recursive, + const std::string &addKey) + { + GimmickDebugMessage(4,"Scanning '"<status()) ) + { + if (recursive) + { + AddDirectoryRecursor( itr->string(), recursive, addKey); + } + } + else + { + std::string parent_id; + // tTest if directory (and only it) exists or not. + bool valid = mSynchronizer->isIndexed(itr->string());//true;//=mTimestampHandler->AddDirectory(dirpath, itr->string(), lastModif, time(0),mCurrentDB); + if(valid) + { + mProgress.IncNumberScannedFiles(); + if (IsHandledFile(itr->string())) + { + mProgress.IncNumberHandledFiles(); + AddFile( itr->string() ); + mTreeHandler->GetTopLevelNodeId("FullFileName",itr->string(),parent_id); + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey, itr->string(),"0",removedOn.str(),mCurrentDB); + } + + mProgressSignal(mProgress); + if (mProgress.GetStop()) + { + //itr = end_itr; + break; + } + + } + } + } + + } + //======================================================================= + + + //======================================================================= + + void TreeHandlerImageAdder::CheckSyncDirectory(const std::string &dirpath, + bool recursive, + bool repair, + bool checkAttributes, + std::vector &i_ignorefiles, + std::vector & attsModified, + std::vector & newfiles) + { + if ( !fs::exists( dirpath ) ) return; + fs::directory_iterator end_itr; // default construction yields past-the-end + + for ( fs::directory_iterator itr( dirpath ); itr != end_itr; ++itr ) + { + // If is directory & recurse : do recurse + if ( fs::is_directory(itr->status()) ) + { + if (recursive) + { + CheckSyncDirectory( itr->string(), recursive, repair, checkAttributes, i_ignorefiles, attsModified, newfiles); + } + } + else + { + if (IsHandledFile(itr->string())) + { + bool bfound = false; + for(std::vector::iterator it_new = i_ignorefiles.begin(); it_new < i_ignorefiles.end(); ++it_new) + { + if((*it_new) == itr->string()) + { + bfound = true; + //Additional checking of attributes + if(checkAttributes) + { + CheckAttributes(repair,(*it_new),attsModified); + } + i_ignorefiles.erase(it_new); + break; + } + } + if(!bfound && i_ignorefiles.size()>0 ) + { + newfiles.push_back( itr->string() ); + } + } + } + } + } + + //======================================================================= + + //======================================================================= + + std::string TreeHandlerImageAdder::Synchronize(bool repair, bool checkAttributes) + { + std::vector fileList; + std::vector ignoreList; + std::vector newFiles; + std::vector attsModified; + std::stringstream mess; + std::vector::iterator iter; + + //Gets the list of added files + mSynchronizer->GetFileList(fileList,mCurrentDB); + + std::vector::iterator i; + //Actions to take if the user doesn't want to repair + if(!repair) + { + //Iterates to see if they are in sync + for(iter=fileList.begin();iter!=fileList.end();++iter) + { + mSynchronizer->GetIgnoredFiles((*iter).key,ignoreList); + bool rec=true; + if((*iter).recursive=="0"){rec=false;} + CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles); + } + + //Add to message the result of new found files + mess<<"New Files Found: "<0) + { + mess<<"Filenames: "<0) + { + mess<<"Filenames: "<0) + { + mess<<"Filenames: "<GetIgnoredFiles((*iter).key,ignoreList); + bool rec=true; + if((*iter).recursive=="0"){rec=false;} + CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles); + + //For the new files, add them + for (i=newFiles.begin();i!=newFiles.end();++i) + { + if (IsHandledFile(*i)) + { + std::stringstream removedOn; + removedOn<InsertIgnoreFile((*iter).key,(*i),"0",removedOn.str(),mCurrentDB); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",(*iter).key,mCurrentDB)).c_str()); + files=files+1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",(*iter).key,mCurrentDB); + AddFile(*i); + } + } + nf+=newFiles.size(); + newFiles.clear(); + + } + //Reports number of added files + mess<<"Files Added: "<0) + { + tree::Node* node; + mTreeHandler->LoadChildren(NULL,4); + for(i=ignoreList.begin();i!=ignoreList.end();++i) + { + FindNode(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",*i,node); + RemoveFile(node); + mTreeHandler->Remove(node); + } + } + mess<<"Files Removed: "<& attsModified) + { + std::map< std::string, std::string> attr; + mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + mReader.ReadAttributes(file,attr); + tree::LevelDescriptor::AttributeDescriptorListType adl= mTreeHandler->GetTree().GetAttributeDescriptorList(mTreeHandler->GetTree().GetNumberOfLevels()-1); + tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = adl.begin();a!=adl.end();++a) + { + std::string databaseVal; + mTreeHandler->GetAttribute("Image","FullFileName",file,a->GetKey(),databaseVal); + std::string fileVal=attr.find(a->GetKey())->second; + if ( a->GetFlags()==0 && databaseVal == fileVal) + { + if(repair) + { + mTreeHandler->SetAttribute("Image",a->GetKey(),fileVal,"FullFileName", file); + } + attsModified.push_back(file); + } + + } + + } + + //======================================================================= + + + void TreeHandlerImageAdder::FindNode(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node) + { + if(level>1) + { + std::vector::iterator iter; + for(iter=parent->GetChildrenList().begin();iter!=parent->GetChildrenList().end();++iter) + { + FindNode(*iter,level-1,searchParam,searchVal,node); + } + } + else + { + if(parent->GetAttribute(searchParam).compare(searchVal)==0) + { + node=parent; + } + + } + } + + void TreeHandlerImageAdder::SaveAs(const std::vector& filenames, std::vector i_images) + { + std::vector::const_iterator it_file = filenames.begin(); + std::vector::iterator it_image = i_images.begin(); + /* mWriter.CanWrite(".jpeg"); + for(; it_file != filenames.end(); ++it_file, ++it_image) + mWriter.WriteImage(it_file->c_str(), (vtkImageData &)it_image);*/ + } + + //======================================================================= + void TreeHandlerImageAdder::FindNodePartial(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node) + { + if(level>1) + { + std::vector::iterator iter; + for(iter=parent->GetChildrenList().begin();iter!=parent->GetChildrenList().end() && node==0 ;++iter) + { + FindNodePartial(*iter,level-1,searchParam,searchVal,node); + } + } + else + { + if(parent->GetAttribute(searchParam).find(searchVal)<9000) + { + node=parent; + return; + } + + } + } + + //======================================================================= + + void TreeHandlerImageAdder::CopyFiles(const std::vector& filenames, const std::string directory ) + { + std::vector::const_iterator i; + if(!boost::filesystem::exists(directory)) + { + boost::filesystem::create_directory(boost::filesystem::path(directory)); + mSynchronizer->InsertAddOp(directory,"0","0",mCurrentDB); + } + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",directory,mCurrentDB); + size_t last; + std::vector newNames; + for(i=filenames.begin();i!=filenames.end();++i) + { + std::string dir=directory.c_str(); + if(boost::filesystem::exists(*i) && (*i).find(dir)==std::string::npos) + { + std::string path=*i; + last=(*i).find_last_of('/'); + std::string f="\\"+(*i).substr(last+1); + + int p=1; + std::stringstream out; + out<SetAttribute("Image","FullFileName",result,"FullFileName", (*i)); + + //To update maintenance database + //1.Add the new path and increase number of children on new operation. + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey, result,"0",removedOn.str(),mCurrentDB); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str()); + files=files+1; + std::stringstream fil; + fil<SetAttribute("FILES_ADDED","ADD_OPS",fil.str(),"ADD_KEY",addKey,mCurrentDB); + fil.str(""); + + //2.Set the old path as removed and decrease number of children on old operation. + //Gets the old add key + std::string oldAddKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB); + //Sets the file as removed + mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",oldAddKey,mCurrentDB); + //Gets the number of files added + files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",oldAddKey,mCurrentDB)).c_str()); + files=files-1; + fil<SetAttribute("FILES_ADDED","ADD_OPS",fil.str(),"ADD_KEY",oldAddKey,mCurrentDB); + + } + + } + } + + //======================================================================= + + void TreeHandlerImageAdder::DeleteDriveFromMainDB(const std::string& drive) + { + //Delete from local database and others + tree::Node* node=0; + mTreeHandler->LoadChildren(NULL,4); + FindNodePartial(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",drive,node); + while(node!=0) + { + mTreeHandler->Remove(node); + node=0; + mTreeHandler->LoadChildren(NULL,4); + FindNodePartial(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",drive,node); + } + } + + //======================================================================= + + void TreeHandlerImageAdder::DeleteDriveFromOtherDB(const std::string& drive) + { + //Delete from maintenance + mSynchronizer->RemoveEntries("ADD_OPS", "PATH", "LIKE", drive+"%"); + mSynchronizer->RemoveEntries("IGNORED_FILES", "PATH", "LIKE", drive+"%"); + } + + //======================================================================= + void TreeHandlerImageAdder::EditField(tree::Node* node, const std::string& name, const std::string& key, const std::string& val) + { + node->SetAttribute(key,val); + mTreeHandler->SetAttribute(node,key,val); + } + + //======================================================================= + void TreeHandlerImageAdder::GetAttributes(const std::vector& params, + const std::string& filename, + std::vector& results) + { + std::vector::const_iterator i; + std::string result; + for(i=params.begin();i!=params.end();i++) + { + mTreeHandler->GetAttribute("Image","FullFileName",filename,*i,result); + results.push_back(result); + } + } + + +} diff --git a/src/creaImageIOTreeHandlerImageAdder.h b/src/creaImageIOTreeHandlerImageAdder.h new file mode 100644 index 0000000..252e788 --- /dev/null +++ b/src/creaImageIOTreeHandlerImageAdder.h @@ -0,0 +1,209 @@ +#ifndef __creaImageIOTreeHandlerImageAdder_h_INCLUDED__ +#define __creaImageIOTreeHandlerImageAdder_h_INCLUDED__ + +#include +#include +#include +//#include + +// Signal/slot mechanism for progress events +#include +#include +#if defined(USE_GDCM2) +#include "creaImageIODicomScanner.h" +#endif + +namespace creaImageIO +{ + /** + * \ingroup Model + */ + + //======================================================================= + /// Object which can add images files to a TreeHandler. Is able to parse (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch) + + class TreeHandlerImageAdder + { + + public: + //==================================================================== + /// Ctor + TreeHandlerImageAdder(TreeHandler* tree); + /// Dtor + ~TreeHandlerImageAdder(); + /// Sets the TreeHandler + void SetTreeHandler(TreeHandler* tree) { mTreeHandler = tree;} + + /// Sets the synchronizer + void SetSynchronizer(Synchronizer* s){mSynchronizer=s;} + /// Sets the synchronizer + void SetCurrentDatabase(std::string cur){mCurrentDB=cur;} + //==================================================================== + + //==================================================================== + /// Structure which holds progress information + /// To stop the image adder use SetStop() + class Progress + { + public: + Progress() { Reset(); } + ~Progress() {} + + void Reset() + { + mStop = false; + mNumberScannedFiles = 0; + mNumberScannedDirs = 0; + mNumberHandledFiles = 0; + mNumberAddedFiles = 0; + } + + int GetNumberScannedFiles() const { return mNumberScannedFiles; } + int GetNumberScannedDirs() const { return mNumberScannedDirs; } + int GetNumberHandledFiles() const { return mNumberHandledFiles; } + int GetNumberAddedFiles() const { return mNumberAddedFiles; } + + void IncNumberScannedFiles() { mNumberScannedFiles++; } + void IncNumberScannedDirs() { mNumberScannedDirs++; } + void IncNumberHandledFiles() { mNumberHandledFiles++; } + void IncNumberAddedFiles() { mNumberAddedFiles++; } + + void SetStop() { mStop = true; } + bool GetStop() const { return mStop; } + + private: + bool mStop; + int mNumberScannedFiles; + int mNumberScannedDirs; + int mNumberHandledFiles; + int mNumberAddedFiles; + }; + //============================================= + + //============================================= + const Progress& GetProgress() const { return mProgress; } + //============================================= + + //============================================= + typedef boost::signal ProgressSignalType; + typedef ProgressSignalType::slot_function_type ProgressCallbackType; + //============================================= + + //================================================================== + /// Adds the function f to the list of functions to call + /// when the addition progresses. + /// f is of type ProgressCallbackType which is: + /// void (*ProgressCallbackType)(Progress&) + /// To pass a member function 'f' of an instance 'c' of a class 'C' + /// as callback you have to 'bind' it, i.e. call: + /// ConnectProgressObserver ( boost::bind( &C::f , c, _1 ) ); + void ConnectProgressObserver(ProgressCallbackType callback); + //================================================================== + + //==================================================================== + /// Returns if the file can be read or not + bool IsHandledFile( const std::string& filename); + /// Adds a list of files to the TreeHandler + void AddFiles( const std::vector& filename ); + /// (Recursively) adds the files of a directory to the TreeHandler + void AddDirectory( const std::string& directory, + bool recurse); + + +#if defined(USE_GDCM2) + void AddDirectoryRecursorScanner(const std::string &dirpath, + bool recursive,const std::string &addKey, DicomImageScanner i_sc, bool b_loaded); +#endif + + /// Removes a file from the databases + void RemoveFile(tree::Node* node); + /// Removes files from the databases + void RemoveFiles(const std::vector& nodes); + /// Synchronizes the DB and disk by repeating the operations the user has done and returns a report + std::string Synchronize(bool repair, bool checkAttributes); + ///Recursively checks if the directory is synchronized and optionally the state of the attributes + void CheckSyncDirectory(const std::string &dirpath, + bool recursive, + bool repair, + bool checkAttributes, + std::vector &i_ignorefiles, + std::vector & attsModified, + std::vector & newfiles); + ///Copies the files indicated in the vector and updates all databases + void CopyFiles(const std::vector& filenames, const std::string directory ); + + ///Saves as the files indicated in the vector in a specific directory + void SaveAs(const std::vector& filenames, std::vector i_images); + ///Finds the node that matches the specified parameters + void FindNode(tree::Node* parent, int level, + const std::string& searchParam, + const std::string& searchVal, + tree::Node*& node); + + ///Finds the nodes that partially match the searchVal + void FindNodePartial(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node); + + ///Checks the attributes of the database against the ones in disk + void CheckAttributes(bool repair, std::string& file, std::vector& attsModified); + ///Deletes the drive with the given name (use for all databases except maintenance and timestamp) + void DeleteDriveFromMainDB(const std::string& drive); + ///Deletes the drive with the given name (use for maintenance and timestamp databases) + void DeleteDriveFromOtherDB(const std::string& drive); + ///Edits the given field and sets the new parameters + void EditField(tree::Node* node, const std::string& name, const std::string& key, const std::string& val); + ///Returns the demanded attributes for the given file + void GetAttributes(const std::vector& params, + const std::string& filename, + std::vector& results); + + + + + //==================================================================== + + private: + + /// Adds a single file to the TreeHandler + /// **WITHOUT** testing wether it is handled or not + /// hence you have to call IsHandledFile before using AddFile! + void AddFile( const std::string& filename ); + + /// Recursive method which does the main job for AddDirectory + void AddDirectoryRecursor( const std::string& directory, + bool recurse, + const std::string &addKey); + + TreeHandler* mTreeHandler; + Synchronizer* mSynchronizer; + ImageReader mReader; + //ImageWriter mWriter; + std::string mCurrentDB; + + Progress mProgress; + ProgressSignalType mProgressSignal; + + + }; + // EO class TreeHandlerImageAdder + //======================================================================= + + + + + +} // EO namespace creaImageIO + +#include +inline std::ostream& operator << ( std::ostream& o, + const creaImageIO::TreeHandlerImageAdder::Progress& p) +{ + o << p.GetNumberScannedFiles() << " files - " + << p.GetNumberScannedDirs() << " dirs - " + << p.GetNumberHandledFiles() << " handled -" + << p.GetNumberAddedFiles() << " added"; + return o; +} + +// EOF +#endif + diff --git a/src/creaImageIOTreeLevelDescriptor.cpp b/src/creaImageIOTreeLevelDescriptor.cpp new file mode 100644 index 0000000..46eb41a --- /dev/null +++ b/src/creaImageIOTreeLevelDescriptor.cpp @@ -0,0 +1,32 @@ +#include +#include + + +namespace creaImageIO +{ + namespace tree + { + /// Adds the AttributeDescriptor to the list + void LevelDescriptor::Add(const AttributeDescriptor& a) + { + GimmickMessage(5,"Adding Attribute Descriptor '"< +#include + +namespace creaImageIO +{ + + namespace tree + { + /** + * \ingroup Tree + */ + //===================================================================== + /// Descriptor of a level of a Tree (name, attributes, ...) + class LevelDescriptor + { + public: + /// Ctor with name + LevelDescriptor(const std::string& name) : mName(name) {} + /// Destructor + ~LevelDescriptor() {} + + /// Returns the name of the level + const std::string& GetName() const { return mName; } + + /// Returns the number of attributes of the level + unsigned int GetNumberOfAttributes() const + { return mAttributeDescriptorList.size(); } + + /// Adds the AttributeDescriptor to the list + void Add(const AttributeDescriptor&); + + /// The type of attribute container + typedef std::vector AttributeDescriptorListType; + + /// Returns the list of AttributeDescriptor (const) + const AttributeDescriptorListType& GetAttributeDescriptorList() const + { return mAttributeDescriptorList; } + + /// \return The list of attributes with flag IDENTIFIER set + const std::vector& GetIdentifierList() const + { return mIdentifierList; } + /// \return The list of attributes with flag LABEL set + const std::vector& GetLabelList() const + { return mLabelList; } + + private: + std::string mName; + AttributeDescriptorListType mAttributeDescriptorList; + /// The list of attributes with flag IDENTIFIER set + std::vector mIdentifierList; + /// The list of attributes with flag LABEL set + std::vector mLabelList; + + }; + // EO class LevelDescriptor + //===================================================================== + }// EO namespace tree + + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src/creaImageIOTreeNode.cpp b/src/creaImageIOTreeNode.cpp new file mode 100644 index 0000000..5d892af --- /dev/null +++ b/src/creaImageIOTreeNode.cpp @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include + +namespace creaImageIO +{ + namespace tree + { + + //============================================================= + /// Ctor with parent + Node::Node(Node* parent) + : mParent(parent),//mData(0), + mChildrenLoaded(false) + { + mData.reset(); + if (parent) + { + GimmickDebugMessage(6,"Default Node constructor (level "<GetChildrenList().push_back(this); + } + else + { + GimmickDebugMessage(6,"Default Node constructor without parent" + << std::endl); + } + } + //============================================================= + + //============================================================= + /// Ctor with parent and attributes map + Node::Node(Node* parent, const AttributeMapType& attr) + : mParent(parent),//mData(0), + mChildrenLoaded(false) + { + mData.reset(); + GimmickDebugMessage(6,"Node constructor (level "<GetChildrenList().push_back(this); + // Initialize attributes + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin(); + a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end(); + ++a) + { + std::string v; + AttributeMapType::const_iterator i = attr.find(a->GetKey()); + if ( i != attr.end() ) + { + v = i->second; + } + GimmickDebugMessage(6,"Setting attribute '"<GetName()<<"' = '" + <GetKey(), v ); + } + } + + } + //============================================================= + + + //============================================================= + Node::~Node() + { + GimmickDebugMessage(6,"Node destructor" + << std::endl); + ChildrenListType::iterator i; + for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++) + { + delete *i; + } + mData.reset(); + } + //============================================================= + + + //============================================================= + /// Initializes the attribute map i.e. creates the entries + void Node::InitializeAttributeMap() + { + // Initialize attributes + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin(); + a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end(); + ++a) + { + UnsafeSetAttribute( a->GetKey(), "" ); + } + } + //============================================================= + + //============================================================= + /// Returns the level descriptor of the node + const LevelDescriptor& Node::GetLevelDescriptor() const + { + return GetTree()->GetLevelDescriptor(GetLevel()); + } + + //============================================================= + + //============================================================= + /// Returns the attribute descriptor of the passed parameter + const AttributeDescriptor& Node::GetAttributeDescriptor(const std::string& k)const + { + LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin(); + a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end(); + ++a) + { + + if(a->GetKey()==k) + { + return *a; + } + + } + return *a; + } + //============================================================= + + //============================================================= + int Node::RemoveChildrenFromList(Node* node) + { + ChildrenListType::iterator i = find(GetChildrenList().begin(), + GetChildrenList().end(), + node); + if (i != GetChildrenList().end()) + { + GetChildrenList().erase(i); + } + return GetChildrenList().size(); + } + //============================================================= + + //============================================================= + const std::string& Node::GetAttribute(const std::string& k) const + { + // std::cout << "this = "<<(void*)this<second; + } + //============================================================= + + //============================================================= + void Node::SetAttribute(const std::string& k, + const std::string& v) + { + AttributeMapType::iterator i = mAttributeMap.find(k); + if (i==mAttributeMap.end()) + { + std::cout<<"[Gimmick!] Node::SetAttribute : no attribute with key '" + <second = v; + } + //============================================================= + + //============================================================= + bool Node::Matches( const AttributeMapType& m ) const + { + GimmickDebugMessage(2,"'"<& id + = GetLevelDescriptor().GetIdentifierList(); + std::vector::const_iterator i; + for (i = id.begin(); i != id.end(); ++i) + { + if (mAttributeMap.find(*i)->second != m.find(*i)->second ) + { + GimmickDebugMessage(2,"IDENTIFIER '"<<*i<<"' values do not match"<Print(); + } + } + //============================================================= + + //============================================================= + std::string Node::GetLabel() const + { + std::string l; + const std::vector& label + = GetLevelDescriptor().GetLabelList(); + + std::vector::const_iterator i; + for (i = label.begin(); i != label.end(); ) + { + GimmickDebugMessage(9,"LABEL '"<<*i<<"'"<second; + ++i; + if (i != label.end()) l += "|"; + } + else + { + GimmickError("Node::GetLabel() : LABEL attribute '" + <<*i + <<"' is not in node attribute map (should be!)" ); + } + } + if (l.size()==0) l="?"; + return l; + } + //============================================================= + + } + +} + diff --git a/src/creaImageIOTreeNode.h b/src/creaImageIOTreeNode.h new file mode 100644 index 0000000..2905c1a --- /dev/null +++ b/src/creaImageIOTreeNode.h @@ -0,0 +1,157 @@ +#ifndef __creaImageIOTreeNode_h_INCLUDED__ +#define __creaImageIOTreeNode_h_INCLUDED__ + +#include +#include +#include +#include +#include + + +namespace creaImageIO +{ + + namespace tree + { + /** + * \ingroup Tree + */ + //===================================================================== + /// Forward declaration of Tree + class Tree; + //===================================================================== + + //===================================================================== + /// Abstract class to store user data on a Tree node + struct NodeData + { + NodeData() {} + virtual ~NodeData() {} + }; + //===================================================================== + + + //===================================================================== + /// Node of an attributed Tree structure + class Node + { + public: + typedef std::map AttributeMapType; + + + /// Ctor with parent + Node(Node* parent); + /// Ctor with parent and attributes map + Node(Node* parent, const AttributeMapType& ); + /// Virtual destructor + virtual ~Node(); + + /// Initializes the attribute map i.e. creates the entries + void InitializeAttributeMap(); + + /// Returns the level descriptor of the node + const LevelDescriptor& GetLevelDescriptor() const; + + + /// Returns the tree to which the node belongs + virtual Tree* GetTree() { return mParent->GetTree(); } + /// Returns the tree to which the node belongs + virtual const Tree* GetTree() const { return mParent->GetTree(); } + /// Returns the level of the node in the tree + virtual int GetLevel() const { return mParent->GetLevel()+1; } + + + /// Returns the parent of the node + Node* GetParent() const { return mParent; } + + /// Returns the number of children of the node. + /// Warning : if the children are not loaded then might return 0 + /// even if the node has children ! + /// see TreeHandler::GetNumberOfChildren + unsigned int GetNumberOfChildren() const { return mChildren.size(); } + + /// Returns true iff the node's children are loaded + bool GetChildrenLoaded() const { return mChildrenLoaded; } + + /// Sets the node's children + void SetChildrenLoaded(bool l) { mChildrenLoaded = l; } + + /// The type of children container + typedef std::vector ChildrenListType; + /// Returns the list of children + ChildrenListType& GetChildrenList() { return mChildren; } + /// Returns the list of children (const) + const ChildrenListType& GetChildrenList() const { return mChildren; } + + /// Remove the given children from the children list + int RemoveChildrenFromList(Node*); + + + /// Get the Attributes Map + AttributeMapType& GetAttributeMap() { return mAttributeMap; } + + /// Get the Attributes Map + const AttributeMapType& GetAttributeMap() const { return mAttributeMap; } + + /// Get the Attribute for a specific key + const std::string& GetAttribute(const std::string& k) const; + + /// Get the Attribute for a specific key without OS dependance (not implemented) + // TODO : backslash OS uniformity + const std::string& GetCleanAttribute(const std::string& k) const; + + /// Set an Attribute for a specific key + void SetAttribute(const std::string& k, const std::string& v); + + /// Set an Attribute for a specific key(unsafe mode) + void UnsafeSetAttribute(const std::string& k, const std::string& v) + { mAttributeMap[k] = v; } + + /// Get Descriptor for an Attribute + const AttributeDescriptor& GetAttributeDescriptor(const std::string& k)const; + + /// Returns true if the KEY attributes of the node match those of the map provided + bool Matches( const AttributeMapType& ) const; + + /// Returns the node data casted into the type T + template T GetData() const + { if (mData!=0) return dynamic_cast(mData); return 0; } + + /// Sets the node data. Deletes existing data if any. + void SetData(boost::shared_ptr d) {mData.reset(); mData = d; }//{ if (mData) delete mData; mData = d; } + + /// Sorts the children of the node + void SortChildren(const LexicographicalComparator&); + + /// Print the node + virtual void Print() const; + + /// Get the Label of the node + std::string GetLabel() const; + + + private: + /// The parent of the node + Node* mParent; + /// The list of children + ChildrenListType mChildren; + /// The map of attributes + AttributeMapType mAttributeMap; + /// User data + boost::shared_ptr mData; + /// Are the children loaded ? + bool mChildrenLoaded; + /// The number of children + // int mNumberOfChildren; + + }; // class Node + //===================================================================== + + } // namespace tree + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOTreeNode_h_INCLUDED__ diff --git a/src/creaImageIOTreeView.cpp b/src/creaImageIOTreeView.cpp new file mode 100644 index 0000000..b0820d3 --- /dev/null +++ b/src/creaImageIOTreeView.cpp @@ -0,0 +1,32 @@ +#include +#include + + +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + // CTor + TreeView::TreeView(TreeHandler* handler, GimmickView* gimmick ) + : mTreeHandler(handler), + mGimmickView(gimmick) + { + GimmickDebugMessage(1,"TreeView::TreeView" + < +#include +#include +#include + +namespace creaImageIO +{ + + class GimmickView; + + /** + * \ingroup View + */ + + //===================================================================== + + //===================================================================== + /// Abstract class that handles the view of a Tree through its TreeHandler + class TreeView + { + public: + /// Ctor + TreeView(TreeHandler*,GimmickView*); + /// Virtual destructor + virtual ~TreeView(); + + + /// Updates the view of a level given the selected items of upper level + virtual void UpdateLevel( int ) + { GimmickError("INTERNAL ERROR : TreeView::UpdateLevel not overloaded");} + + ///Removes selected nodes + virtual void RemoveSelected(std::string &i_save ) + { GimmickError("INTERNAL ERROR : TreeView::RemoveSelected not overloaded");} + + ///Validates the selected images + virtual void ValidateSelectedImages() + { GimmickError("INTERNAL ERROR : TreeView::ValidateSelected not overloaded");} + + ///Returns the last selected level + virtual unsigned int GetLastSelectedLevel(){GimmickError("INTERNAL ERROR : TreeView::GetLastSelectedLevel not overloaded");} + + ///Returns the maximum number of levels + virtual int GetNumberOfLevels(){ GimmickError("INTERNAL ERROR : TreeView::GetLevels not overloaded"); } + ///Gets the current selections filenames + virtual void GetSelectedAsString(std::vector&s){ GimmickError("INTERNAL ERROR : TreeView::GetSelectedAsString not overloaded"); } + + /// Gets the user selected data from the level passed as a parameter + virtual const std::vector& GetSelected(int level){ GimmickError("INTERNAL ERROR : TreeView::GetSelected not overloaded"); } + + /// Gets the next nodes on the list, be it up(true) or down(false). + virtual void GetNodes(std::vector& nodes, bool direction){ GimmickError("INTERNAL ERROR : TreeView::GetNodes not overloaded"); } + + /// Gets the attributes that are being shown and the ones that have been blocked on a specific level + virtual void GetAttributes(std::vector& areShown, std::vector& notShown, int level){ GimmickError("INTERNAL ERROR : TreeView::GetAttributes not overloaded"); } + + ///Sets the non visible attributes and refreshes the GUI + virtual void SetNonVisibleAttributes(const std::vector& notShown, int level){ GimmickError("INTERNAL ERROR : TreeView::SetNonVisibleAttributes not overloaded"); } + + ///Creates a new listctrl + virtual void CreateCtrl(std::vector& notShown, int nlevel){ GimmickError("INTERNAL ERROR : TreeView::CreateCtrl not overloaded"); } + + protected: + TreeHandler* GetTreeHandler() { return mTreeHandler; } + GimmickView* GetGimmickView() { return mGimmickView; } + + private: + /// The TreeHandler with which it corresponds + TreeHandler* mTreeHandler; + /// The GimmickView which holds the TreeView + GimmickView* mGimmickView; + + }; + // EO class TreeView + //===================================================================== + + +} // EO namespace creaImageIO + +// EOF +#endif diff --git a/src/creaImageIOUltrasonixImageReader.cpp b/src/creaImageIOUltrasonixImageReader.cpp new file mode 100644 index 0000000..8eb476b --- /dev/null +++ b/src/creaImageIOUltrasonixImageReader.cpp @@ -0,0 +1,261 @@ + +#include "creaImageIOSystem.h" +#include "creaImageIOUltrasonixImageReader.h" +#include +#include +namespace creaImageIO +{ +#define HEADER_SIZE 19 +#define TYPE_RF 16 +#define TYPE_B8 4 +#define TYPE_B32 8 + + + //===================================================================== + UltrasonixImageReader::UltrasonixImageReader() + { + SetName("Ultrasonix"); + } + //===================================================================== + + //===================================================================== + UltrasonixImageReader::~UltrasonixImageReader() + { + } + //===================================================================== + + //===================================================================== + struct Ultrasonix_header + { + // frames, width, height, ultrasounds frequency, sampling rate + int type, frame, width, height, frequency, samplingRate; + }; + //===================================================================== + + + //===================================================================== + bool ReadHeader( FILE *Ultrasonix_file, Ultrasonix_header& h ) + { + //int *header=(int*)malloc(sizeof(int)*HEADER_SIZE); + int header[HEADER_SIZE]; + fread(header, sizeof(int), HEADER_SIZE, Ultrasonix_file); + if (ferror(Ultrasonix_file)) + return false; + h.type = header[0]; + h.frame = header[1]; + h.height = header[2]; + h.width = header[3]; + h.frequency = header[14]; + h.samplingRate = header[15]; + //free(header); + return true; + } + //===================================================================== + + //===================================================================== + bool UltrasonixImageReader::CanRead(const std::string& filename) + { + long size; + bool ok = false; + FILE *Ultrasonix_file=fopen(filename.c_str(), "rb"); + if (Ultrasonix_file) + { + Ultrasonix_header h; + if (!ReadHeader(Ultrasonix_file, h) ) + { + fclose(Ultrasonix_file); + std::cout << "cannot read Ultrasonix header for file [" << filename << "]" << std::endl; + return false; + } + + fseek(Ultrasonix_file,0,SEEK_END); // go to end of file + if (h.type == TYPE_RF) + size = (ftell(Ultrasonix_file) - (HEADER_SIZE+h.frame) * sizeof(int)) / sizeof(short); + else if (h.type == TYPE_B8) + size = (ftell(Ultrasonix_file) - HEADER_SIZE * sizeof(int)) / sizeof(char); + else if (h.type == TYPE_B32) + size = (ftell(Ultrasonix_file) - HEADER_SIZE * sizeof(int)) / sizeof(int); + + // check if the data size corresponds to the dimensions of the images + if (size == h.width * h.height * h.frame) + ok = true; + + fclose(Ultrasonix_file); + } + return ok; + } + //===================================================================== + + //===================================================================== + vtkImageData* UltrasonixImageReader::ReadImage(const std::string& filename) + { + FILE *Ultrasonix_file=fopen(filename.c_str(),"rb"); + if (!Ultrasonix_file) + { + std::cout << "cannot open file [" << filename << "]" << std::endl; + return 0; + } + Ultrasonix_header h; + if (!ReadHeader(Ultrasonix_file,h)) + { + std::cout << "cannot read Ultrasonix header for file [" << filename << "]" << std::endl; + fclose(Ultrasonix_file); + return 0; + } + + long frame_size = h.height * h.width; + long im_size = frame_size * h.frame; + + short *dataRF, *ptrRF; + char *dataB8, *ptrB8; + int *dataB32, *ptrB32; + vtkImageData* im; + int temp; + + switch (h.type) + { + case TYPE_RF: + dataRF = (short*)malloc(sizeof(short)*im_size); + ptrRF = dataRF; + + for (int k=0; k& v) + { + v.push_back("Ultrasonix"); + } + //===================================================================== + + + + //===================================================================== + void UltrasonixImageReader::ReadAttributes(const std::string& filename, + std::map& attr) + { + GimmickMessage(2,"Reading attributes from '" << filename << std::endl); + + FILE *Ultrasonix_file = fopen(filename.c_str(), "rb"); + if (!Ultrasonix_file) + { + std::cout << "cannot open RF file [" << filename << "]" << std::endl; + return; + } + + Ultrasonix_header h; + if (!ReadHeader(Ultrasonix_file, h)) + { + fclose(Ultrasonix_file); + std::cout << "cannot read Ultrasonix Attributes for RF file [" << filename << "]" << std::endl; + return; + } + + fclose(Ultrasonix_file); + + // Columns + char cols[128]; + sprintf(cols,"%i", h.width); + // Rows + char rows[128]; + sprintf(rows,"%i", h.height); + // Planes + char planes[128]; + sprintf(planes,"%i", h.frame); + // Sampling frequency + char samplingFrequency[128]; + sprintf(samplingFrequency,"%i", h.samplingRate); + // Transducer frequency + char transducerFrequency[128]; + sprintf(transducerFrequency,"%i", h.frequency); + + // + std::map::iterator i; + if ( (i = attr.find("FullFileName")) != attr.end()) + { + i->second = filename; + } + if ( (i = attr.find("D0004_1500")) != attr.end()) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + if ( (i = attr.find("D0028_0010")) != attr.end()) + { + i->second = rows; + } + if ( (i = attr.find("D0028_0011")) != attr.end()) + { + i->second = cols; + } + if ( (i = attr.find("D0028_0012")) != attr.end()) + { + i->second = planes; + } + if ( (i = attr.find("D003a_001a")) != attr.end()) + { + i->second = samplingFrequency; + } + if ( (i = attr.find("D0018_6030")) != attr.end()) + { + i->second = transducerFrequency; + } + + GimmickMessage(2,"Attributes map:"< +#include + +namespace creaImageIO +{ + /** + * \ingroup IO + */ + + //===================================================================== + /// Concrete image reader for ultrasonix 'rf' files + class CREAIMAGEIO_EXPORT UltrasonixImageReader : virtual public AbstractImageReader + { + public: + UltrasonixImageReader(); + + virtual ~UltrasonixImageReader(); + /// Add file extensions read by the reader + virtual void PushBackExtensions(std::vector&); + /// Test if file is read by this reader + virtual bool CanRead(const std::string& filename); + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename); + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + + }; + //===================================================================== + +} // namespace creaImageIO + +#endif // #ifndef __creaImageIOUltrasonixImageReader_h_INCLUDED__ diff --git a/src/creaImageIOVtkImageReader.cpp b/src/creaImageIOVtkImageReader.cpp new file mode 100644 index 0000000..0d3780a --- /dev/null +++ b/src/creaImageIOVtkImageReader.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include "boost/filesystem/path.hpp" + +namespace creaImageIO{ + //===================================================================== + VtkImageReader::VtkImageReader(vtkImageReader2* r, + const std::string& name, + const std::string& extensions) + : mReader(r), mExtensions(extensions) + { + if (name.size() == 0) + { + const char *test =mReader->GetDescriptiveName(); + if(test != "") + { + SetName ( "toto");// mReader->GetDescriptiveName()); + } + + } + else + { + SetName ( name ); + } + GimmickDebugMessage(5,"Constructing vtkImageReader : "<Delete(); + } + //===================================================================== + + //===================================================================== + bool VtkImageReader::CanRead(const std::string& filename) + { + + return (mReader->CanReadFile(filename.c_str())!=0); +/* if(filename != "") + { + return (mReader->CanReadFile(filename.c_str())!=0); + } + else + { + return false; + }*/ + } + //===================================================================== + + //===================================================================== + vtkImageData* VtkImageReader::ReadImage(const std::string& filename) + { + vtkImageData* im = 0; + try + { + mReader->SetFileName(filename.c_str()); + mReader->Update(); + im = vtkImageData::New(); + im->ShallowCopy(mReader->GetOutput()); + } + catch (...) + { + if (im!=0) im->Delete(); + im = 0; + } + return im; + } + //===================================================================== + + //===================================================================== + void SplitExtensionsString ( const std::string& str, + const std::string& delimiters, + std::vector& tokens) + { + // Skip delimiters at beginning. + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first delimiter. + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) + { + // Found a token, add it to the vector. + // SPECIFIC : REMOVE INITIAL DOT (lastPos + 1) + tokens.push_back(str.substr(lastPos+1, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next delimiter + pos = str.find_first_of(delimiters, lastPos); + } + + } + //===================================================================== + + //===================================================================== + void VtkImageReader::PushBackExtensions(std::vector& v) + { + std::string ext = mExtensions; + if (ext.size()==0) ext = mReader->GetFileExtensions (); + + SplitExtensionsString(ext," ",v); + } + //===================================================================== + + + + //===================================================================== + void VtkImageReader::ReadAttributes(const std::string& filename, + std::map& attr) + { + GimmickMessage(2,"Reading attributes from '"<SetFileName(filename.c_str()); + mReader->Update(); //OpenFile(); + int ext[6]; + mReader->GetDataExtent(ext); + // Columns + char cols[128]; + sprintf(cols,"%i",ext[1]-ext[0]); + // Rows + char rows[128]; + sprintf(rows,"%i",ext[3]-ext[2]); + // Planes + char planes[128]; + sprintf(planes,"%i",ext[5]-ext[4]); + + std::map::iterator i; + if ( (i = attr.find("FullFileName")) != attr.end()) + { + i->second = filename; + } + if ( (i = attr.find("D0004_1500")) != attr.end()) + { + boost::filesystem::path full_path(filename); + std::string f = full_path.leaf(); + i->second = f; + } + if ( (i = attr.find("D0028_0010")) != attr.end()) + { + i->second = rows; + } + if ( (i = attr.find("D0028_0011")) != attr.end()) + { + i->second = cols; + } + + if ( (i = attr.find("D0028_0012")) != attr.end()) + { + i->second = planes; + } + if ( (i = attr.find("FullFileDirectory")) != attr.end()) + { + std::string::size_type last_pos = filename.find_last_of("//"); + i->second = filename.substr(0, last_pos); + } + + GimmickMessage(2,"Attributes map:"< + +// forward decl +class vtkImageReader2; + +namespace creaImageIO +{ + + + /** + * \ingroup IO + */ + + //===================================================================== + /// Concrete image reader based on a vtkImageReader2 + class VtkImageReader : virtual public AbstractImageReader + { + public: + VtkImageReader(vtkImageReader2* reader, + const std::string& name = "", + const std::string& extensions = ""); + + virtual ~VtkImageReader(); + + + + /// Add file extensions read by the reader + virtual void PushBackExtensions(std::vector&); + /// Test if file is read by this reader + virtual bool CanRead(const std::string& filename); + /// return for a file a 2D VTkImage + virtual vtkImageData* ReadImage(const std::string& filename); + /// Read the attributes for a file + virtual void ReadAttributes(const std::string& filename, + tree::AttributeMapType& attr); + + private: + vtkImageReader2* mReader; + std::string mExtensions; + }; + //===================================================================== + + + +} // namespace creaImageIO + + + +#endif // #ifndef __creaImageIOVtkImageReader_h_INCLUDED__ diff --git a/src/creaImageIOWxAttributeSelectionPanel.cpp b/src/creaImageIOWxAttributeSelectionPanel.cpp new file mode 100644 index 0000000..29538b9 --- /dev/null +++ b/src/creaImageIOWxAttributeSelectionPanel.cpp @@ -0,0 +1,193 @@ +#include +#include + +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif +namespace creaImageIO +{ + const int ID_COMBO = 180; + // CTor + WxAttributeSelectionPanel::WxAttributeSelectionPanel(wxWindow *parent, + wxDialog* dial, + WxGimmickView* view, + std::vector sAtts, + std::vector nsAtts, + int numLev) + : wxPanel( parent, + -1, wxDefaultPosition, + wxDefaultSize, + wxRESIZE_BORDER | + wxSYSTEM_MENU | + wxCLOSE_BOX | + wxMAXIMIZE_BOX | + wxMINIMIZE_BOX | + wxCAPTION + ), + dialog(dial), + shownA(sAtts), + notShownA(nsAtts), + mView(view) + { + GimmickDebugMessage(1,"WxCustomizeConfigPanel::WxCustomizeConfigPanel" + <