diff --git a/Modules/Python/QmitkPythonSnippets.cpp b/Modules/Python/QmitkPythonSnippets.cpp index 975944557c..b0947c81af 100644 --- a/Modules/Python/QmitkPythonSnippets.cpp +++ b/Modules/Python/QmitkPythonSnippets.cpp @@ -1,483 +1,483 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPythonSnippets.h" #include "QmitkPythonScriptEditorHighlighter.h" #include #include #include struct QmitkPythonSnippetsData { QString m_AutoSaveFileName; QString m_SaveFileName; QAction* m_PasteSnippet; QAction* m_RemoveSnippet; QAction* m_RenameSnippet; QAction* m_AddSnippet; QAction* m_RestoreDefaultSnippets; QAction* m_LoadSnippets; QAction* m_SaveSnippets; QToolBar* m_Toolbar; QComboBox* m_Name; QTextEdit* m_Content; QGridLayout* m_Layout; QmitkPythonSnippets::QStringMap m_Snippets; }; const QString QmitkPythonSnippets::DEFAULT_SNIPPET_FILE( ":/mitkPython/PythonSnippets.xml" ); const QString QmitkPythonSnippets::SNIPPETS_ROOT_XML_ELEMENT_NAME( "PythonSnippets" ); const QString QmitkPythonSnippets::SNIPPETS_XML_ELEMENT_NAME( "PythonSnippet" ); QmitkPythonSnippets::QmitkPythonSnippets( const QString& _AutoSaveFileName, QWidget* parent ) : QWidget(parent), d(new QmitkPythonSnippetsData) { d->m_SaveFileName = QDir::currentPath(); d->m_AutoSaveFileName = _AutoSaveFileName; - if( !this->LoadStringMap( d->m_AutoSaveFileName, d->m_Snippets ) ) + if( !QmitkPythonSnippets::LoadStringMap( d->m_AutoSaveFileName, d->m_Snippets ) ) { - this->LoadStringMap( DEFAULT_SNIPPET_FILE, d->m_Snippets ); + QmitkPythonSnippets::LoadStringMap( DEFAULT_SNIPPET_FILE, d->m_Snippets ); } d->m_PasteSnippet = new QAction(this); d->m_PasteSnippet->setObjectName(QString::fromUtf8("PasteSnippet")); QIcon icon; icon.addFile(QString::fromUtf8(":/mitkPython/edit-paste.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_PasteSnippet->setIcon(icon); d->m_PasteSnippet->setToolTip("Paste snippet!"); d->m_PasteSnippet->setEnabled(false); d->m_RemoveSnippet = new QAction(this); d->m_RemoveSnippet->setObjectName(QString::fromUtf8("RemoveSnippet")); QIcon icon1; icon1.addFile(QString::fromUtf8(":/mitkPython/edit-delete.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RemoveSnippet->setIcon(icon1); d->m_RemoveSnippet->setToolTip("Remove snippet."); d->m_RemoveSnippet->setEnabled(false); d->m_RenameSnippet = new QAction(this); d->m_RenameSnippet->setObjectName(QString::fromUtf8("RenameSnippet")); QIcon icon2; icon2.addFile(QString::fromUtf8(":/mitkPython/edit-find-replace.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RenameSnippet->setIcon(icon2); d->m_RenameSnippet->setToolTip("Rename snippet."); d->m_RenameSnippet->setEnabled(false); d->m_AddSnippet = new QAction(this); d->m_AddSnippet->setObjectName(QString::fromUtf8("AddSnippet")); QIcon icon3; icon3.addFile(QString::fromUtf8(":/mitkPython/document-new.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_AddSnippet->setIcon(icon3); d->m_AddSnippet->setToolTip("Add snippet."); d->m_RestoreDefaultSnippets = new QAction(this); d->m_RestoreDefaultSnippets->setObjectName(QString::fromUtf8("RestoreDefaultSnippets")); QIcon icon4; icon4.addFile(QString::fromUtf8(":/mitkPython/edit-clear.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RestoreDefaultSnippets->setIcon(icon4); d->m_RestoreDefaultSnippets->setToolTip("Restore default snippets"); d->m_LoadSnippets = new QAction(this); d->m_LoadSnippets->setToolTip("Load Snippets from disk."); d->m_LoadSnippets->setObjectName(QString::fromUtf8("LoadSnippets")); QIcon icon5; icon5.addFile(QString::fromUtf8(":/mitkPython/document-open.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_LoadSnippets->setIcon(icon5); d->m_SaveSnippets = new QAction(this); d->m_SaveSnippets->setToolTip("Save Snippets to disk."); d->m_SaveSnippets->setObjectName(QString::fromUtf8("SaveSnippets")); QIcon icon6; icon6.addFile(QString::fromUtf8(":/mitkPython/document-save.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_SaveSnippets->setIcon(icon6); d->m_SaveSnippets->setEnabled(false); d->m_Toolbar = new QToolBar; d->m_Toolbar->addAction( d->m_PasteSnippet ); d->m_Toolbar->addAction( d->m_AddSnippet ); d->m_Toolbar->addAction( d->m_RemoveSnippet ); d->m_Toolbar->addAction( d->m_RenameSnippet ); d->m_Toolbar->addAction( d->m_RestoreDefaultSnippets ); d->m_Toolbar->addAction( d->m_SaveSnippets ); d->m_Toolbar->addAction( d->m_LoadSnippets ); d->m_Name = new QComboBox; d->m_Name->setObjectName(QString::fromUtf8("Name")); d->m_Content = new QTextEdit(this); d->m_Content->setObjectName(QString::fromUtf8("Content")); d->m_Content->setEnabled(false); QmitkPythonScriptEditorHighlighter* highlighter = new QmitkPythonScriptEditorHighlighter( d->m_Content->document() ); d->m_Layout = new QGridLayout; d->m_Layout->addWidget( d->m_Toolbar, 0, 0, 1, 1 ); d->m_Layout->addWidget( d->m_Name, 1, 0, 1, 1 ); d->m_Layout->addWidget( d->m_Content, 2, 0, 1, 1 ); d->m_Layout->setContentsMargins(2,2,2,2); this->setLayout(d->m_Layout); QMetaObject::connectSlotsByName(this); this->Update(); } QmitkPythonSnippets::~QmitkPythonSnippets() { delete d; } void QmitkPythonSnippets::on_PasteSnippet_triggered( bool ) { emit PasteCommandRequested( d->m_Content->toPlainText() ); } void QmitkPythonSnippets::on_RenameSnippet_triggered(bool) { QString oldname = d->m_Name->currentText(); QString name = oldname; bool ok = false; while( true ) { name = QInputDialog::getText(this, tr("Add new snippet"), tr("Name of snippet:"), QLineEdit::Normal, name, &ok); if (ok) { if ( d->m_Snippets.contains(name) ) { QMessageBox::warning(this, tr("Duplicate name."), tr("The entered name already exists. Enter another one or cancel the operation."), QMessageBox::Ok, QMessageBox::Ok ); } else { QString tmpSnippet = d->m_Snippets[oldname]; d->m_Snippets.remove(oldname); d->m_Snippets[name] = tmpSnippet; this->Update(name); this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); break; } } else { break; } } } void QmitkPythonSnippets::on_AddSnippet_triggered(bool) { bool ok; QString name = QInputDialog::getText(this, tr("Add new snippet"), tr("Name of snippet:"), QLineEdit::Normal, "newSnippet", &ok); if (ok && !name.isEmpty()) { MITK_DEBUG("QmitkPythonSnippets") << "creating unique name for " << name.toStdString(); name = this->CreateUniqueName(name); MITK_DEBUG("QmitkPythonSnippets") << "creating snippet " << name.toStdString(); d->m_Snippets[name] = ""; this->Update(name); this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); } } QString QmitkPythonSnippets::CreateUniqueName( const QString& name ) const { QString newName = name; size_t i = 2; while( d->m_Snippets.contains(name) ) { newName = name + QString("_%1").arg(i); ++i; } return newName; } void QmitkPythonSnippets::on_RemoveSnippet_triggered(bool) { QString name = d->m_Name->currentText(); QString question = QString("Really remove Snippet %1?").arg(name); int remove = QMessageBox::question( this, QString("Confirm removal"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if( remove == QMessageBox::Yes || remove == QMessageBox::Ok ) { d->m_Snippets.remove(name); this->Update(); this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); } } void QmitkPythonSnippets::on_RestoreDefaultSnippets_triggered(bool) { QString question = QString("Really restore default Snippets?"); int remove = QMessageBox::question( this, QString("Confirm restoring"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if( remove == QMessageBox::Yes || remove == QMessageBox::Ok ) { - this->LoadStringMap( DEFAULT_SNIPPET_FILE, d->m_Snippets ); + QmitkPythonSnippets::LoadStringMap( DEFAULT_SNIPPET_FILE, d->m_Snippets ); this->Update(); this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); } } void QmitkPythonSnippets::on_Name_currentIndexChanged(int i) { bool validSelection = i >= 0 ; d->m_PasteSnippet->setEnabled(validSelection); d->m_RemoveSnippet->setEnabled(validSelection); d->m_RenameSnippet->setEnabled(validSelection); d->m_Content->setEnabled(validSelection); d->m_SaveSnippets->setEnabled(validSelection); if( validSelection ) { QString name = d->m_Name->currentText(); MITK_DEBUG("QmitkPythonSnippets") << "selected snippet " << name.toStdString(); d->m_Content->setText( d->m_Snippets[name] ); MITK_DEBUG("QmitkPythonSnippets") << "selected snippet content " << d->m_Snippets[name].toStdString(); } } void QmitkPythonSnippets::SaveStringMap(const QString &filename, const QmitkPythonSnippets::QStringMap &map) const { MITK_DEBUG("QmitkPythonSnippets") << "saving to xml file " << filename.toStdString(); if( filename.isEmpty() ) { MITK_WARN("QmitkPythonSnippets") << "empty auto save file path given. quit."; return; } QFile file(filename); file.open(QIODevice::WriteOnly); if( !file.isOpen() ) { MITK_WARN("QmitkPythonSnippets") << "could not open file " << filename.toStdString() << " for writing"; return; } QXmlStreamWriter xmlWriter(&file); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument(); xmlWriter.writeStartElement(SNIPPETS_ROOT_XML_ELEMENT_NAME); QStringMap::const_iterator it = d->m_Snippets.begin(); while( it != d->m_Snippets.end() ) { { MITK_DEBUG("QmitkPythonSnippets") << "SNIPPETS_XML_ELEMENT_NAME " << SNIPPETS_XML_ELEMENT_NAME.toStdString(); MITK_DEBUG("QmitkPythonSnippets") << "writing item " << it.key().toStdString(); } xmlWriter.writeStartElement(SNIPPETS_XML_ELEMENT_NAME); xmlWriter.writeAttribute( "key", it.key() ); xmlWriter.writeAttribute( "value", it.value() ); xmlWriter.writeEndElement(); ++it; } xmlWriter.writeEndDocument(); if( file.isOpen() ) file.close(); { MITK_DEBUG("QmitkPythonSnippets") << "SaveStringMap successful "; } } -bool QmitkPythonSnippets::LoadStringMap( const QString& filename, QmitkPythonSnippets::QStringMap& oldMap ) const +bool QmitkPythonSnippets::LoadStringMap( const QString& filename, QmitkPythonSnippets::QStringMap& oldMap ) { MITK_DEBUG("QmitkPythonSnippets") << "loading from xml file " << filename.toStdString(); QStringMap map; QXmlStreamReader xmlReader; QFile file; QByteArray data; // resource file if( filename.startsWith(":") ) { QResource res( filename ); data = QByteArray( reinterpret_cast< const char* >( res.data() ), res.size() ); xmlReader.addData( data ); } else { file.setFileName( filename ); if (!file.open(QFile::ReadOnly | QFile::Text)) { MITK_ERROR << "Error: Cannot read file " << qPrintable(filename) << ": " << qPrintable(file.errorString()); return false; } xmlReader.setDevice(&file); } xmlReader.readNext(); while(!xmlReader.atEnd()) { xmlReader.readNext(); if(xmlReader.name() == SNIPPETS_XML_ELEMENT_NAME) { QXmlStreamAttributes attributes = xmlReader.attributes(); QString key; QString value; if(attributes.hasAttribute("key")) { key = attributes.value("key").toString(); } if(attributes.hasAttribute("value")) { value = attributes.value("value").toString(); } if( !key.isEmpty() ) { MITK_DEBUG("QmitkPythonSnippets") << "loaded snippet " << key.toStdString(); MITK_DEBUG("QmitkPythonSnippets") << "value " << value.toStdString(); map[key] = value; } } } if (xmlReader.hasError()) { MITK_ERROR << "Error: Failed to parse file " << qPrintable(filename) << ": " << qPrintable(xmlReader.errorString()); return false; } else if (file.error() != QFile::NoError) { MITK_ERROR << "Error: Cannot read file " << qPrintable(filename) << ": " << qPrintable(file.errorString()); return false; } if( file.isOpen() ) file.close(); oldMap = map; return true; } void QmitkPythonSnippets::Update(const QString &name) { d->m_Name->clear(); d->m_Content->clear(); MITK_DEBUG("QmitkPythonSnippets") << "size of snippets " << d->m_Snippets.size(); QStringMap::const_iterator it = d->m_Snippets.begin(); while( it != d->m_Snippets.end() ) { MITK_DEBUG("QmitkPythonSnippets") << "adding item " << it.key().toStdString(); d->m_Name->addItem( it.key() ); ++it; } int index = d->m_Name->findText( name ); if( index >= 0 ) { MITK_DEBUG("QmitkPythonSnippets") << "selecting index " << index; d->m_Name->setCurrentIndex(index); } } void QmitkPythonSnippets::on_Content_textChanged() { if( d->m_Content->isEnabled() ) { QString name = d->m_Name->currentText(); QString snippet = d->m_Content->toPlainText(); d->m_Snippets[name] = snippet; this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); MITK_DEBUG("QmitkPythonSnippets") << "SaveStringMap successful"; } } void QmitkPythonSnippets::on_SaveSnippets_triggered(bool) { QString fileName = QFileDialog::getSaveFileName(this, "Save snippets", d->m_SaveFileName, "XML files (*.xml)"); if( !fileName.isEmpty() ) { d->m_SaveFileName = fileName; this->SaveStringMap( d->m_SaveFileName, d->m_Snippets ); } } void QmitkPythonSnippets::on_LoadSnippets_triggered(bool) { QString fileName = QFileDialog::getOpenFileName(this, "Load snippets", d->m_SaveFileName, "XML files (*.xml)"); if( !fileName.isEmpty() ) { d->m_SaveFileName = fileName; QString question = QString("Your current snippets will be overwritten. Proceed?"); int overwrite = QMessageBox::warning(this, QString("Confirm overwrite"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if( overwrite == QMessageBox::Yes ) { this->LoadStringMap( d->m_SaveFileName, d->m_Snippets ); this->Update( d->m_Name->currentText() ); this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets ); } } } diff --git a/Modules/Python/QmitkPythonSnippets.h b/Modules/Python/QmitkPythonSnippets.h index 39c808a419..998e0a635b 100644 --- a/Modules/Python/QmitkPythonSnippets.h +++ b/Modules/Python/QmitkPythonSnippets.h @@ -1,104 +1,105 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _QmitkPythonSnippets_H #define _QmitkPythonSnippets_H #include #include #include struct QmitkPythonSnippetsData; /// /// a widget that holds snippets and serializes the snippets to a certain places class MITK_PYTHON_EXPORT QmitkPythonSnippets: public QWidget { Q_OBJECT public: static const QString DEFAULT_SNIPPET_FILE; static const QString SNIPPETS_ROOT_XML_ELEMENT_NAME; static const QString SNIPPETS_XML_ELEMENT_NAME; /// /// typedef for string map typedef QMap QStringMap; /// /// build ui here /// the snippets will be loaded from _AutoSaveFileName if not empty and readable /// otherwise the default snippets will be loaded QmitkPythonSnippets( const QString& _AutoSaveFileName="", QWidget* parent=0 ); /// /// delete d pointer virtual ~QmitkPythonSnippets(); + /// + /// read string map from xml file + static bool LoadStringMap( const QString& filename, QStringMap& oldMap ); + signals: /// /// this class whishes to paste sth command void PasteCommandRequested(const QString& command); protected slots: /// /// emits PasteCommandRequested signal void on_PasteSnippet_triggered( bool checked = false ); /// /// ask for name as long as it exists, call update() void on_RenameSnippet_triggered( bool checked = false ); /// /// ask for name, create snippet, call update() void on_AddSnippet_triggered( bool checked = false ); /// /// remove the current snippet, call update() void on_RemoveSnippet_triggered( bool checked = false ); /// /// call LoadStringMap with d->m_DefaultSnippetsAutoSaveFileName void on_RestoreDefaultSnippets_triggered( bool checked = false ); /// /// update action state (enable/disable), update text box void on_Name_currentIndexChanged( int i ); /// /// save changed snippet void on_Content_textChanged(); /// /// ask for file, save snippets void on_SaveSnippets_triggered( bool checked = false ); /// /// ask for file, load snippets (do not replace) void on_LoadSnippets_triggered( bool checked = false ); protected: /// /// write string map to xml file void SaveStringMap( const QString& filename, const QStringMap& map ) const; /// - /// read string map from xml file - bool LoadStringMap( const QString& filename, QStringMap& oldMap ) const; - /// /// creates a name which does not exist in the list QString CreateUniqueName(const QString &name) const; /// /// update combo box /// if name is passed, the according element will be selected void Update(const QString &name = ""); private: /// /// d pointer declaration (holds members) QmitkPythonSnippetsData* d; }; #endif // _QmitkPythonSnippets_H_INCLUDED diff --git a/Modules/Python/Testing/CMakeLists.txt b/Modules/Python/Testing/CMakeLists.txt index c1470c885b..153cd81e2e 100644 --- a/Modules/Python/Testing/CMakeLists.txt +++ b/Modules/Python/Testing/CMakeLists.txt @@ -1,4 +1 @@ MITK_CREATE_MODULE_TESTS() - -mitkAddCustomModuleTest(mitkCopyToPythonAsItkImage_CopyPic3DToPythonAndBackToMitk_ImageIsEqual mitkCopyToPythonAsItkImageTest ${MITK_DATA_DIR}/Pic3D.nrrd) - diff --git a/Modules/Python/Testing/files.cmake b/Modules/Python/Testing/files.cmake index c8e9fe00e2..70ed5dac94 100644 --- a/Modules/Python/Testing/files.cmake +++ b/Modules/Python/Testing/files.cmake @@ -1,7 +1,4 @@ set(MODULE_TESTS mitkPythonTest.cpp -) - -set(MODULE_CUSTOM_TESTS - mitkCopyToPythonAsItkImageTest.cpp + mitkPythonWrappingTest.cpp ) diff --git a/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp b/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp deleted file mode 100644 index 7927ebcb8b..0000000000 --- a/Modules/Python/Testing/mitkCopyToPythonAsItkImageTest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -int mitkCopyToPythonAsItkImageTest(int /*argc*/, char* argv[]) -{ - MITK_TEST_BEGIN("mitkCopyToPythonAsItkImageTest") - - //get the context of the python module - us::Module* module = us::ModuleRegistry::GetModule("MitkPython"); - us::ModuleContext* context = module->GetModuleContext(); - //get the service which is generated in the PythonModuleActivator - us::ServiceReference serviceRef = context->GetServiceReference(); - mitk::PythonService* pythonService = dynamic_cast( context->GetService(serviceRef) ); - MITK_TEST_CONDITION(pythonService->IsSimpleItkPythonWrappingAvailable() == true, "Is Python available?"); - - mitk::Image::Pointer testImage = mitk::IOUtil::LoadImage(std::string(argv[1])); - - //give it a name in python - std::string nameOfImageInPython("mitkImage"); - - MITK_TEST_CONDITION( pythonService->CopyToPythonAsSimpleItkImage( testImage, nameOfImageInPython) == true, "Valid image copied to python import should return true."); - mitk::Image::Pointer pythonImage = pythonService->CopySimpleItkImageFromPython(nameOfImageInPython); - - - mitk::Index3D index; - index[0] = 128; - index[1] = 128; - index[2] = 24; - - try{ - // pic3D of type char - mitk::ImagePixelReadAccessor pythonImageAccesor(pythonImage); - - //TODO Use the assert comparison methods once we have them implemented and remove GetPixelValueByIndex - MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(0) == 256, "Is the 1st dimension of Pic3D still 256?"); - MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(1) == 256, "Is the 2nd dimension of Pic3D still 256?"); - MITK_TEST_CONDITION( pythonImageAccesor.GetDimension(2) == 49, "Is the 3rd dimension of Pic3D still 49?"); - - MITK_TEST_CONDITION( pythonImageAccesor.GetPixelByIndex(index) == 96, "Is the value of Pic3D at (128,128,24) still 96?"); - }catch(...) - { - MITK_TEST_CONDITION( false, "Image is not readable! "); - } - - MITK_TEST_END() -} diff --git a/Modules/Python/Testing/mitkPythonWrappingTest.cpp b/Modules/Python/Testing/mitkPythonWrappingTest.cpp new file mode 100644 index 0000000000..5ca5df6192 --- /dev/null +++ b/Modules/Python/Testing/mitkPythonWrappingTest.cpp @@ -0,0 +1,253 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +// vtk cone sample snippet +#include +// vtk decimate pro snippet +#include + + +namespace sitk = itk::simple; + +namespace mitk { + static bool Equal ( mitk::Image* img1, mitk::Image* img2 ); + static bool Equal ( mitk::Surface* s1, Surface *s2 ); +} + +bool mitk::Equal ( mitk::Image* img1, mitk::Image* img2 ) +{ + mitk::ImageReadAccessor ra1(img1); + mitk::ImageReadAccessor ra2(img2); + const unsigned int* img1Dims = img1->GetDimensions(); + + if ( img1->GetDimension() != img2->GetDimension() ) + return false; + + if( img1Dims[0] != img2->GetDimensions()[0] || + img1Dims[1] != img2->GetDimensions()[1] || + img1Dims[2] != img2->GetDimensions()[2] ) + return false; + + if ( img1->GetPixelType().GetPixelType() != img2->GetPixelType().GetPixelType()) + return false; + + if ( img1->GetGeometry()->GetSpacing()[0] != img2->GetGeometry()->GetSpacing()[0] || + img1->GetGeometry()->GetSpacing()[1] != img2->GetGeometry()->GetSpacing()[1] || + img1->GetGeometry()->GetSpacing()[2] != img2->GetGeometry()->GetSpacing()[2] ) + return false; + + if ( img1->GetGeometry()->GetOrigin()[0] != img2->GetGeometry()->GetOrigin()[0] || + img1->GetGeometry()->GetOrigin()[1] != img2->GetGeometry()->GetOrigin()[1] || + img1->GetGeometry()->GetOrigin()[2] != img2->GetGeometry()->GetOrigin()[2] ) + return false; + + size_t size = img1Dims[0] * img1Dims[1] * img1Dims[2] * img1->GetPixelType().GetSize(); + + // bytewise compare the image + for ( size_t i = 0; i < size; ++i ) + { + if ( ((char*)ra1.GetData())[i] != ((char*)ra2.GetData())[i] ) + return false; + } + + return true; +} + +bool mitk::Equal( mitk::Surface* s1, mitk::Surface* s2 ) +{ + vtkPolyData* poly1 = s1->GetVtkPolyData(); + vtkPolyData* poly2 = s2->GetVtkPolyData(); + double p1[3] = { 0.0,0.0,0.0 }; + double p2[3] = { 0.0,0.0,0.0 }; + + if ( poly1->GetNumberOfPoints() != poly2->GetNumberOfPoints() ) { + MITK_WARN << "Size does not match : s1 = " << poly1->GetNumberOfPoints() + << ", s2 = " << poly2->GetNumberOfPoints(); + return false; + } + + for ( vtkIdType i = 0; i < poly1->GetNumberOfPoints(); ++i ) + { + poly1->GetPoint(i,p1); + poly2->GetPoint(i,p2); + + if ( !mitk::Equal(p1[0],p2[0]) || + !mitk::Equal(p1[1],p2[1]) || + !mitk::Equal(p1[2],p2[2]) ) + { + MITK_WARN << "Points do not match: i: "<< i << "p1(" + << p1[0] << "," << p1[1] << "," << p1[2] << "), p2(" + << p2[0] << "," << p2[1] << "," << p2[2] << ")" ; + return false; + } + } + + return true; +} + +class mitkPythonWrappingTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkPythonWrappingTestSuite); + MITK_TEST(testImageTransfer); + MITK_TEST(testSurfaceTransfer); + MITK_TEST(testVtkCreateConePythonSnippet); + MITK_TEST(testVtkDecimateProPythonSnippet); + MITK_TEST(testSimpleITKMedianFilterSnippet); + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::PythonService* m_PythonService; + mitk::Image::Pointer m_Image; + mitk::Surface::Pointer m_Surface; + QMap m_Snippets; + +public: + + void setUp() + { + //get the context of the python module + us::Module* module = us::ModuleRegistry::GetModule("MitkPython"); + us::ModuleContext* context = module->GetModuleContext(); + //get the service which is generated in the PythonModuleActivator + us::ServiceReference serviceRef = context->GetServiceReference(); + m_PythonService = dynamic_cast( context->GetService(serviceRef) ); + + m_Image = mitk::IOUtil::LoadImage(GetTestDataFilePath("Pic3D.nrrd")); + m_Surface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("binary.stl")); + + QmitkPythonSnippets::LoadStringMap(QmitkPythonSnippets::DEFAULT_SNIPPET_FILE,m_Snippets); + } + + void tearDown() + { + m_Image = NULL; + m_Surface = NULL; + } + + void testImageTransfer() + { + std::string varName("mitkImage"); + CPPUNIT_ASSERT_MESSAGE ( "Is SimpleITK Python Wrapping available?", + m_PythonService->IsSimpleItkPythonWrappingAvailable() == true ); + + CPPUNIT_ASSERT_MESSAGE( "Valid image copied to python import should return true.", + m_PythonService->CopyToPythonAsSimpleItkImage( m_Image, varName) == true ); + + mitk::Image::Pointer pythonImage = m_PythonService->CopySimpleItkImageFromPython(varName); + + CPPUNIT_ASSERT_MESSAGE( "Compare if images are equal after transfer.", + mitk::Equal(pythonImage,m_Image) ); + } + + void testSurfaceTransfer() + { + std::string varName("mitkSurface"); + CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); + + CPPUNIT_ASSERT_MESSAGE( "Valid surface copied to python import should return true.", + m_PythonService->CopyToPythonAsVtkPolyData( m_Surface, varName) == true ); + + mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython(varName); + + CPPUNIT_ASSERT_MESSAGE( "Compare if surfaces are equal after transfer.", mitk::Equal(pythonSurface,m_Surface) ); + } + + void testVtkCreateConePythonSnippet() + { + // cone in cpp + mitk::Surface::Pointer mitkSurface = mitk::Surface::New(); + vtkSmartPointer coneSrc = vtkSmartPointer::New(); + coneSrc->SetResolution(60); + coneSrc->SetCenter(-2,0,0); + coneSrc->Update(); + mitkSurface->SetVtkPolyData(coneSrc->GetOutput()); + + // run python code + CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); + + m_PythonService->Execute( m_Snippets["vtk: create cone"].toStdString(), mitk::IPythonService::MULTI_LINE_COMMAND ); + + mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython("cone"); + + CPPUNIT_ASSERT_MESSAGE( "Compare if cones are equal.", mitk::Equal(pythonSurface, mitkSurface) ); + } + + void testVtkDecimateProPythonSnippet() + { + // decimate pro in cpp + mitk::Surface::Pointer mitkSurface = mitk::Surface::New(); + vtkSmartPointer deci = vtkSmartPointer::New(); + deci->SetInputData(m_Surface->GetVtkPolyData()); + deci->SetTargetReduction(0.9); + deci->PreserveTopologyOn(); + deci->Update(); + mitkSurface->SetVtkPolyData(deci->GetOutput()); + + // decimate pro in python + CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); + + CPPUNIT_ASSERT_MESSAGE( "Valid surface copied to python import should return true.", m_PythonService->CopyToPythonAsVtkPolyData( m_Surface, "mitkSurface") == true ); + + m_PythonService->Execute( m_Snippets["vtk.vtkDecimatePro"].toStdString(), mitk::IPythonService::MULTI_LINE_COMMAND ); + + mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython("mitkSurface_new"); + + CPPUNIT_ASSERT_MESSAGE( "Compare if surfaces are equal.", mitk::Equal(pythonSurface, mitkSurface) ); + } + + void testSimpleITKMedianFilterSnippet() + { + // simple itk median filter in cpp + sitk::MedianImageFilter medianFilter; + medianFilter.SetRadius(1); + sitk::Image sitkImage = medianFilter.Execute(mitk::SimpleItkImageImport::MitkToSimpleItkImage(m_Image)); + mitk::Image::Pointer mitkImage = mitk::SimpleItkImageImport::SimpleItkToMitkImage(sitkImage); + + // simple itk median filter in python + CPPUNIT_ASSERT_MESSAGE ( "Is SimpleItk Python Wrapping available?", m_PythonService->IsSimpleItkPythonWrappingAvailable() == true ); + + CPPUNIT_ASSERT_MESSAGE( "Valid image copied to python import should return true.", m_PythonService->CopyToPythonAsSimpleItkImage(m_Image, "mitkImage") == true ); + + m_PythonService->Execute( m_Snippets["medianfilter"].toStdString(), mitk::IPythonService::MULTI_LINE_COMMAND ); + + mitk::Image::Pointer pythonImage = m_PythonService->CopySimpleItkImageFromPython("mitkImage_new"); + + CPPUNIT_ASSERT_MESSAGE( "Compare if images are equal.", mitk::Equal(pythonImage, mitkImage) ); + } + //TODO opencv median filter +}; + +MITK_TEST_SUITE_REGISTRATION(mitkPythonWrapping) diff --git a/Modules/Python/files.cmake b/Modules/Python/files.cmake index 66fb3a3ce7..2e49e2d5b5 100644 --- a/Modules/Python/files.cmake +++ b/Modules/Python/files.cmake @@ -1,28 +1,29 @@ SET(CPP_FILES mitkIPythonService.cpp mitkPythonActivator.cpp mitkPythonService.cpp + mitkSimpleItkImageImport.cpp QmitkCtkPythonShell.cpp QmitkPythonVariableStackTableModel.cpp QmitkPythonVariableStackTableView.cpp QmitkPythonScriptEditorHighlighter.cpp QmitkPythonTextEditor.cpp QmitkPythonSnippets.cpp ) #SET(UI_FILES # QmitkPythonSnippets.ui #) SET(MOC_H_FILES QmitkCtkPythonShell.h QmitkPythonVariableStackTableModel.h QmitkPythonVariableStackTableView.h QmitkPythonScriptEditorHighlighter.h QmitkPythonTextEditor.h QmitkPythonSnippets.h ) set(QRC_FILES resources/mitkPython.qrc ) diff --git a/Modules/Python/mitkPythonService.cpp b/Modules/Python/mitkPythonService.cpp index 19f87a0b03..c30afb389f 100644 --- a/Modules/Python/mitkPythonService.cpp +++ b/Modules/Python/mitkPythonService.cpp @@ -1,620 +1,639 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPythonService.h" #include #include #include #include #include #include "PythonPath.h" #include #include #include #include #include #ifndef WIN32 #include #endif const QString mitk::PythonService::m_TmpDataFileName("temp_mitk_data_file"); #ifdef USE_MITK_BUILTIN_PYTHON static char* pHome = NULL; #endif mitk::PythonService::PythonService() : m_ItkWrappingAvailable( true ), m_OpenCVWrappingAvailable( true ), m_VtkWrappingAvailable( true ), m_ErrorOccured( false ) { { MITK_DEBUG << "will init python if necessary"; } bool pythonInitialized = static_cast( Py_IsInitialized() ); //m_PythonManager.isPythonInitialized() ); { MITK_DEBUG << "pythonInitialized " << pythonInitialized; MITK_DEBUG << "m_PythonManager.isPythonInitialized() " << m_PythonManager.isPythonInitialized(); } // due to strange static var behaviour on windows Py_IsInitialized() returns correct value while // m_PythonManager.isPythonInitialized() does not because it has been constructed and destructed again if( !m_PythonManager.isPythonInitialized() ) { try { #ifndef WIN32 dlopen(PYTHON_LIBRARY_NAME, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL); #endif std::string programPath = mitk::IOUtil::GetProgramPath(); QDir programmDir( QString( programPath.c_str() ).append("/Python") ); QString pythonCommand; // Set the pythonpath variable depending if // we have an installer or development environment if ( programmDir.exists() ) { // runtime directory used in installers pythonCommand.append( QString("import sys\n") ); pythonCommand.append( QString("sys.path.append('')\n") ); pythonCommand.append( QString("sys.path.append('%1')\n").arg(programPath.c_str()) ); pythonCommand.append( QString("sys.path.append('%1/Python')\n").arg(programPath.c_str()) ); pythonCommand.append( QString("sys.path.append('%1/Python/SimpleITK')").arg(programPath.c_str()) ); // set python home if own runtime is deployed } else { pythonCommand.append(PYTHONPATH_COMMAND); } if( pythonInitialized ) m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut|PythonQt::PythonAlreadyInitialized); else m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut); // set python home if own runtime is used #ifdef USE_MITK_BUILTIN_PYTHON QString pythonHome; if ( programmDir.exists() ) pythonHome.append(QString("%1/Python").arg(programPath.c_str())); else pythonHome.append(PYTHONHOME); if(pHome) delete[] pHome; pHome = new char[pythonHome.toStdString().length() + 1]; strcpy(pHome,pythonHome.toStdString().c_str()); Py_SetPythonHome(pHome); MITK_DEBUG("PythonService") << "PythonHome: " << pHome; #endif MITK_DEBUG("PythonService") << "initalizing python"; m_PythonManager.initialize(); #ifdef USE_MITK_BUILTIN_PYTHON PyObject* dict = PyDict_New(); // Import builtin modules if (PyDict_GetItemString(dict, "__builtins__") == NULL) { PyObject* builtinMod = PyImport_ImportModule("__builtin__"); if (builtinMod == NULL || PyDict_SetItemString(dict, "__builtins__", builtinMod) != 0) { Py_DECREF(dict); Py_XDECREF(dict); return; } Py_DECREF(builtinMod); } #endif MITK_DEBUG("PythonService")<< "Python Search paths: " << Py_GetPath(); MITK_DEBUG("PythonService") << "python initalized"; MITK_DEBUG("PythonService") << "registering python paths" << PYTHONPATH_COMMAND; m_PythonManager.executeString( pythonCommand, ctkAbstractPythonManager::FileInput ); } catch (...) { MITK_DEBUG("PythonService") << "exception initalizing python"; } } } mitk::PythonService::~PythonService() { MITK_DEBUG("mitk::PythonService") << "destructing PythonService"; #ifdef USE_MITK_BUILTIN_PYTHON if(pHome) delete[] pHome; #endif } std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType) { QString pythonCommand = QString::fromStdString(stdpythonCommand); { MITK_DEBUG("mitk::PythonService") << "pythonCommand = " << pythonCommand.toStdString(); MITK_DEBUG("mitk::PythonService") << "commandType = " << commandType; } QVariant result; bool commandIssued = true; if(commandType == IPythonService::SINGLE_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput ); else if(commandType == IPythonService::MULTI_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput ); else if(commandType == IPythonService::EVAL_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput ); else commandIssued = false; if(commandIssued) { this->NotifyObserver(pythonCommand.toStdString()); m_ErrorOccured = PythonQt::self()->hadError(); } return result.toString().toStdString(); } void mitk::PythonService::ExecuteScript( const std::string& pythonScript ) { m_PythonManager.executeFile(QString::fromStdString(pythonScript)); } std::vector mitk::PythonService::GetVariableStack() const { std::vector list; PyObject* dict = PyImport_GetModuleDict(); PyObject* object = PyDict_GetItemString(dict, "__main__"); PyObject* dirMain = PyObject_Dir(object); PyObject* tempObject = 0; PyObject* strTempObject = 0; if(dirMain) { std::string name, attrValue, attrType; for(int i = 0; iob_type->tp_name; strTempObject = PyObject_Repr(tempObject); if(strTempObject && ( PyUnicode_Check(strTempObject) || PyString_Check(strTempObject) ) ) attrValue = PyString_AsString(strTempObject); else attrValue = ""; mitk::PythonVariable var; var.m_Name = name; var.m_Value = attrValue; var.m_Type = attrType; list.push_back(var); } } return list; } bool mitk::PythonService::DoesVariableExist(const std::string& name) const { bool varExists = false; std::vector allVars = this->GetVariableStack(); for(unsigned int i = 0; i< allVars.size(); i++) { if( allVars.at(i).m_Name == name ) { varExists = true; break; } } return varExists; } void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer) { if(!m_Observer.contains(observer)) m_Observer.append(observer); } void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer) { m_Observer.removeOne(observer); } void mitk::PythonService::NotifyObserver(const std::string &command) { MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size(); for( int i=0; i< m_Observer.size(); ++i ) { m_Observer.at(i)->CommandExecuted(command); } } QString mitk::PythonService::GetTempDataFileName(const std::string& ext) const { QString tmpFolder = QDir::tempPath(); QString fileName = tmpFolder + QDir::separator() + m_TmpDataFileName + QString::fromStdString(ext); return fileName; } bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName) { QString varName = QString::fromStdString( stdvarName ); QString command; unsigned int* imgDim = image->GetDimensions(); int npy_nd = 1; npy_intp* npy_dims = new npy_intp[1]; npy_dims[0] = imgDim[0] * imgDim[1] * imgDim[2]; // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); + const mitk::Point3D origin = image->GetGeometry()->GetOrigin(); mitk::PixelType pixelType = image->GetPixelType(); itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelType(); PyObject* npyArray = NULL; mitk::ImageReadAccessor racc(image); void* array = (void*) racc.GetData(); // default pixeltype: unsigned short NPY_TYPES npy_type = NPY_USHORT; std::string sitk_type = "sitkUInt8"; if( ioPixelType == itk::ImageIOBase::SCALAR ) { if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { npy_type = NPY_DOUBLE; sitk_type = "sitkFloat64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { npy_type = NPY_FLOAT; sitk_type = "sitkFloat32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { npy_type = NPY_SHORT; sitk_type = "sitkInt16"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { npy_type = NPY_BYTE; sitk_type = "sitkInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { npy_type = NPY_INT; sitk_type = "sitkInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { npy_type = NPY_LONG; sitk_type = "sitkInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { npy_type = NPY_UBYTE; sitk_type = "sitkUInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { npy_type = NPY_UINT; sitk_type = "sitkUInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { npy_type = NPY_LONG; sitk_type = "sitkUInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { npy_type = NPY_USHORT; sitk_type = "sitkUInt16"; } + } else { + MITK_WARN << "not a scalar pixeltype"; + return false; } // creating numpy array import_array1 (true); npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array); // add temp array it to the python dictionary to access it in python code const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array") .arg(varName).toStdString().c_str(), npyArray ); // sanity check if ( status != 0 ) return false; command.append( QString("%1 = sitk.Image(%2,%3,%4,sitk.%5)\n").arg(varName) .arg(QString::number(imgDim[0])) .arg(QString::number(imgDim[1])) .arg(QString::number(imgDim[2])) .arg(QString(sitk_type.c_str())) ); command.append( QString("%1.SetSpacing([%2,%3,%4])\n").arg(varName) .arg(QString::number(spacing[0])) .arg(QString::number(spacing[1])) .arg(QString::number(spacing[2])) ); + command.append( QString("%1.SetOrigin([%2,%3,%4])\n").arg(varName) + .arg(QString::number(origin[0])) + .arg(QString::number(origin[1])) + .arg(QString::number(origin[2])) ); command.append( QString("sitk._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName) ); command.append( QString("del %1_numpy_array").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName) { + double*ds = NULL; // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitk::Vector3D spacing; + mitk::Point3D origin; QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName) ); command.append( QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName) ); + command.append( QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName) ); command.append( QString("%1_dtype = %1_numpy_array.dtype.name").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() ); std::string dtype = PyString_AsString(py_dtype); PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_numpy_array").arg(varName).toStdString().c_str() ); PyArrayObject* py_spacing = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_spacing").arg(varName).toStdString().c_str() ); + PyArrayObject* py_origin = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_origin").arg(varName).toStdString().c_str() ); size_t sz = sizeof(short); mitk::PixelType pixelType = MakeScalarPixelType(); if( dtype.compare("float64") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(double); } else if( dtype.compare("float32") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(float); } else if( dtype.compare("int16") == 0) { pixelType = MakeScalarPixelType(); sz = sizeof(short); } else if( dtype.compare("int8") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(char); } else if( dtype.compare("int32") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(int); } else if( dtype.compare("int64") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(long); } else if( dtype.compare("uint8") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(unsigned char); } else if( dtype.compare("uint32") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(unsigned int); } else if( dtype.compare("uint64") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(unsigned long); } else if( dtype.compare("uint16") == 0 ) { pixelType = MakeScalarPixelType(); sz = sizeof(unsigned short); } unsigned int* dimensions = new unsigned int[py_data->nd]; // fill backwards , nd data saves dimensions in opposite direction for( int i = 0; i < py_data->nd; ++i ) { dimensions[i] = py_data->dimensions[py_data->nd - 1 - i]; sz *= dimensions[i]; } mitkImage->Initialize(pixelType, py_data->nd, dimensions); // copy data mitk::ImageWriteAccessor iwa(mitkImage); memcpy( iwa.GetData(), py_data->data, sz); - double* ds = (double*)py_spacing->data; + ds = (double*)py_spacing->data; spacing[0] = ds[0]; spacing[1] = ds[1]; spacing[2] = ds[2]; mitkImage->GetGeometry()->SetSpacing(spacing); + ds = (double*)py_origin->data; + origin[0] = ds[0]; + origin[1] = ds[1]; + origin[2] = ds[2]; + mitkImage->GetGeometry()->SetOrigin(origin); + // cleanup command.clear(); command.append( QString("del %1_numpy_array\n").arg(varName) ); command.append( QString("del %1_dtype\n").arg(varName) ); - command.append( QString("del %1_spacing").arg(varName) ); + command.append( QString("del %1_spacing\n").arg(varName) ); + command.append( QString("del %1_origin").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); delete[] dimensions; return mitkImage; } bool mitk::PythonService::CopyToPythonAsCvImage( mitk::Image* image, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); bool convert = false; if(image->GetDimension() != 2) { MITK_ERROR << "Only 2D images allowed for OpenCV images"; return convert; } // try to save mitk image QString fileName = this->GetTempDataFileName( ".bmp" ); fileName = QDir::fromNativeSeparators( fileName ); MITK_DEBUG("PythonService") << "Saving temporary file " << fileName.toStdString(); if( !mitk::IOUtil::SaveImage(image, fileName.toStdString()) ) { MITK_ERROR << "Temporary file " << fileName.toStdString() << " could not be created."; return convert; } QString command; command.append( QString("%1 = cv2.imread(\"%2\")\n") .arg( varName ).arg( fileName ) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); MITK_DEBUG("PythonService") << "Removing file " << fileName.toStdString(); QFile file(fileName); file.remove(); convert = true; return convert; } mitk::Image::Pointer mitk::PythonService::CopyCvImageFromPython( const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); mitk::Image::Pointer mitkImage; QString command; QString fileName = GetTempDataFileName( ".bmp" ); fileName = QDir::fromNativeSeparators( fileName ); MITK_DEBUG("PythonService") << "run python command to save image with opencv to " << fileName.toStdString(); command.append( QString( "cv2.imwrite(\"%1\", %2)\n").arg( fileName ).arg( varName ) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); try { MITK_DEBUG("PythonService") << "Loading temporary file " << fileName.toStdString() << " as MITK image"; mitkImage = mitk::IOUtil::LoadImage( fileName.toStdString() ); } catch(std::exception& e) { MITK_ERROR << e.what(); } QFile file(fileName); if( file.exists() ) { MITK_DEBUG("PythonService") << "Removing temporary file " << fileName.toStdString(); file.remove(); } return mitkImage; } ctkAbstractPythonManager *mitk::PythonService::GetPythonManager() { return &m_PythonManager; } mitk::Surface::Pointer mitk::PythonService::CopyVtkPolyDataFromPython( const std::string& stdvarName ) { // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); // python memory address PyObject *pyAddr = NULL; // cpp address size_t addr = 0; mitk::Surface::Pointer surface = mitk::Surface::New(); QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_addr_str = %1.GetAddressAsString(\"vtkPolyData\")\n").arg(varName) ); // remove 0x from the address command.append( QString("%1_addr = int(%1_addr_str[5:],16)").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); // get address of the object pyAddr = PyDict_GetItemString(pyDict,QString("%1_addr").arg(varName).toStdString().c_str()); // convert to long addr = PyInt_AsLong(pyAddr); MITK_DEBUG << "Python object address: " << addr; // get the object vtkPolyData* poly = (vtkPolyData*)((void*)addr); surface->SetVtkPolyData(poly); // delete helper variables from python stack command = ""; command.append( QString("del %1_addr_str\n").arg(varName) ); command.append( QString("del %1_addr").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return surface; } bool mitk::PythonService::CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); std::ostringstream oss; std::string addr = ""; QString command; QString address; oss << (void*) ( surface->GetVtkPolyData() ); // get the address addr = oss.str(); // remove "0x" address = QString::fromStdString(addr.substr(2)); command.append( QString("%1 = vtk.vtkPolyData(\"%2\")\n").arg(varName).arg(address) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable() { this->Execute( "import SimpleITK as sitk\n", IPythonService::SINGLE_LINE_COMMAND ); //this->Execute( "import itk\n", IPythonService::SINGLE_LINE_COMMAND ); //this->Execute( "print \"Using ITK version \" + itk.Version.GetITKVersion()\n", IPythonService::SINGLE_LINE_COMMAND ); m_ItkWrappingAvailable = !this->PythonErrorOccured(); // check for numpy this->Execute( "import numpy\n", IPythonService::SINGLE_LINE_COMMAND ); if ( this->PythonErrorOccured() ) MITK_ERROR << "Numpy not found."; m_ItkWrappingAvailable = !this->PythonErrorOccured(); return m_ItkWrappingAvailable; } bool mitk::PythonService::IsOpenCvPythonWrappingAvailable() { this->Execute( "import cv2\n", IPythonService::SINGLE_LINE_COMMAND ); m_OpenCVWrappingAvailable = !this->PythonErrorOccured(); return m_OpenCVWrappingAvailable; } bool mitk::PythonService::IsVtkPythonWrappingAvailable() { this->Execute( "import vtk", IPythonService::SINGLE_LINE_COMMAND ); //this->Execute( "print \"Using VTK version \" + vtk.vtkVersion.GetVTKVersion()\n", IPythonService::SINGLE_LINE_COMMAND ); m_VtkWrappingAvailable = !this->PythonErrorOccured(); return m_VtkWrappingAvailable; } bool mitk::PythonService::PythonErrorOccured() const { return m_ErrorOccured; } diff --git a/Modules/Python/mitkSimpleItkImageImport.cpp b/Modules/Python/mitkSimpleItkImageImport.cpp new file mode 100644 index 0000000000..52e25fd20d --- /dev/null +++ b/Modules/Python/mitkSimpleItkImageImport.cpp @@ -0,0 +1,147 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include "mitkSimpleItkImageImport.h" +#include +#include +#include + +namespace sitk = itk::simple; + +sitk::Image mitk::SimpleItkImageImport::MitkToSimpleItkImage( mitk::Image* image ) +{ + const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); + mitk::Point3D origin = image->GetGeometry()->GetOrigin(); + mitk::PixelType pixelType = image->GetPixelType(); + mitk::ImageReadAccessor ra(image); + void* buffer = (void*) ra.GetData(); + sitk::ImportImageFilter importer; + + std::vector sitkSpacing; + sitkSpacing.push_back(spacing[0]); + sitkSpacing.push_back(spacing[1]); + sitkSpacing.push_back(spacing[2]); + std::vector sitkOrigin; + sitkOrigin.push_back(origin[0]); + sitkOrigin.push_back(origin[1]); + sitkOrigin.push_back(origin[2]); + std::vector sitkSize; + + for ( unsigned int i = 0; i < image->GetDimension(); ++i ) + sitkSize.push_back(image->GetDimensions()[i]); + + importer.SetSpacing(sitkSpacing); + importer.SetSize(sitkSize); + importer.SetOrigin(sitkOrigin); + + if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { + importer.SetBufferAsDouble((double*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { + importer.SetBufferAsFloat((float*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { + importer.SetBufferAsInt16((int16_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { + importer.SetBufferAsInt8((int8_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { + importer.SetBufferAsInt32((int32_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { + importer.SetBufferAsInt64((int64_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { + importer.SetBufferAsUInt8((uint8_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { + importer.SetBufferAsUInt32((uint32_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { + importer.SetBufferAsUInt64((uint64_t*) buffer); + } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { + importer.SetBufferAsUInt16((uint16_t*) buffer); + } + + return importer.Execute(); +} + +mitk::Image::Pointer mitk::SimpleItkImageImport::SimpleItkToMitkImage( sitk::Image& sitkImage ) +{ + mitk::Image::Pointer image = mitk::Image::New(); + void* buffer = NULL; + mitk::PixelType pixelType = MakeScalarPixelType(); + std::vector sitkSpacing = sitkImage.GetSpacing(); + double spacing[3] = { sitkSpacing[0], sitkSpacing[1], sitkSpacing[2] }; + std::vector sitkOrigin = sitkImage.GetOrigin(); + double origin[3] = { sitkOrigin[0], sitkOrigin[1], sitkOrigin[2] }; + std::vector sitkSize = sitkImage.GetSize(); + unsigned int dimensions[4] = { 1,1,1,1}; + + for ( size_t i = 0; i < sitkSize.size(); ++i ) + dimensions[i] = sitkSize[i]; + + size_t size = 0; + if ( sitkImage.GetPixelIDValue() == sitk::sitkInt8 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsInt8(); + size = sizeof(char); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkInt16 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsInt16(); + size = sizeof(short); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkInt32 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsInt32(); + size = sizeof(int); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkInt64 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsInt64(); + size = sizeof(long); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkUInt8 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsUInt8(); + size = sizeof(unsigned char); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkUInt16 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsUInt16(); + size = sizeof(unsigned short); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkUInt32 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsUInt32(); + size = sizeof(unsigned int); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkUInt64 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsUInt64(); + size = sizeof(unsigned long); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkFloat32 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsFloat(); + size = sizeof(float); + } else if( sitkImage.GetPixelIDValue() == sitk::sitkFloat64 ) { + pixelType = MakeScalarPixelType(); + buffer = (void*) sitkImage.GetBufferAsDouble(); + size = sizeof(double); + } + + image->Initialize(pixelType,sitkImage.GetDimension(),dimensions); + image->SetSpacing(spacing); + image->SetOrigin(origin); + + for(size_t i = 0; i < sitkSize.size(); ++i ) + size *= sitkSize[i]; + + mitk::ImageWriteAccessor wa(image); + + memcpy(wa.GetData(),buffer, size); + + return image; +} diff --git a/Modules/Python/mitkSimpleItkImageImport.h b/Modules/Python/mitkSimpleItkImageImport.h new file mode 100644 index 0000000000..1c01f743f8 --- /dev/null +++ b/Modules/Python/mitkSimpleItkImageImport.h @@ -0,0 +1,56 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef __MITKSIMPLEITKIMAGEIMPORT_H +#define __MITKSIMPLEITKIMAGEIMPORT_H + +#include +#include + +// Export macro +#include + +namespace mitk +{ + +class Image; + +/** @brief Class that provides methods to copy a SimpleITK image to an MITK image + * and vice versa. + */ +class MITK_PYTHON_EXPORT SimpleItkImageImport +{ + +public: + + /** + * Copy the content of a MITK image into an SimpleITK image. + * @param The source MITK image. + * @returns A copy of the MITK image as SimpleITK image. + */ + static itk::simple::Image MitkToSimpleItkImage(mitk::Image* image); + + /** + * This method deep copies an SimpleITK image into a MITK image and return it. + * @param The source image + * @returns A copy as MITK image. + */ + static itk::SmartPointer SimpleItkToMitkImage(itk::simple::Image& sitkImage); +}; + +} + +#endif