diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4063728
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+gdcm-win7
diff --git a/NiDBUploader.pro b/NiDBUploader.pro
new file mode 100644
index 0000000..9cadefc
--- /dev/null
+++ b/NiDBUploader.pro
@@ -0,0 +1,82 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2014-04-16T12:57:27
+#
+#-------------------------------------------------
+
+QT += core gui
+QT += network
+QT += testlib
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = NiDBUploader
+TEMPLATE = app
+
+#CONFIG += static
+#CONFIG += staticlib
+
+SOURCES += main.cpp\
+ mainwindow.cpp \
+ anonymize.cpp
+
+HEADERS += mainwindow.h \
+ anonymize.h
+
+FORMS += mainwindow.ui
+
+#win32 {
+#DEFINES += BUILDTIME=\\\"$$system('echo %time%')\\\"
+#DEFINES += BUILDDATE=\\\"$$system('echo %date%')\\\"
+#} else {
+#DEFINES += BUILDTIME=\\\"$$system(date '+%H:%M.%s')\\\"
+#DEFINES += BUILDDATE=\\\"$$system(date '+%d/%m/%y')\\\"
+#}
+
+INCLUDEPATH += $$PWD/gdcm/Source/Attribute
+INCLUDEPATH += $$PWD/gdcm/Source/Common
+INCLUDEPATH += $$PWD/gdcm/Source/DataDictionary
+INCLUDEPATH += $$PWD/gdcm/Source/DataStructureAndEncodingDefinition
+INCLUDEPATH += $$PWD/gdcm/Source/InformationObjectDefinition
+INCLUDEPATH += $$PWD/gdcm/Source/MediaStorageAndFileFormat
+INCLUDEPATH += $$PWD/gdcm/Source/MessageExchangeDefinition
+INCLUDEPATH += $$PWD/gdcm-win7/Source/Common # for gdcmConfigure.h
+
+#win32-msvc*:contains(QMAKE_TARGET.arch, x86_64):{
+# win32:CONFIG(release, debug|release): LIBS += -L$$PWD/min-gdcm64-win7/bin/Release/
+# else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/min-gdcm64-win7/bin/Debug/
+#}
+#else {
+ win32-msvc2010: {
+ LIBS += -LC:\Qt5\5.3\msvc2010_opengl\plugins\platforms
+
+ LIBS += -L$$PWD/min-gdcm32-win7/bin/Debug/
+ LIBS += -lopengl32 -limm32 -lwinmm -lWs2_32 -lQt5PlatformSupport
+ }
+ else {
+ linux-g++: {
+ LIBS += -L$$PWD/gdcm-centos7/bin
+ }
+ else {
+ win32:CONFIG(release, debug|release): LIBS += -L$$PWD/gdcm-win7/bin/Release/
+ else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/gdcm-win7/bin/Debug/
+ }
+ }
+#}
+
+LIBS += -lgdcmMSFF \
+ -lgdcmCommon \
+ -lgdcmDICT \
+ -lgdcmDSED \
+ -lgdcmIOD \
+ -lgdcmMEXD \
+ -lgdcmcharls \
+ -lgdcmexpat \
+ -lgdcmjpeg12 \
+ -lgdcmjpeg16 \
+ -lgdcmjpeg8 \
+ -lgdcmopenjpeg \
+ -lgdcmzlib \
+# -lgdcmgetopt \
+ -lsocketxx
+
diff --git a/NiDBUploader.pro.user b/NiDBUploader.pro.user
new file mode 100644
index 0000000..e69ce9d
--- /dev/null
+++ b/NiDBUploader.pro.user
@@ -0,0 +1,444 @@
+
+
+
+
+
+ EnvironmentId
+ {deb408f9-cde3-423c-9520-a04988756476}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 1
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop Qt 5.3.0 MSVC2010 OpenGL 32bit
+ Desktop Qt 5.3.0 MSVC2010 OpenGL 32bit
+ qt.53.win32_msvc2010_opengl_kit
+ 1
+ 0
+ 0
+
+ C:/projects/NiDBUploader-build/MSVC2010_32bit-Debug
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+ true
+
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Debug
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ true
+
+
+ C:/projects/NiDBUploader-build/MSVC2010_32bit-Release
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+ true
+
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 2
+
+
+ 0
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy locally
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ NiDBUploader
+
+ Qt4ProjectManager.Qt4RunConfiguration:Q:/dev/NiDBUploader/NiDBUploader.pro
+
+ NiDBUploader.pro
+ false
+ false
+
+ 3768
+ false
+ true
+ false
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.Target.1
+
+ Desktop Qt 5.3.0 MSVC2012 OpenGL 32bit
+ Desktop Qt 5.3.0 MSVC2012 OpenGL 32bit
+ qt.53.win32_msvc2012_opengl_kit
+ 1
+ 0
+ 0
+
+ C:/projects/NiDBUploader-build/MSVC2012_32bit-Debug
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+ true
+
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Debug
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ true
+
+
+ C:/projects/NiDBUploader-build/MSVC2012_32bit-Release
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+ true
+
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 2
+
+
+ 0
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy locally
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ NiDBUploader
+
+ Qt4ProjectManager.Qt4RunConfiguration:Q:/dev/NiDBUploader/NiDBUploader.pro
+
+ NiDBUploader.pro
+ false
+ false
+
+ 3768
+ false
+ true
+ false
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 2
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 18
+
+
+ Version
+ 18
+
+
diff --git a/NiDBUploader.sh b/NiDBUploader.sh
new file mode 100644
index 0000000..a02a70e
--- /dev/null
+++ b/NiDBUploader.sh
@@ -0,0 +1,2 @@
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
+./NiDBUploader
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f3d240
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+nidb-uploader
+=============
+
+NiDBUploader
diff --git a/anonymize.cpp b/anonymize.cpp
new file mode 100644
index 0000000..5787618
--- /dev/null
+++ b/anonymize.cpp
@@ -0,0 +1,11 @@
+#include "anonymize.h"
+#include "gdcmReader.h"
+#include "gdcmImageReader.h"
+#include "gdcmImageWriter.h"
+#include "gdcmCSAHeader.h"
+#include "gdcmAttribute.h"
+#include "gdcmPrivateTag.h"
+
+Anonymize::Anonymize()
+{
+}
diff --git a/anonymize.h b/anonymize.h
new file mode 100644
index 0000000..45b4a8b
--- /dev/null
+++ b/anonymize.h
@@ -0,0 +1,10 @@
+#ifndef ANONYMIZE_H
+#define ANONYMIZE_H
+
+class Anonymize
+{
+public:
+ Anonymize();
+};
+
+#endif // ANONYMIZE_H
diff --git a/gdcm/AUTHORS b/gdcm/AUTHORS
new file mode 100644
index 0000000..ced3a19
--- /dev/null
+++ b/gdcm/AUTHORS
@@ -0,0 +1,52 @@
+GDCM 2.x:
+--------
+
+ Fabrice Bellet : Contributor (RPM)
+ Charl P. Botha : Contributor (Python, VTK)
+ Shane Blackett : Contributor (Win32)
+ Niels Dekker : Contributor (C++ guru)
+ Daniele E. Domenichelli : Contributor (C++)
+ Dean Inglis : Contributor (Win32, VTK)
+ Paulo Henrique Junqueira Amorim : Contributor (Python)
+ Pierre Le Duff : Contributor (C++, VTK)
+ Mathieu Malaterre : Project leader, main developer
+ Lars Matthaus : Contributor (Java)
+ Sean McBride : Contributor (C++)
+ Steve M. Robbins : Contributor (debian)
+ Mark Roden : Developer (C++, C#)
+ Jordi Romera : Developer (C++, RTSTRUCT)
+ Jean-Pierre Roux : Developer, maintain gdcmDataExtra
+ Christina Rossmanith : Contributor (Siemens CSA)
+ Christopher W Treml : Contributor (C#)
+ Joel Spaltenstein : Developer (MacOSX)
+
+
+GDCM 1.x (Developpers and contributors, alphabetical order)
+--------
+ Aris Basic
+ Fabrice Bellet
+ Hugues Benoit-Cattin
+ Francois Bertel
+ Eric Boix
+ Charl P. Botha
+ Peter Cech
+ Eduardo Enrique Davila Serrano
+ Antonin Descampe
+ David Feng
+ Marco Feuerstein
+ Pierre Fillard
+ Leonardo Flores Valencia
+ Dennis Hu
+ Luis Ibanez
+ Michal Kurgan
+ Gianni Lazzarato
+ George Li
+ Sean McInemey
+ Johan Montagnat
+ Emmanuel Olart
+ Luca Picello
+ Parikshit Prasad
+ Benoit Regrain
+ Jean-Michel Rouet
+ Olivier Stern
+ Satomi Takeo
diff --git a/gdcm/Applications/CMakeLists.txt b/gdcm/Applications/CMakeLists.txt
new file mode 100644
index 0000000..ccbaece
--- /dev/null
+++ b/gdcm/Applications/CMakeLists.txt
@@ -0,0 +1 @@
+subdirs(Cxx)
diff --git a/gdcm/Applications/Cxx/CMakeLists.txt b/gdcm/Applications/Cxx/CMakeLists.txt
new file mode 100644
index 0000000..1c83bc3
--- /dev/null
+++ b/gdcm/Applications/Cxx/CMakeLists.txt
@@ -0,0 +1,217 @@
+# Build the GDCM applications
+# Namely:
+# gdcmdump
+# gdcminfo
+# gdcmconv
+# gdcmanon
+
+
+if(WIN32 AND NOT CYGWIN)
+ include_directories(
+ "${GDCM_SOURCE_DIR}/Utilities/getopt"
+ )
+endif()
+
+# Add the include paths
+include_directories(
+ "${GDCM_BINARY_DIR}/Source/Common"
+ "${GDCM_SOURCE_DIR}/Source/Common"
+ "${GDCM_SOURCE_DIR}/Source/DataStructureAndEncodingDefinition"
+ "${GDCM_SOURCE_DIR}/Source/MediaStorageAndFileFormat"
+ "${GDCM_SOURCE_DIR}/Source/InformationObjectDefinition"
+ "${GDCM_SOURCE_DIR}/Source/MessageExchangeDefinition"
+# FIXME:
+ "${GDCM_SOURCE_DIR}/Source/DataDictionary"
+ "${GDCM_SOURCE_DIR}/Utilities"
+
+ )
+
+if(NOT GDCM_USE_SYSTEM_SOCKETXX)
+ include_directories(
+ "${GDCM_SOURCE_DIR}/Utilities/socketxx"
+ "${GDCM_SOURCE_DIR}/Utilities/socketxx/socket++" # local.h
+ "${GDCM_BINARY_DIR}/Utilities/socketxx/socket++" # config.h
+ )
+endif()
+
+if(WIN32)
+ if (BUILD_SHARED_LIBS)
+ add_definitions(-DGETOPT_DLL)
+ endif ()
+endif()
+
+set(GDCM_EXECUTABLE_NAME
+ gdcmdump
+ gdcmdiff
+ gdcmraw
+ gdcmscanner
+ gdcmanon
+ gdcmgendir
+ #gdcmoverlay
+ gdcmimg
+ #deflate
+ gdcmconv
+ #gdcmstream
+ gdcmtar
+ gdcminfo
+ gdcmscu
+ gdcmxml
+ gdcmpap3
+ )
+# poppler people have the worse API backward compatibility I know of.
+# there is absolutely no way to check the version of poppler
+# they change the API during a minor change of the version
+if(GDCM_USE_SYSTEM_POPPLER)
+ include(CheckCXXSourceCompiles)
+ set(CMAKE_REQUIRED_INCLUDES ${POPPLER_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${POPPLER_LIBRARIES})
+ CHECK_CXX_SOURCE_COMPILES(
+ "\#include \nint main() { globalParams = new GlobalParams(0); return 0;}"
+ LIBPOPPLER_GLOBALPARAMS_CSTOR_HAS_PARAM)
+ set(libpoppler_flags)
+ if(LIBPOPPLER_GLOBALPARAMS_CSTOR_HAS_PARAM)
+ list(APPEND libpoppler_flags -DLIBPOPPLER_GLOBALPARAMS_CSTOR_HAS_PARAM)
+ endif()
+ CHECK_CXX_SOURCE_COMPILES(
+ "\#include \nint main() { PDFDoc d((GooString*)NULL,(GooString*)NULL,(GooString*)NULL); d.getPDFVersion(); return 0;}"
+ LIBPOPPLER_PDFDOC_HAS_PDFVERSION)
+ if(LIBPOPPLER_PDFDOC_HAS_PDFVERSION)
+ list(APPEND libpoppler_flags -DLIBPOPPLER_PDFDOC_HAS_PDFVERSION)
+ endif()
+ if(libpoppler_flags)
+ set_source_files_properties(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gdcminfo.cxx
+ PROPERTIES COMPILE_FLAGS ${libpoppler_flags})
+ set_source_files_properties(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gdcmpdf.cxx
+ PROPERTIES COMPILE_FLAGS ${libpoppler_flags})
+ endif()
+ include_directories(${POPPLER_INCLUDE_DIRS})
+ set(GDCM_EXECUTABLE_NAME
+ ${GDCM_EXECUTABLE_NAME}
+ gdcmpdf
+ )
+endif()
+
+if(GDCM_USE_SYSTEM_LIBXML2)
+ include_directories(${LIBXML2_INCLUDE_DIR})
+endif()
+
+if(GDCM_USE_SYSTEM_OPENJPEG)
+ include_directories(${OPENJPEG_INCLUDE_DIRS} )
+else()
+ include_directories(
+ "${GDCM_BINARY_DIR}/Utilities/gdcmopenjpeg"
+ )
+endif()
+
+if(NOT BUILD_SHARED_LIBS)
+ set_source_files_properties(gdcmstream.cxx
+ PROPERTIES
+ COMPILE_FLAGS -DOPJ_STATIC
+ )
+endif()
+
+if(GDCM_USE_SYSTEM_PAPYRUS3)
+ include_directories(
+ ${PAPYRUS3_INCLUDE_DIRS}
+ )
+endif()
+
+foreach(exename ${GDCM_EXECUTABLE_NAME})
+ if(${exename} STREQUAL "gdcminfo")
+ add_executable(${exename} ${exename}.cxx puff.c)
+ else()
+ add_executable(${exename} ${exename}.cxx)
+ endif()
+ target_link_libraries(${exename} gdcmMSFF)
+ if(${exename} STREQUAL "gdcmpdf")
+ target_link_libraries(${exename} ${POPPLER_LIBRARIES})
+ elseif(${exename} STREQUAL "gdcmxml")
+ target_link_libraries(${exename} ${LIBXML2_LIBRARIES})
+ elseif(${exename} STREQUAL "gdcmscu")
+ if(NOT GDCM_USE_SYSTEM_SOCKETXX)
+ target_link_libraries(${exename} gdcmMEXD socketxx)
+ else()
+ target_link_libraries(${exename} gdcmMEXD socket++)
+ endif()
+ elseif(${exename} STREQUAL "gdcmstream")
+ target_link_libraries(${exename} ${GDCM_OPENJPEG_LIBRARIES})
+ elseif(${exename} STREQUAL "gdcminfo")
+ if(GDCM_USE_SYSTEM_POPPLER)
+ target_link_libraries(${exename} ${POPPLER_LIBRARIES})
+ endif()
+ endif()
+ if(GDCM_EXECUTABLE_PROPERTIES)
+ set_target_properties(${exename} PROPERTIES ${GDCM_EXECUTABLE_PROPERTIES})
+ endif()
+ if(WIN32 AND NOT CYGWIN)
+ target_link_libraries(${exename} gdcmgetopt)
+ endif()
+ if(NOT GDCM_INSTALL_NO_RUNTIME)
+ install(TARGETS ${exename}
+ EXPORT ${GDCM_TARGETS_NAME}
+ RUNTIME DESTINATION ${GDCM_INSTALL_BIN_DIR} COMPONENT Applications
+ )
+ endif()
+endforeach()
+
+if(GDCM_USE_SYSTEM_PAPYRUS3)
+ target_link_libraries(gdcmpap3 ${PAPYRUS3_LIBRARIES})
+endif()
+
+#if(BUILD_TESTING)
+# if(GDCM_DATA_ROOT)
+# file(MAKE_DIRECTORY ${GDCM_TEMP_DIRECTORY}/gdcmanon-gdcmdata)
+# add_test(gdcmanon-gdcmdata ${EXECUTABLE_OUTPUT_PATH}/gdcmanon
+# --continue # skip LIBIDO-16-ACR_NEMA-Volume.dcm
+# --certificate ${GDCM_SOURCE_DIR}/Testing/Source/Data/certificate.pem
+# ${GDCM_DATA_ROOT}
+# ${GDCM_TEMP_DIRECTORY}/gdcmanon-gdcmdata
+# )
+# endif()
+#endif()
+
+if(BUILD_TESTING)
+ # http://www.na-mic.org/Wiki/index.php/CTSC:ARRA:Mockup
+ # http://www.dicomserver.co.uk/
+ # the NAMIC server is offline, Steve Pieper has volunteered his servers, but they are intermittent
+ # IP Address: common.bwh.harvard.edu (155.52.248.49)
+ # Port: 11112
+ # AE Title: CTK_AE
+ # and
+ # IP Address: joe.bwh.harvard.edu (155.52.248.50)
+ # Port: 5678
+ # AE Title: CONQUESTSRV1
+ #since these servers have no guaranteed uptime, these tests should be changed to some other, known-to-be-up server
+ #add_test(gdcmscu-echo-CONQUEST ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --echo --call CONQUESTSRV1 joe.bwh.harvard.edu 5678)
+ add_test(NAME gdcmscu-echo-dicomserver COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --echo www.dicomserver.co.uk 11112)
+ if(GDCM_DATA_ROOT)
+ # CR-MONO1-10-chest.dcm gets rejected by www.dicomserver.co.uk:
+ # Tag 0x0,0x902 reported as Incorrect VM (1) - Minimum value is 2 : 'Array of N Elements' (error 42752)
+ # CR-MONO1-10-chest.dcm gets rejected by mi2b2.slicer.org
+ # Tag 0x0,0x902 reported as Acceptance of objects without Patient ID is disabled (error 43264)
+ #add_test(gdcmscu-store-CONQUEST ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --store --call CONQUESTSRV1 joe.bwh.harvard.edu 11112 ${GDCM_DATA_ROOT}/SIEMENS_MAGNETOM-12-MONO2-FileSeq1.dcm)
+ add_test(NAME gdcmscu-store-dicomserver COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --store www.dicomserver.co.uk 11112 ${GDCM_DATA_ROOT}/SIEMENS_MAGNETOM-12-MONO2-FileSeq1.dcm)
+ endif()
+ #add_test(gdcmscu-find-CONQUEST ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --find --patient --call CONQUESTSRV1 joe.bwh.harvard.edu 11112 --patientroot -k 10,10=X*)
+ add_test(NAME gdcmscu-find-dicomserver COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --find --patient www.dicomserver.co.uk 11112 --patientroot -k 10,20=*)
+
+ if(GDCM_DICOM_SERVER_AETITLE)
+ if(GDCM_DATA_ROOT)
+ # Let's C-STORE a limited subset of gdcmData for now:
+ set(CSTORE_DATA_FILES
+ CR-MONO1-10-chest.dcm # Implicit VR Little Endian: Default Transfer Syntax for DICOM
+ 012345.002.050.dcm # JPEG Lossless, Non-Hierarchical, First-Order Prediction
+ )
+ foreach(cstorefile ${CSTORE_DATA_FILES})
+ add_test(NAME gdcmscu-echo-${cstorefile} COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --echo --aetitle ${GDCM_DICOM_CLIENT_AETITLE} --call ${GDCM_DICOM_SERVER_AETITLE} ${GDCM_DICOM_SERVER_PEER} ${GDCM_DICOM_SERVER_PORT})
+ add_test(NAME gdcmscu-store-${cstorefile} COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --store --aetitle ${GDCM_DICOM_CLIENT_AETITLE} --call ${GDCM_DICOM_SERVER_AETITLE} ${GDCM_DICOM_SERVER_PEER} ${GDCM_DICOM_SERVER_PORT} ${GDCM_DATA_ROOT}/${cstorefile})
+ add_test(NAME gdcmscu-find-${cstorefile} COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --find --patient --aetitle ${GDCM_DICOM_CLIENT_AETITLE} --call ${GDCM_DICOM_SERVER_AETITLE} ${GDCM_DICOM_SERVER_PEER} ${GDCM_DICOM_SERVER_PORT} --patientroot -k 10,10=A* -k 10,20)
+ add_test(NAME gdcmscu-move1-${cstorefile} COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --move --patient --aetitle ${GDCM_DICOM_CLIENT_AETITLE} --call ${GDCM_DICOM_SERVER_AETITLE} ${GDCM_DICOM_SERVER_PEER} ${GDCM_DICOM_SERVER_PORT} -o ${CMAKE_CURRENT_BINARY_DIR} --patientroot -k 10,10=Anonymized --port-scp ${GDCM_DICOM_CLIENT_PORT})
+ add_test(NAME gdcmscu-move2-${cstorefile} COMMAND ${EXECUTABLE_OUTPUT_PATH}/gdcmscu --move --patient --aetitle ${GDCM_DICOM_CLIENT_AETITLE} --call ${GDCM_DICOM_SERVER_AETITLE} ${GDCM_DICOM_SERVER_PEER} ${GDCM_DICOM_SERVER_PORT} -o ${CMAKE_CURRENT_BINARY_DIR} --patientroot -k 10,10=X* --port-scp ${GDCM_DICOM_CLIENT_PORT})
+ endforeach()
+ endif()
+ endif()
+
+endif()
diff --git a/gdcm/Applications/Cxx/deflate.cxx b/gdcm/Applications/Cxx/deflate.cxx
new file mode 100644
index 0000000..3338baf
--- /dev/null
+++ b/gdcm/Applications/Cxx/deflate.cxx
@@ -0,0 +1,116 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#include "zlib/zlib.h"
+
+#define CHUNK 16384
+
+/* Decompress from file source to file dest until stream ends or EOF.
+ inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_DATA_ERROR if the deflate data is
+ invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+ the version of the library linked do not match, or Z_ERRNO if there
+ is an error reading or writing the files. */
+int inf(FILE *source, FILE *dest)
+{
+ int ret;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate inflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ //ret = inflateInit(&strm);
+ ret = inflateInit2(&strm, -MAX_WBITS);
+ if (ret != Z_OK)
+ {
+ std::cout << "could not inflateInit" << std::endl;
+ return ret;
+ }
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (strm.avail_in == 0)
+ break;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+void zerr(int ret)
+{
+ fputs("zpipe: ", stderr);
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stderr);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stderr);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stderr);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stderr);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stderr);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stderr);
+ }
+}
+
+int main()
+{
+ FILE *input;
+ FILE *output;
+ input = fopen("deflat.gz","r");
+ output = fopen("deflat.dcm","w");
+ if(!input) std::cout << "no input" << std::endl;
+ if(!output) std::cout << "no output" << std::endl;
+int r = inf(input, output);
+//std::cout << strerror( errno) << std::endl;
+// zerr(r) ;
+ return r;
+}
diff --git a/gdcm/Applications/Cxx/gdcm.cxx b/gdcm/Applications/Cxx/gdcm.cxx
new file mode 100644
index 0000000..5321d8d
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcm.cxx
@@ -0,0 +1,51 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+// gdcm will convert almost anything into dicom
+// - jpg
+// - j2k
+// - pdf
+// - raw..
+// - wav
+// - txt (??)
+// - 2D stuff -> RT
+// - new segmentation !
+
+// provide a dicom2 compatible API...maybe ??
+
+// For pdf:
+/*
+Provide a mapping for:
+
+Title: ITK Software Guide
+Subject: Medical Image Segmentation and Registration Toolkit
+Keywords: Registration,Segmentation,Guide
+Author: Luis Ibanez and Will Schroeder
+Creator: LaTeX with hyperref package
+Producer: dvips + GPL Ghostscript 8.15
+CreationDate: Mon Nov 21 19:34:28 2005
+ModDate: Mon Nov 21 19:34:28 2005
+Tagged: no
+Pages: 836
+Encrypted: no
+Page size: 522 x 675 pts
+File size: 5580502 bytes
+Optimized: no
+PDF version: 1.4
+
+into DICOM elements...
+*/
+int main(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmanon.cxx b/gdcm/Applications/Cxx/gdcmanon.cxx
new file mode 100644
index 0000000..afdbaef
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmanon.cxx
@@ -0,0 +1,829 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * PS 3.15 / E.1 / Basic Application Level Confidentiality Profile
+ * Implementation of E.1.1 De-identify & E.1.2 Re-identify
+ */
+
+#include
+
+#include "gdcmReader.h"
+#include "gdcmWriter.h"
+#include "gdcmVersion.h"
+#include "gdcmSystem.h"
+#include "gdcmCryptoFactory.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmAnonymizer.h"
+#include "gdcmGlobal.h"
+#include "gdcmDefs.h"
+#include "gdcmDirectory.h"
+
+#include
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcmanon: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+// FIXME
+ int deidentify = 0;
+ int reidentify = 0;
+
+
+static bool AnonymizeOneFileDumb(gdcm::Anonymizer &anon, const char *filename, const char *outfilename,
+ std::vector const &empty_tags, std::vector const &remove_tags, std::vector< std::pair > const & replace_tags, bool continuemode = false)
+{
+ gdcm::Reader reader;
+ reader.SetFileName( filename );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read : " << filename << std::endl;
+ if( continuemode )
+ {
+ std::cerr << "Skipping from anonymization process (continue mode)." << std::endl;
+ return true;
+ }
+ else
+ {
+ std::cerr << "Check [--continue] option for skipping files." << std::endl;
+ return false;
+ }
+ }
+ gdcm::File &file = reader.GetFile();
+
+ anon.SetFile( file );
+
+ if( empty_tags.empty() && replace_tags.empty() && remove_tags.empty() )
+ {
+ std::cerr << "No operation to be done." << std::endl;
+ return false;
+ }
+
+ std::vector::const_iterator it = empty_tags.begin();
+ bool success = true;
+ for(; it != empty_tags.end(); ++it)
+ {
+ success = success && anon.Empty( *it );
+ }
+ it = remove_tags.begin();
+ for(; it != remove_tags.end(); ++it)
+ {
+ success = success && anon.Remove( *it );
+ }
+
+ std::vector< std::pair >::const_iterator it2 = replace_tags.begin();
+ for(; it2 != replace_tags.end(); ++it2)
+ {
+ success = success && anon.Replace( it2->first, it2->second.c_str() );
+ }
+
+ gdcm::Writer writer;
+ writer.SetFileName( outfilename );
+ writer.SetFile( file );
+ if( !writer.Write() )
+ {
+ std::cerr << "Could not Write : " << outfilename << std::endl;
+ if( strcmp(filename,outfilename) != 0 )
+ {
+ gdcm::System::RemoveFile( outfilename );
+ }
+ else
+ {
+ std::cerr << "gdcmanon just corrupted: " << filename << " for you (data lost)." << std::endl;
+ }
+
+ return false;
+ }
+ return success;
+}
+
+static bool AnonymizeOneFile(gdcm::Anonymizer &anon, const char *filename, const char *outfilename, bool continuemode = false)
+{
+ gdcm::Reader reader;
+ reader.SetFileName( filename );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read : " << filename << std::endl;
+ if( continuemode )
+ {
+ std::cerr << "Skipping from anonymization process (continue mode)." << std::endl;
+ return true;
+ }
+ else
+ {
+ std::cerr << "Check [--continue] option for skipping files." << std::endl;
+ return false;
+ }
+ }
+ gdcm::File &file = reader.GetFile();
+ gdcm::MediaStorage ms;
+ ms.SetFromFile(file);
+ if( !gdcm::Defs::GetIODNameFromMediaStorage(ms) )
+ {
+ std::cerr << "The Media Storage Type of your file is not supported: " << ms << std::endl;
+ std::cerr << "Please report" << std::endl;
+ return false;
+ }
+
+ anon.SetFile( file );
+
+ if( deidentify )
+ {
+ //anon.RemovePrivateTags();
+ //anon.RemoveRetired();
+ if( !anon.BasicApplicationLevelConfidentialityProfile( true ) )
+ {
+ std::cerr << "Could not De-indentify : " << filename << std::endl;
+ return false;
+ }
+ }
+ else if ( reidentify )
+ {
+ if( !anon.BasicApplicationLevelConfidentialityProfile( false ) )
+ {
+ std::cerr << "Could not Re-indentify : " << filename << std::endl;
+ return false;
+ }
+ }
+
+ gdcm::FileMetaInformation &fmi = file.GetHeader();
+ fmi.Clear();
+
+ gdcm::Writer writer;
+ writer.SetFileName( outfilename );
+ writer.SetFile( file );
+ if( !writer.Write() )
+ {
+ std::cerr << "Could not Write : " << outfilename << std::endl;
+ if( strcmp(filename,outfilename) != 0 )
+ {
+ gdcm::System::RemoveFile( outfilename );
+ }
+ else
+ {
+ std::cerr << "gdcmanon just corrupted: " << filename << " for you (data lost)." << std::endl;
+ }
+
+ return false;
+ }
+ return true;
+}
+
+static bool GetRSAKeys(gdcm::CryptographicMessageSyntax &cms, const char *privpath = 0, const char *certpath = 0)
+{
+ if( privpath && *privpath )
+ {
+ if( !cms.ParseKeyFile( privpath ) )
+ {
+ std::cerr << "Could not parse Private Key: " << privpath << std::endl;
+ return false;
+ }
+ }
+
+ if( certpath && *certpath )
+ {
+ if( !cms.ParseCertificateFile( certpath ) )
+ {
+ std::cerr << "Could not parse Certificate Key: " << certpath << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmanon [OPTION]... FILE..." << std::endl;
+ std::cout << "PS 3.15 / E.1 / Basic Application Level Confidentiality Profile" << std::endl;
+ std::cout << "Implementation of E.1.1 De-identify & E.1.2 Re-identify" << std::endl;
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -e --de-identify (encrypt) De-identify DICOM (default)" << std::endl;
+ std::cout << " -d --re-identify (decrypt) Re-identify DICOM" << std::endl;
+ std::cout << " --dumb Dumb mode anonymizer" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -i --input DICOM filename / directory" << std::endl;
+ std::cout << " -o --output DICOM filename / directory" << std::endl;
+ std::cout << " -r --recursive recursively process (sub-)directories." << std::endl;
+ std::cout << " --continue Do not stop when file found is not DICOM." << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ std::cout << " --resources-path Resources path." << std::endl;
+ std::cout << " -k --key Path to RSA Private Key." << std::endl;
+ std::cout << " -c --certificate Path to Certificate." << std::endl;
+ std::cout << " -p --password Encryption passphrase." << std::endl;
+ std::cout << "Crypto Library Options:" << std::endl;
+ std::cout << " --crypto=" << std::endl;
+ std::cout << " openssl OpenSSL (default on non-Windows systems)." << std::endl;
+ std::cout << " capi Microsoft CryptoAPI (default on Windows systems)." << std::endl;
+ std::cout << " openssl-p7 Old OpenSSL implementation." << std::endl;
+ std::cout << "Encryption Algorithm Options:" << std::endl;
+ std::cout << " --des3 Triple DES." << std::endl;
+ std::cout << " --aes128 AES 128." << std::endl;
+ std::cout << " --aes192 AES 192." << std::endl;
+ std::cout << " --aes256 AES 256 (default)." << std::endl;
+ std::cout << "Dumb mode options:" << std::endl;
+ std::cout << " --empty %d,%d DICOM tag(s) to empty" << std::endl;
+ std::cout << " --remove %d,%d DICOM tag(s) to remove" << std::endl;
+ std::cout << " --replace %d,%d=%s DICOM tag(s) to replace" << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+ std::cout << " GDCM_RESOURCES_PATH path pointing to resources files (Part3.xml, ...)" << std::endl;
+}
+
+static gdcm::CryptographicMessageSyntax::CipherTypes GetFromString( const char * str )
+{
+ gdcm::CryptographicMessageSyntax::CipherTypes ciphertype;
+ if( strcmp( str, "des3" ) == 0 )
+ {
+ ciphertype = gdcm::CryptographicMessageSyntax::DES3_CIPHER;
+ }
+ else if( strcmp( str, "aes128" ) == 0 )
+ {
+ ciphertype = gdcm::CryptographicMessageSyntax::AES128_CIPHER;
+ }
+ else if( strcmp( str, "aes192" ) == 0 )
+ {
+ ciphertype = gdcm::CryptographicMessageSyntax::AES192_CIPHER;
+ }
+ else if( strcmp( str, "aes256" ) == 0 )
+ {
+ ciphertype = gdcm::CryptographicMessageSyntax::AES256_CIPHER;
+ }
+ else
+ {
+ // if unrecognized return aes 256...
+ ciphertype = gdcm::CryptographicMessageSyntax::AES256_CIPHER;
+ }
+ return ciphertype;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string filename;
+ gdcm::Directory::FilenamesType filenames;
+ std::string outfilename;
+ gdcm::Directory::FilenamesType outfilenames;
+ std::string root;
+ std::string xmlpath;
+ std::string rsa_path;
+ std::string cert_path;
+ std::string password;
+ int resourcespath = 0;
+ int dumb_mode = 0;
+ int des3 = 0;
+ int aes128 = 0;
+ int aes192 = 0;
+ int aes256 = 0;
+ int rootuid = 0;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ int recursive = 0;
+ int continuemode = 0;
+ int empty_tag = 0;
+ int remove_tag = 0;
+ int replace_tag = 0;
+ int crypto_api = 0;
+ std::vector empty_tags;
+ std::vector remove_tags;
+ std::vector< std::pair > replace_tags_value;
+ gdcm::Tag tag;
+ gdcm::CryptoFactory::CryptoLib crypto_lib;
+ crypto_lib = gdcm::CryptoFactory::DEFAULT;
+
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", required_argument, NULL, 'i'}, // i
+ {"output", required_argument, NULL, 'o'}, // o
+ {"root-uid", required_argument, &rootuid, 1}, // specific Root (not GDCM)
+ {"resources-path", required_argument, &resourcespath, 1},
+ {"de-identify", no_argument, NULL, 'e'},
+ {"re-identify", no_argument, NULL, 'd'},
+ {"key", required_argument, NULL, 'k'},
+ {"certificate", required_argument, NULL, 'c'}, // 7
+ {"password", required_argument, NULL, 'p'},
+
+ {"des3", no_argument, &des3, 1},
+ {"aes128", no_argument, &aes128, 1},
+ {"aes192", no_argument, &aes192, 1},
+ {"aes256", no_argument, &aes256, 1},
+
+ {"recursive", no_argument, NULL, 'r'},
+ {"dumb", no_argument, &dumb_mode, 1},
+ {"empty", required_argument, &empty_tag, 1}, // 15
+ {"remove", required_argument, &remove_tag, 1},
+ {"replace", required_argument, &replace_tag, 1},
+ {"continue", no_argument, &continuemode, 1},
+ {"crypto", required_argument, &crypto_api, 1}, //19
+
+ {"verbose", no_argument, NULL, 'V'},
+ {"warning", no_argument, NULL, 'W'},
+ {"debug", no_argument, NULL, 'D'},
+ {"error", no_argument, NULL, 'E'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:rdek:c:p:VWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ //if( option_index == 0 ) /* input */
+ // {
+ // assert( strcmp(s, "input") == 0 );
+ // assert( filename.empty() );
+ // filename = optarg;
+ // }
+ //else if( option_index == 1 ) /* output */
+ // {
+ // assert( strcmp(s, "output") == 0 );
+ // assert( outfilename.empty() );
+ // outfilename = optarg;
+ // }
+ /*else*/ if( option_index == 2 ) /* root-uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ assert( root.empty() );
+ root = optarg;
+ }
+ else if( option_index == 3 ) /* resources-path */
+ {
+ assert( strcmp(s, "resources-path") == 0 );
+ assert( xmlpath.empty() );
+ xmlpath = optarg;
+ }
+ //else if( option_index == 6 ) /* key */
+ // {
+ // assert( strcmp(s, "key") == 0 );
+ // assert( rsa_path.empty() );
+ // rsa_path = optarg;
+ // }
+ //else if( option_index == 7 ) /* certificate */
+ // {
+ // assert( strcmp(s, "certificate") == 0 );
+ // assert( cert_path.empty() );
+ // cert_path = optarg;
+ // }
+ else if( option_index == 15 ) /* empty */
+ {
+ assert( strcmp(s, "empty") == 0 );
+ if( !tag.ReadFromCommaSeparatedString(optarg) )
+ {
+ std::cerr << "Could not read Tag: " << optarg << std::endl;
+ return 1;
+ }
+ empty_tags.push_back( tag );
+ }
+ else if( option_index == 16 ) /* remove */
+ {
+ assert( strcmp(s, "remove") == 0 );
+ if( !tag.ReadFromCommaSeparatedString(optarg) )
+ {
+ std::cerr << "Could not read Tag: " << optarg << std::endl;
+ return 1;
+ }
+ remove_tags.push_back( tag );
+ }
+ else if( option_index == 17 ) /* replace */
+ {
+ assert( strcmp(s, "replace") == 0 );
+ if( !tag.ReadFromCommaSeparatedString(optarg) )
+ {
+ std::cerr << "Could not read Tag: " << optarg << std::endl;
+ return 1;
+ }
+ std::stringstream ss;
+ ss.str( optarg );
+ uint16_t dummy;
+ char cdummy; // comma
+ ss >> std::hex >> dummy;
+ assert( tag.GetGroup() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' );
+ ss >> std::hex >> dummy;
+ assert( tag.GetElement() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' || cdummy == '=' );
+ std::string str;
+ //ss >> str;
+ std::getline(ss, str); // do not skip whitespace
+ replace_tags_value.push_back( std::make_pair(tag, str) );
+ }
+ else if( option_index == 19 ) /* crypto */
+ {
+ assert( strcmp(s, "crypto") == 0 );
+ if (strcmp(optarg, "openssl") == 0)
+ crypto_lib = gdcm::CryptoFactory::OPENSSL;
+ else if (strcmp(optarg, "capi") == 0)
+ crypto_lib = gdcm::CryptoFactory::CAPI;
+ else if (strcmp(optarg, "openssl-p7") == 0)
+ crypto_lib = gdcm::CryptoFactory::OPENSSLP7;
+ else
+ {
+ std::cerr << "Cryptography library id not recognized: " << optarg << std::endl;
+ return 1;
+ }
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ case 'r':
+ recursive = 1;
+ break;
+
+ case 'k': // key
+ assert( rsa_path.empty() );
+ rsa_path = optarg;
+ break;
+
+ case 'c': // certificate
+ assert( cert_path.empty() );
+ cert_path = optarg;
+ break;
+
+ case 'p': // password
+ assert( password.empty() );
+ password = optarg;
+ break;
+
+ case 'e': // encrypt
+ deidentify = 1;
+ break;
+
+ case 'd': // decrypt
+ reidentify = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() == 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0];
+ outfilename = files[1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // by default de-identify
+ if( !deidentify && !reidentify && !dumb_mode)
+ {
+ deidentify = 1;
+ }
+
+ // one option only please
+ if( deidentify && reidentify )
+ {
+ std::cerr << "One option please" << std::endl;
+ return 1;
+ }
+ // dumb mode vs smart mode:
+ if( ( deidentify || reidentify ) && dumb_mode )
+ {
+ std::cerr << "One option please" << std::endl;
+ return 1;
+ }
+
+ gdcm::CryptoFactory* crypto_factory = NULL;
+ if( deidentify || reidentify )
+ {
+ crypto_factory = gdcm::CryptoFactory::GetFactoryInstance(crypto_lib);
+ if (!crypto_factory)
+ {
+ std::cerr << "Requested cryptoraphic library not configured." << std::endl;
+ return 1;
+ }
+ }
+
+ // by default AES 256
+ gdcm::CryptographicMessageSyntax::CipherTypes ciphertype =
+ gdcm::CryptographicMessageSyntax::AES256_CIPHER;
+ if( !dumb_mode )
+ {
+ if( !des3 && !aes128 && !aes192 && !aes256 )
+ {
+ aes256 = 1;
+ }
+
+ if( des3 )
+ {
+ ciphertype = GetFromString( "des3" );
+ }
+ else if( aes128 )
+ {
+ ciphertype = GetFromString( "aes128" );
+ }
+ else if( aes192 )
+ {
+ ciphertype = GetFromString( "aes192" );
+ }
+ else if( aes256 )
+ {
+ ciphertype = GetFromString( "aes256" );
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ if( !gdcm::System::FileExists(filename.c_str()) )
+ {
+ std::cerr << "Could not find file: " << filename << std::endl;
+ return 1;
+ }
+
+ // Are we in single file or directory mode:
+ unsigned int nfiles = 1;
+ gdcm::Directory dir;
+ if( gdcm::System::FileIsDirectory(filename.c_str()) )
+ {
+ if( !gdcm::System::FileIsDirectory(outfilename.c_str()) )
+ {
+ if( gdcm::System::FileExists( outfilename.c_str() ) )
+ {
+ std::cerr << "Could not create directory since " << outfilename << " is already a file" << std::endl;
+ return 1;
+ }
+
+ }
+ // For now avoid user mistake
+ if( filename == outfilename )
+ {
+ std::cerr << "Input directory should be different from output directory" << std::endl;
+ return 1;
+ }
+ nfiles = dir.Load(filename, (recursive > 0 ? true : false));
+ filenames = dir.GetFilenames();
+ gdcm::Directory::FilenamesType::const_iterator it = filenames.begin();
+ // Prepare outfilenames
+ for( ; it != filenames.end(); ++it )
+ {
+ std::string dup = *it; // make a copy
+ std::string &out = dup.replace(0, filename.size(), outfilename );
+ outfilenames.push_back( out );
+ }
+ // Prepare outdirectory
+ gdcm::Directory::FilenamesType const &dirs = dir.GetDirectories();
+ gdcm::Directory::FilenamesType::const_iterator itdir = dirs.begin();
+ for( ; itdir != dirs.end(); ++itdir )
+ {
+ std::string dirdup = *itdir; // make a copy
+ std::string &dirout = dirdup.replace(0, filename.size(), outfilename );
+ //std::cout << "Making directory: " << dirout << std::endl;
+ if( !gdcm::System::MakeDirectory( dirout.c_str() ) )
+ {
+ std::cerr << "Could not create directory: " << dirout << std::endl;
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ filenames.push_back( filename );
+ outfilenames.push_back( outfilename );
+ }
+
+ if( filenames.size() != outfilenames.size() )
+ {
+ std::cerr << "Something went really wrong" << std::endl;
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmanon" );
+ gdcm::Global& g = gdcm::Global::GetInstance();
+ if( !resourcespath )
+ {
+ const char *xmlpathenv = getenv("GDCM_RESOURCES_PATH");
+ if( xmlpathenv )
+ {
+ // Make sure to look for XML dict in user explicitly specified dir first:
+ xmlpath = xmlpathenv;
+ resourcespath = 1;
+ }
+ }
+ if( resourcespath )
+ {
+ // xmlpath is set either by the cmd line option or the env var
+ if( !g.Prepend( xmlpath.c_str() ) )
+ {
+ std::cerr << "Specified Resources Path is not valid: " << xmlpath << std::endl;
+ return 1;
+ }
+ }
+ // All set, then load the XML files:
+ if( !g.LoadResourcesFiles() )
+ {
+ std::cerr << "Could not load XML file from specified path" << std::endl;
+ return 1;
+ }
+ const gdcm::Defs &defs = g.GetDefs(); (void)defs;
+ if( !rootuid )
+ {
+ // only read the env var if no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ // root is set either by the cmd line option or the env var
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ // Get private key/certificate
+ std::auto_ptr cms_ptr;
+ if( crypto_factory )
+ {
+ cms_ptr = std::auto_ptr(crypto_factory->CreateCMSProvider());
+ }
+ if( !dumb_mode )
+ {
+ if( !GetRSAKeys(*cms_ptr, rsa_path.c_str(), cert_path.c_str() ) )
+ {
+ return 1;
+ }
+ if (!password.empty() && !cms_ptr->SetPassword(password.c_str(), password.length()) )
+ {
+ std::cerr << "Could not set the password " << std::endl;
+ return 1;
+ }
+ cms_ptr->SetCipherType( ciphertype );
+ }
+
+ // Setup gdcm::Anonymizer
+ gdcm::Anonymizer anon;
+ if( !dumb_mode )
+ {
+ anon.SetCryptographicMessageSyntax( cms_ptr.get() );
+ }
+
+ 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, (continuemode > 0 ? true: false)) )
+ {
+ //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, (continuemode > 0 ? true: false)) )
+ {
+ //std::cerr << "Could not anonymize: " << in << std::endl;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmcheck.cxx b/gdcm/Applications/Cxx/gdcmcheck.cxx
new file mode 100644
index 0000000..e1b466a
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmcheck.cxx
@@ -0,0 +1,24 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * Need to move gdcminfo --deflated here
+ * gdcmcheck would check for improper Basic Offset Table
+ * JP2 in place of JPEG
+ * more to come ?
+ */
+
+int main(int, char *[])
+{
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmconv.cxx b/gdcm/Applications/Cxx/gdcmconv.cxx
new file mode 100644
index 0000000..702d303
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmconv.cxx
@@ -0,0 +1,1464 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * HISTORY:
+ * In GDCM 1.X the prefered terms was 'ReWrite', however one author of GDCM dislike
+ * the term ReWrite since it is associated with the highly associated with the Rewrite
+ * notion in software programming where using reinvent the wheel and rewrite from scratch code
+ * the term convert was prefered
+ *
+ * Tools to conv. Goals being to 'purify' a DICOM file.
+ * For now it will do the minimum:
+ * - If Group Length is present, the length is guarantee to be correct
+ * - If Element with Group Tag 0x1, 0x3, 0x5 or 0x7 are present they are
+ * simply discarded (not written).
+ * - Elements are written in alphabetical order
+ * - 32bits VR have the residue bytes sets to 0x0,0x0
+ * - Same goes from Item Length end delimitor, sets to 0x0,0x0
+ * - All buggy files (wrong length: GE, 13 and Siemens Leonardo) are fixed
+ * - All size are even (no odd length from gdcm 1.x)
+ *
+ * // \todo:
+ * // --preamble: clean preamble
+ * // --meta: clean meta (meta info version...)
+ * // --dicomV3 (use TS unless not supported)
+ * // --recompute group-length
+ * // --undefined sq
+ * // --explicit sq *
+ * \todo in a close future:
+ * - Set appropriate VR from DICOM dict
+ * - Rewrite PMS SQ into DICOM SQ
+ * - Rewrite Implicit SQ with defined length as undefined length
+ * - PixelData with `overlay` in unused bits should be cleanup
+ * - Any broken JPEG file (wrong bits) should be fixed
+ * - DicomObject bug should be fixed
+ * - Meta and Dataset should have a matching UID (more generally File Meta
+ * should be correct (Explicit!) and consistant with DataSet)
+ * - User should be able to specify he wants Group Length (or remove them)
+ * - Media SOP should be correct (deduct from something else or set to
+ * SOP Secondary if all else fail).
+ * - Padding character should be correct
+ *
+ * \todo distant future:
+ * - Later on, it should run through a Validator
+ * which will make sure all field 1, 1C are present and those only
+ * - In a perfect world I should remove private tags and transform them into
+ * public fields.
+ * - DA should be correct, PN should be correct (no space!)
+ * - Enumerated Value should be correct
+ */
+/*
+ check-meta is ideal for image like:
+
+ gdcmconv -C gdcmData/PICKER-16-MONO2-No_DicomV3_Preamble.dcm bla.dcm
+*/
+#include "gdcmReader.h"
+#include "gdcmFileDerivation.h"
+#include "gdcmAnonymizer.h"
+#include "gdcmVersion.h"
+#include "gdcmPixmapReader.h"
+#include "gdcmPixmapWriter.h"
+#include "gdcmWriter.h"
+#include "gdcmSystem.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmIconImageGenerator.h"
+#include "gdcmAttribute.h"
+#include "gdcmSequenceOfItems.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmImage.h"
+#include "gdcmImageChangeTransferSyntax.h"
+#include "gdcmImageApplyLookupTable.h"
+#include "gdcmImageFragmentSplitter.h"
+#include "gdcmImageChangePlanarConfiguration.h"
+#include "gdcmImageChangePhotometricInterpretation.h"
+#include "gdcmFileExplicitFilter.h"
+#include "gdcmJPEG2000Codec.h"
+#include "gdcmJPEGCodec.h"
+#include "gdcmJPEGLSCodec.h"
+#include "gdcmSequenceOfFragments.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+struct SetSQToUndefined
+{
+ void operator() (gdcm::DataElement &de) {
+ de.SetVLToUndefined();
+ }
+};
+
+static void PrintVersion()
+{
+ std::cout << "gdcmconv: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintLossyWarning()
+{
+ std::cout << "You have selected a lossy compression transfer syntax." << std::endl;
+ std::cout << "This will degrade the quality of your input image, and can." << std::endl;
+ std::cout << "impact professional interpretation of the image." << std::endl;
+ std::cout << "Do not use if you do not understand the risk." << std::endl;
+ std::cout << "WARNING: this mode is very experimental." << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmconv [OPTION] input.dcm output.dcm" << std::endl;
+ std::cout << "Convert a DICOM file into another DICOM file.\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input DICOM filename" << std::endl;
+ std::cout << " -o --output DICOM filename" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -X --explicit Change Transfer Syntax to explicit." << std::endl;
+ std::cout << " -M --implicit Change Transfer Syntax to implicit." << std::endl;
+ std::cout << " -U --use-dict Use dict for VR (only public by default)." << std::endl;
+ std::cout << " --with-private-dict Use private dict for VR (advanced user only)." << std::endl;
+ std::cout << " -C --check-meta Check File Meta Information (advanced user only)." << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ std::cout << " --remove-gl Remove group length (deprecated in DICOM 2008)." << std::endl;
+ std::cout << " --remove-private-tags Remove private tags." << std::endl;
+ std::cout << " --remove-retired Remove retired tags." << std::endl;
+ std::cout << "Image only Options:" << std::endl;
+ std::cout << " -l --apply-lut Apply LUT (non-standard, advanced user only)." << std::endl;
+ std::cout << " -P --photometric-interpretation %s Change Photometric Interpretation (when possible)." << std::endl;
+ std::cout << " -w --raw Decompress image." << std::endl;
+ std::cout << " -d --deflated Compress using deflated (gzip)." << std::endl;
+ std::cout << " -J --jpeg Compress image in jpeg." << std::endl;
+ std::cout << " -K --j2k Compress image in j2k." << std::endl;
+ std::cout << " -L --jpegls Compress image in jpeg-ls." << std::endl;
+ std::cout << " -R --rle Compress image in rle (lossless only)." << std::endl;
+ std::cout << " -F --force Force decompression/merging before recompression/splitting." << std::endl;
+ std::cout << " --generate-icon Generate icon." << std::endl;
+ std::cout << " --icon-minmax %d,%d Min/Max value for icon." << std::endl;
+ std::cout << " --icon-auto-minmax Automatically commpute best Min/Max values for icon." << std::endl;
+ std::cout << " --compress-icon Decide whether icon follows main TransferSyntax or remains uncompressed." << std::endl;
+ std::cout << " --planar-configuration [01] Change planar configuration." << std::endl;
+ std::cout << " -Y --lossy Use the lossy (if possible) compressor." << std::endl;
+ std::cout << " -S --split %d Write 2D image with multiple fragments (using max size)" << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << " --quiet do not print to stdout." << std::endl;
+ std::cout << "JPEG Options:" << std::endl;
+ std::cout << " -q --quality %*f set quality." << std::endl;
+ std::cout << "JPEG-LS Options:" << std::endl;
+ std::cout << " -e --lossy-error %*i set error." << std::endl;
+ std::cout << "J2K Options:" << std::endl;
+ std::cout << " -r --rate %*f set rate." << std::endl;
+ std::cout << " -q --quality %*f set quality." << std::endl;
+ std::cout << " -t --tile %d,%d set tile size." << std::endl;
+ std::cout << " -n --number-resolution %d set number of resolution." << std::endl;
+ std::cout << " --irreversible set irreversible." << std::endl;
+ std::cout << "Special Options:" << std::endl;
+ std::cout << " -I --ignore-errors convert even if file is corrupted (advanced users only, see disclaimers)." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+/*
+ * Default behavior for root UID is:
+ * By default the GDCM one is used
+ * If GDCM_ROOT_UID is set, then use this one instead
+ * If --root-uid is explicitly set on the command line, it will override any other defined behavior
+ */
+}
+
+template
+static size_t readvector(std::vector &v, const char *str)
+{
+ if( !str ) return 0;
+ std::istringstream os( str );
+ T f;
+ while( os >> f )
+ {
+ v.push_back( f );
+ os.get(); // == ","
+ }
+ return v.size();
+}
+
+namespace gdcm
+{
+static bool derives( File & file, const Pixmap& compressed_image )
+{
+#if 1
+ DataSet &ds = file.GetDataSet();
+
+ if( !ds.FindDataElement( Tag(0x0008,0x0016) )
+ || ds.GetDataElement( Tag(0x0008,0x0016) ).IsEmpty() )
+ {
+ return false;
+ }
+ if( !ds.FindDataElement( Tag(0x0008,0x0018) )
+ || ds.GetDataElement( Tag(0x0008,0x0018) ).IsEmpty() )
+ {
+ return false;
+ }
+ const DataElement &sopclassuid = ds.GetDataElement( Tag(0x0008,0x0016) );
+ const DataElement &sopinstanceuid = ds.GetDataElement( Tag(0x0008,0x0018) );
+ // Make sure that const char* pointer will be properly padded with \0 char:
+ std::string sopclassuid_str( sopclassuid.GetByteValue()->GetPointer(), sopclassuid.GetByteValue()->GetLength() );
+ std::string sopinstanceuid_str( sopinstanceuid.GetByteValue()->GetPointer(), sopinstanceuid.GetByteValue()->GetLength() );
+ ds.Remove( Tag(0x8,0x18) );
+
+ FileDerivation fd;
+ fd.SetFile( file );
+ fd.AddReference( sopclassuid_str.c_str(), sopinstanceuid_str.c_str() );
+
+ // CID 7202 Source Image Purposes of Reference
+ // {"DCM",121320,"Uncompressed predecessor"},
+ fd.SetPurposeOfReferenceCodeSequenceCodeValue( 121320 );
+
+ // CID 7203 Image Derivation
+ // { "DCM",113040,"Lossy Compression" },
+ fd.SetDerivationCodeSequenceCodeValue( 113040 );
+ fd.SetDerivationDescription( "lossy conversion" );
+ if( !fd.Derive() )
+ {
+ std::cerr << "Sorry could not derive using input info" << std::endl;
+ return false;
+ }
+
+
+#else
+/*
+(0008,2111) ST [Lossy compression with JPEG extended sequential 8 bit, IJG quality... # 102, 1 DerivationDescription
+(0008,2112) SQ (Sequence with explicit length #=1) # 188, 1 SourceImageSequence
+ (fffe,e000) na (Item with explicit length #=3) # 180, 1 Item
+ (0008,1150) UI =UltrasoundImageStorage # 28, 1 ReferencedSOPClassUID
+ (0008,1155) UI [1.2.840.1136190195280574824680000700.3.0.1.19970424140438] # 58, 1 ReferencedSOPInstanceUID
+ (0040,a170) SQ (Sequence with explicit length #=1) # 66, 1 PurposeOfReferenceCodeSequence
+ (fffe,e000) na (Item with explicit length #=3) # 58, 1 Item
+ (0008,0100) SH [121320] # 6, 1 CodeValue
+ (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
+ (0008,0104) LO [Uncompressed predecessor] # 24, 1 CodeMeaning
+ (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
+ (fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
+ (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
+(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
+*/
+ const Tag sisq(0x8,0x2112);
+ SequenceOfItems * sqi;
+ sqi = new SequenceOfItems;
+ DataElement de( sisq);
+ de.SetVR( VR::SQ );
+ de.SetValue( *sqi );
+ de.SetVLToUndefined();
+
+ DataSet &ds = file.GetDataSet();
+ ds.Insert( de );
+{
+ // (0008,0008) CS [ORIGINAL\SECONDARY] # 18, 2 ImageType
+ gdcm::Attribute<0x0008,0x0008> at3;
+ static const gdcm::CSComp values[] = {"DERIVED","SECONDARY"};
+ at3.SetValues( values, 2, true ); // true => copy data !
+ if( ds.FindDataElement( at3.GetTag() ) )
+ {
+ const gdcm::DataElement &de = ds.GetDataElement( at3.GetTag() );
+ at3.SetFromDataElement( de );
+ // Make sure that value #1 is at least 'DERIVED', so override in all cases:
+ at3.SetValue( 0, values[0] );
+ }
+ ds.Replace( at3.GetAsDataElement() );
+
+}
+{
+ Attribute<0x0008,0x2111> at1;
+ at1.SetValue( "lossy conversion" );
+ ds.Replace( at1.GetAsDataElement() );
+}
+
+ sqi = (SequenceOfItems*)ds.GetDataElement( sisq ).GetSequenceOfItems();
+ sqi->SetLengthToUndefined();
+
+ if( !sqi->GetNumberOfItems() )
+ {
+ Item item; //( Tag(0xfffe,0xe000) );
+ item.SetVLToUndefined();
+ sqi->AddItem( item );
+ }
+
+ Item &item1 = sqi->GetItem(1);
+ DataSet &subds = item1.GetNestedDataSet();
+/*
+ (0008,1150) UI =UltrasoundImageStorage # 28, 1 ReferencedSOPClassUID
+ (0008,1155) UI [1.2.840.1136190195280574824680000700.3.0.1.19970424140438] # 58, 1 ReferencedSOPInstanceUID
+*/
+{
+ DataElement sopinstanceuid = ds.GetDataElement( Tag(0x0008,0x0016) );
+ sopinstanceuid.SetTag( Tag(0x8,0x1150 ) );
+ subds.Replace( sopinstanceuid );
+ DataElement sopclassuid = ds.GetDataElement( Tag(0x0008,0x0018) );
+ sopclassuid.SetTag( Tag(0x8,0x1155 ) );
+ subds.Replace( sopclassuid );
+ ds.Remove( Tag(0x8,0x18) );
+}
+
+ const Tag prcs(0x0040,0xa170);
+ if( !subds.FindDataElement( prcs) )
+ {
+ SequenceOfItems *sqi2 = new SequenceOfItems;
+ DataElement de( prcs );
+ de.SetVR( VR::SQ );
+ de.SetValue( *sqi2 );
+ de.SetVLToUndefined();
+ subds.Insert( de );
+ }
+
+ sqi = (SequenceOfItems*)subds.GetDataElement( prcs ).GetSequenceOfItems();
+ sqi->SetLengthToUndefined();
+
+ if( !sqi->GetNumberOfItems() )
+ {
+ Item item; //( Tag(0xfffe,0xe000) );
+ item.SetVLToUndefined();
+ sqi->AddItem( item );
+ }
+ Item &item2 = sqi->GetItem(1);
+ DataSet &subds2 = item2.GetNestedDataSet();
+
+/*
+ (0008,0100) SH [121320] # 6, 1 CodeValue
+ (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
+ (0008,0104) LO [Uncompressed predecessor] # 24, 1 CodeMeaning
+*/
+
+ Attribute<0x0008,0x0100> at1;
+ at1.SetValue( "121320" );
+ subds2.Replace( at1.GetAsDataElement() );
+ Attribute<0x0008,0x0102> at2;
+ at2.SetValue( "DCM" );
+ subds2.Replace( at2.GetAsDataElement() );
+ Attribute<0x0008,0x0104> at3;
+ at3.SetValue( "Uncompressed predecessor" );
+ subds2.Replace( at3.GetAsDataElement() );
+
+/*
+(0008,9215) SQ (Sequence with explicit length #=1) # 98, 1 DerivationCodeSequence
+ (fffe,e000) na (Item with explicit length #=3) # 90, 1 Item
+ (0008,0100) SH [121327] # 6, 1 CodeValue
+ (0008,0102) SH [DCM] # 4, 1 CodingSchemeDesignator
+ (0008,0104) LO [Full fidelity image, uncompressed or lossless compressed] # 56, 1 CodeMeaning
+ (fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
+(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
+*/
+{
+ const Tag sisq(0x8,0x9215);
+ SequenceOfItems * sqi;
+ sqi = new SequenceOfItems;
+ DataElement de( sisq );
+ de.SetVR( VR::SQ );
+ de.SetValue( *sqi );
+ de.SetVLToUndefined();
+ ds.Insert( de );
+ sqi = (SequenceOfItems*)ds.GetDataElement( sisq ).GetSequenceOfItems();
+ sqi->SetLengthToUndefined();
+
+ if( !sqi->GetNumberOfItems() )
+ {
+ Item item; //( Tag(0xfffe,0xe000) );
+ item.SetVLToUndefined();
+ sqi->AddItem( item );
+ }
+
+ Item &item1 = sqi->GetItem(1);
+ DataSet &subds3 = item1.GetNestedDataSet();
+
+ Attribute<0x0008,0x0100> at1;
+ at1.SetValue( "121327" );
+ subds3.Replace( at1.GetAsDataElement() );
+ Attribute<0x0008,0x0102> at2;
+ at2.SetValue( "DCM" );
+ subds3.Replace( at2.GetAsDataElement() );
+ Attribute<0x0008,0x0104> at3;
+ at3.SetValue( "Full fidelity image, uncompressed or lossless compressed" );
+ subds3.Replace( at3.GetAsDataElement() );
+}
+#endif
+
+{
+ /*
+ (0028,2110) CS [01] # 2, 1 LossyImageCompression
+ (0028,2112) DS [15.95] # 6, 1 LossyImageCompressionRatio
+ (0028,2114) CS [ISO_10918_1] # 12, 1 LossyImageCompressionMethod
+ */
+ const DataElement & pixeldata = compressed_image.GetDataElement();
+ size_t len = pixeldata.GetSequenceOfFragments()->ComputeByteLength();
+ size_t reflen = compressed_image.GetBufferLength();
+ double ratio = (double)reflen / (double)len;
+ Attribute<0x0028,0x2110> at1;
+ at1.SetValue( "01" );
+ ds.Replace( at1.GetAsDataElement() );
+ Attribute<0x0028,0x2112> at2;
+ at2.SetValues( &ratio, 1);
+ ds.Replace( at2.GetAsDataElement() );
+ Attribute<0x0028,0x2114> at3;
+
+ // ImageWriter will properly set attribute 0028,2114 (Lossy Image Compression Method)
+}
+
+return true;
+
+}
+} // end namespace gdcm
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string filename;
+ std::string outfilename;
+ std::string root;
+ int explicitts = 0; // explicit is a reserved keyword
+ int implicit = 0;
+ int quiet = 0;
+ int lut = 0;
+ int raw = 0;
+ int deflated = 0;
+ int rootuid = 0;
+ int checkmeta = 0;
+ int jpeg = 0;
+ int jpegls = 0;
+ int j2k = 0;
+ int lossy = 0;
+ int split = 0;
+ int fragmentsize = 0;
+ int rle = 0;
+ int force = 0;
+ int planarconf = 0;
+ int planarconfval = 0;
+ double iconmin = 0;
+ double iconmax = 0;
+ int usedict = 0;
+ int compressicon = 0;
+ int generateicon = 0;
+ int iconminmax = 0;
+ int iconautominmax = 0;
+ int removegrouplength = 0;
+ int removeprivate = 0;
+ int removeretired = 0;
+ int photometricinterpretation = 0;
+ std::string photometricinterpretation_str;
+ int quality = 0;
+ int rate = 0;
+ int tile = 0;
+ int nres = 0;
+ int nresvalue = 6; // ??
+ std::vector qualities;
+ std::vector rates;
+ std::vector tilesize;
+ int irreversible = 0;
+ int changeprivatetags = 0;
+
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ int ignoreerrors = 0;
+ int jpeglserror = 0;
+ int jpeglserror_value = 0;
+
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"group-length", 1, 0, 0}, // valid / create / remove
+ {"preamble", 1, 0, 0}, // valid / create / remove
+ {"padding", 1, 0, 0}, // valid (\0 -> space) / optimize (at most 1 byte of padding)
+ {"vr", 1, 0, 0}, // valid
+ {"sop", 1, 0, 0}, // default to SC...
+ {"iod", 1, 0, 0}, // valid
+ {"meta", 1, 0, 0}, // valid / create / remove
+ {"dataset", 1, 0, 0}, // valid / create / remove?
+ {"sequence", 1, 0, 0}, // defined / undefined
+ {"deflate", 1, 0, 0}, // 1 - 9 / best = 9 / fast = 1
+ {"tag", 1, 0, 0}, // need to specify a tag xxxx,yyyy = value to override default
+ {"name", 1, 0, 0}, // same as tag but explicit use of name
+ {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
+ {"check-meta", 0, &checkmeta, 1}, // specific Root (not GDCM)
+// Image specific options:
+ {"pixeldata", 1, 0, 0}, // valid
+ {"apply-lut", 0, &lut, 1}, // default (implicit VR, LE) / Explicit LE / Explicit BE
+ {"raw", 0, &raw, 1}, // default (implicit VR, LE) / Explicit LE / Explicit BE
+ {"deflated", 0, &deflated, 1}, // DeflatedExplicitVRLittleEndian
+ {"lossy", 0, &lossy, 1}, // Specify lossy comp
+ {"force", 0, &force, 1}, // force decompression even if target compression is identical
+ {"jpeg", 0, &jpeg, 1}, // JPEG lossy / lossless
+ {"jpegls", 0, &jpegls, 1}, // JPEG-LS: lossy / lossless
+ {"j2k", 0, &j2k, 1}, // J2K: lossy / lossless
+ {"rle", 0, &rle, 1}, // lossless !
+ {"mpeg2", 0, 0, 0}, // lossy !
+ {"jpip", 0, 0, 0}, // ??
+ {"split", 1, &split, 1}, // split fragments
+ {"planar-configuration", 1, &planarconf, 1}, // Planar Configuration
+ {"explicit", 0, &explicitts, 1}, //
+ {"implicit", 0, &implicit, 1}, //
+ {"use-dict", 0, &usedict, 1}, //
+ {"generate-icon", 0, &generateicon, 1}, //
+ {"icon-minmax", 1, &iconminmax, 1}, //
+ {"icon-auto-minmax", 0, &iconautominmax, 1}, //
+ {"compress-icon", 0, &compressicon, 1}, //
+ {"remove-gl", 0, &removegrouplength, 1}, //
+ {"remove-private-tags", 0, &removeprivate, 1}, //
+ {"remove-retired", 0, &removeretired, 1}, //
+ {"photometric-interpretation", 1, &photometricinterpretation, 1}, //
+ {"with-private-dict", 0, &changeprivatetags, 1}, //
+// j2k :
+ {"rate", 1, &rate, 1}, //
+ {"quality", 1, &quality, 1}, // will also work for regular jpeg compressor
+ {"tile", 1, &tile, 1}, //
+ {"number-resolution", 1, &nres, 1}, //
+ {"irreversible", 0, &irreversible, 1}, //
+ {"allowed-error", 1, &jpeglserror, 1}, //
+
+// General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {"ignore-errors", 0, &ignoreerrors, 1},
+ {"quiet", 0, &quiet, 1},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:XMUClwdJKLRFYS:P:VWDEhvIr:q:t:n:e:",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 14 ) /* root-uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ assert( root.empty() );
+ root = optarg;
+ }
+ else if( option_index == 28 ) /* split */
+ {
+ assert( strcmp(s, "split") == 0 );
+ fragmentsize = atoi(optarg);
+ }
+ else if( option_index == 29 ) /* planar conf*/
+ {
+ assert( strcmp(s, "planar-configuration") == 0 );
+ planarconfval = atoi(optarg);
+ }
+ else if( option_index == 34 ) /* icon minmax*/
+ {
+ assert( strcmp(s, "icon-minmax") == 0 );
+ std::stringstream ss;
+ ss.str( optarg );
+ ss >> iconmin;
+ char comma;
+ ss >> comma;
+ ss >> iconmax;
+ }
+ else if( option_index == 40 ) /* photometricinterpretation */
+ {
+ assert( strcmp(s, "photometric-interpretation") == 0 );
+ photometricinterpretation_str = optarg;
+ }
+ else if( option_index == 42 ) /* rate */
+ {
+ assert( strcmp(s, "rate") == 0 );
+ readvector(rates, optarg);
+ }
+ else if( option_index == 43 ) /* quality */
+ {
+ assert( strcmp(s, "quality") == 0 );
+ readvector(qualities, optarg);
+ }
+ else if( option_index == 44 ) /* tile */
+ {
+ assert( strcmp(s, "tile") == 0 );
+ size_t n = readvector(tilesize, optarg);
+ assert( n == 2 ); (void)n;
+ }
+ else if( option_index == 45 ) /* number of resolution */
+ {
+ assert( strcmp(s, "number-resolution") == 0 );
+ nresvalue = atoi(optarg);
+ }
+ else if( option_index == 47 ) /* JPEG-LS error */
+ {
+ assert( strcmp(s, "allowed-error") == 0 );
+ jpeglserror_value = atoi(optarg);
+ }
+ //printf (" with arg %s, index = %d", optarg, option_index);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ //printf ("option o with value '%s'\n", optarg);
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ case 'X':
+ explicitts = 1;
+ break;
+
+ case 'M':
+ implicit = 1;
+ break;
+
+ case 'U':
+ usedict = 1;
+ break;
+
+ case 'C':
+ checkmeta = 1;
+ break;
+
+ // root-uid
+
+ case 'l':
+ lut = 1;
+ break;
+
+ case 'w':
+ raw = 1;
+ break;
+
+ case 'e':
+ jpeglserror = 1;
+ jpeglserror_value = atoi(optarg);
+ break;
+
+ case 'd':
+ deflated = 1;
+ break;
+
+ case 'J':
+ jpeg = 1;
+ break;
+
+ case 'K':
+ j2k = 1;
+ break;
+
+ case 'L':
+ jpegls = 1;
+ break;
+
+ case 'R':
+ rle = 1;
+ break;
+
+ case 'F':
+ force = 1;
+ break;
+
+ case 'Y':
+ lossy = 1;
+ break;
+
+ case 'S':
+ split = 1;
+ fragmentsize = atoi(optarg);
+ break;
+
+ case 'P':
+ photometricinterpretation = 1;
+ photometricinterpretation_str = optarg;
+ break;
+
+ case 'r':
+ rate = 1;
+ readvector(rates, optarg);
+ break;
+
+ case 'q':
+ quality = 1;
+ readvector(qualities, optarg);
+ break;
+
+ case 't':
+ tile = 1;
+ readvector(tilesize, optarg);
+ break;
+
+ case 'n':
+ nres = 1;
+ nresvalue = atoi(optarg);
+ break;
+
+ // General option
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case 'I':
+ ignoreerrors = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ // For now only support one input / one output
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: ");
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() == 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0];
+ outfilename = files[1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+ if( outfilename.empty() )
+ {
+ //std::cerr << "Need output file (-o)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmconv" );
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ // root is set either by the cmd line option or the env var
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ if( removegrouplength || removeprivate || removeretired )
+ {
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read: " << filename << std::endl;
+ return 1;
+ }
+ gdcm::MediaStorage ms;
+ ms.SetFromFile( reader.GetFile() );
+ if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
+ {
+ std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
+ return 1;
+ }
+
+ gdcm::Anonymizer ano;
+ ano.SetFile( reader.GetFile() );
+ if( removegrouplength )
+ {
+ if( !ano.RemoveGroupLength() )
+ {
+ std::cerr << "Could not remove group length" << std::endl;
+ }
+ }
+ if( removeretired )
+ {
+ if( !ano.RemoveRetired() )
+ {
+ std::cerr << "Could not remove retired tags" << std::endl;
+ }
+ }
+ if( removeprivate )
+ {
+ if( !ano.RemovePrivateTags() )
+ {
+ std::cerr << "Could not remove private tags" << std::endl;
+ }
+ }
+
+ gdcm::Writer writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( ano.GetFile() );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ // Handle here the general file (not required to be image)
+ if ( explicitts || implicit || deflated )
+ {
+ if( explicitts && implicit ) return 1; // guard
+ if( explicitts && deflated ) return 1; // guard
+ if( implicit && deflated ) return 1; // guard
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read: " << filename << std::endl;
+ return 1;
+ }
+ gdcm::MediaStorage ms;
+ ms.SetFromFile( reader.GetFile() );
+ if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
+ {
+ std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
+ return 1;
+ }
+
+ gdcm::Writer writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( reader.GetFile() );
+ gdcm::File & file = writer.GetFile();
+ gdcm::FileMetaInformation &fmi = file.GetHeader();
+
+ const gdcm::TransferSyntax &orits = fmi.GetDataSetTransferSyntax();
+ if( orits != gdcm::TransferSyntax::ExplicitVRLittleEndian
+ && orits != gdcm::TransferSyntax::ImplicitVRLittleEndian
+ && orits != gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian )
+ {
+ std::cerr << "Sorry input Transfer Syntax not supported for this conversion: " << orits << std::endl;
+ return 1;
+ }
+
+ gdcm::TransferSyntax ts = gdcm::TransferSyntax::ImplicitVRLittleEndian;
+ if( explicitts )
+ {
+ ts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
+ }
+ else if( deflated )
+ {
+ ts = gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian;
+ }
+ std::string tsuid = gdcm::TransferSyntax::GetTSString( ts );
+ if( tsuid.size() % 2 == 1 )
+ {
+ tsuid.push_back( 0 ); // 0 padding
+ }
+ gdcm::DataElement de( gdcm::Tag(0x0002,0x0010) );
+ de.SetByteValue( &tsuid[0], (uint32_t)tsuid.size() );
+ de.SetVR( gdcm::Attribute<0x0002, 0x0010>::GetVR() );
+ fmi.Clear();
+ fmi.Replace( de );
+
+ fmi.SetDataSetTransferSyntax(ts);
+
+ if( explicitts || deflated )
+ {
+ gdcm::FileExplicitFilter fef;
+ fef.SetChangePrivateTags( (changeprivatetags > 0 ? true: false));
+ fef.SetFile( reader.GetFile() );
+ if( !fef.Change() )
+ {
+ std::cerr << "Failed to change: " << filename << std::endl;
+ return 1;
+ }
+ }
+
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ // split fragments
+ if( split )
+ {
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read (pixmap): " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::Pixmap &image = reader.GetPixmap();
+
+ gdcm::ImageFragmentSplitter splitter;
+ splitter.SetInput( image );
+ splitter.SetFragmentSizeMax( fragmentsize );
+ splitter.SetForce( (force > 0 ? true: false));
+ bool b = splitter.Split();
+ if( !b )
+ {
+ std::cerr << "Could not split: " << filename << std::endl;
+ return 1;
+ }
+ gdcm::PixmapWriter writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( reader.GetFile() );
+ writer.SetPixmap( splitter.PixmapToPixmapFilter::GetOutput() );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+ else if( photometricinterpretation )
+ {
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read (pixmap): " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::Pixmap &image = reader.GetPixmap();
+
+ // Just in case:
+ if( gdcm::PhotometricInterpretation::GetPIType(photometricinterpretation_str.c_str())
+ == gdcm::PhotometricInterpretation::PI_END )
+ {
+ std::cerr << "Do not handle PhotometricInterpretation: " << photometricinterpretation_str << std::endl;
+ return 1;
+ }
+ gdcm::PhotometricInterpretation pi (
+ gdcm::PhotometricInterpretation::GetPIType(photometricinterpretation_str.c_str()) );
+ gdcm::ImageChangePhotometricInterpretation pifilt;
+ pifilt.SetInput( image );
+ pifilt.SetPhotometricInterpretation( pi );
+ bool b = pifilt.Change();
+ if( !b )
+ {
+ std::cerr << "Could not apply PhotometricInterpretation: " << filename << std::endl;
+ return 1;
+ }
+ gdcm::PixmapWriter writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( reader.GetFile() );
+ writer.SetPixmap( pifilt.PixmapToPixmapFilter::GetOutput() );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+ else if( lut )
+ {
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read (pixmap): " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::Pixmap &image = reader.GetPixmap();
+
+ gdcm::ImageApplyLookupTable lutfilt;
+ lutfilt.SetInput( image );
+ bool b = lutfilt.Apply();
+ if( !b )
+ {
+ std::cerr << "Could not apply LUT: " << filename << std::endl;
+ return 1;
+ }
+ gdcm::PixmapWriter writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( reader.GetFile() );
+ writer.SetPixmap( lutfilt.PixmapToPixmapFilter::GetOutput() );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+ else if( jpeg || j2k || jpegls || rle || raw || force /*|| deflated*/ /*|| planarconf*/ )
+ {
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read (pixmap): " << filename << std::endl;
+ return 1;
+ }
+ gdcm::Pixmap &image = reader.GetPixmap();
+ //const gdcm::IconImage &icon = image.GetIconImage();
+ //if( !icon.IsEmpty() )
+ // {
+ // std::cerr << "Icons are not supported" << std::endl;
+ // return 1;
+ // }
+ if( generateicon )
+ {
+ gdcm::IconImageGenerator iig;
+ iig.SetPixmap( image );
+ const unsigned int idims[2] = { 64, 64 };
+ iig.SetOutputDimensions( idims );
+ if( iconminmax )
+ {
+ iig.SetPixelMinMax( iconmin, iconmax );
+ }
+ iig.AutoPixelMinMax( iconautominmax ? true : false );
+ bool b = iig.Generate();
+ if( !b ) return 1;
+ const gdcm::IconImage &icon = iig.GetIconImage();
+ image.SetIconImage( icon );
+ }
+
+ gdcm::JPEG2000Codec j2kcodec;
+ gdcm::JPEGCodec jpegcodec;
+ gdcm::JPEGLSCodec jpeglscodec;
+ gdcm::ImageChangeTransferSyntax change;
+ change.SetForce( (force > 0 ? true: false));
+ change.SetCompressIconImage( (compressicon > 0 ? true: false));
+ if( jpeg )
+ {
+ if( lossy )
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEGBaselineProcess1 );
+ jpegcodec.SetLossless( false );
+ if( quality )
+ {
+ assert( qualities.size() == 1 );
+ jpegcodec.SetQuality( qualities[0] );
+ }
+ change.SetUserCodec( &jpegcodec );
+ }
+ else
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLosslessProcess14_1 );
+ }
+ }
+ else if( jpegls )
+ {
+ if( lossy )
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLSNearLossless );
+ jpeglscodec.SetLossless( false );
+ if( jpeglserror )
+ {
+ jpeglscodec.SetLossyError( jpeglserror_value );
+ }
+ change.SetUserCodec( &jpeglscodec );
+ }
+ else
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEGLSLossless );
+ }
+ }
+ else if( j2k )
+ {
+ if( lossy )
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEG2000 );
+ if( rate )
+ {
+ int i = 0;
+ for(std::vector::const_iterator it = rates.begin(); it != rates.end(); ++it )
+ {
+ j2kcodec.SetRate(i++, *it );
+ }
+ }
+ if( quality )
+ {
+ int i = 0;
+ for(std::vector::const_iterator it = qualities.begin(); it != qualities.end(); ++it )
+ {
+ j2kcodec.SetQuality( i++, *it );
+ }
+ }
+ if( tile )
+ {
+ j2kcodec.SetTileSize( tilesize[0], tilesize[1] );
+ }
+ if( nres )
+ {
+ j2kcodec.SetNumberOfResolutions( nresvalue );
+ }
+ j2kcodec.SetReversible( !irreversible );
+ change.SetUserCodec( &j2kcodec );
+ }
+ else
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::JPEG2000Lossless );
+ }
+ }
+ else if( raw )
+ {
+ if( lossy )
+ {
+ std::cerr << "no such thing as raw & lossy" << std::endl;
+ return 1;
+ }
+ const gdcm::TransferSyntax &ts = image.GetTransferSyntax();
+#ifdef GDCM_WORDS_BIGENDIAN
+ (void)ts;
+ change.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRBigEndian );
+#else
+ if( ts.IsExplicit() )
+ {
+ change.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
+ }
+ else
+ {
+ assert( ts.IsImplicit() );
+ change.SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
+ }
+#endif
+ }
+ else if( rle )
+ {
+ if( lossy )
+ {
+ std::cerr << "no such thing as rle & lossy" << std::endl;
+ return 1;
+ }
+ change.SetTransferSyntax( gdcm::TransferSyntax::RLELossless );
+ }
+ else if( deflated )
+ {
+ if( lossy )
+ {
+ std::cerr << "no such thing as deflated & lossy" << std::endl;
+ return 1;
+ }
+ change.SetTransferSyntax( gdcm::TransferSyntax::DeflatedExplicitVRLittleEndian );
+ }
+ else if( force )
+ {
+ // If image is encapsulated it will check some attribute (col/row/pi/pf) and
+ // some attributes...
+ }
+ else
+ {
+ std::cerr << "unhandled action" << std::endl;
+ return 1;
+ }
+ if( raw && planarconf )
+ {
+ gdcm::ImageChangePlanarConfiguration icpc;
+ icpc.SetPlanarConfiguration( planarconfval );
+ icpc.SetInput( image );
+ bool b = icpc.Change();
+ if( !b )
+ {
+ std::cerr << "Could not change the Planar Configuration: " << filename << std::endl;
+ return 1;
+ }
+ change.SetInput( icpc.PixmapToPixmapFilter::GetOutput() );
+ }
+ else
+ {
+ change.SetInput( image );
+ }
+ bool b = change.Change();
+ if( !b )
+ {
+ std::cerr << "Could not change the Transfer Syntax: " << filename << std::endl;
+ return 1;
+ }
+ if( lossy )
+ {
+ if(!quiet)
+ PrintLossyWarning();
+ if( !gdcm::derives( reader.GetFile(), change.PixmapToPixmapFilter::GetOutput() ) )
+ {
+ std::cerr << "Failed to derives: " << filename << std::endl;
+ return 1;
+ }
+ }
+ if( usedict /*ts.IsImplicit()*/ )
+ {
+ gdcm::FileExplicitFilter fef;
+ fef.SetChangePrivateTags( (changeprivatetags > 0 ? true : false));
+ fef.SetFile( reader.GetFile() );
+ if(!fef.Change())
+ {
+ std::cerr << "Failed to change: " << filename << std::endl;
+ return 1;
+ }
+ }
+
+ gdcm::PixmapWriter writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetFile( reader.GetFile() );
+ //writer.SetFile( fef.GetFile() );
+
+ gdcm::File & file = writer.GetFile();
+ gdcm::FileMetaInformation &fmi = file.GetHeader();
+ fmi.Remove( gdcm::Tag(0x0002,0x0100) ); // ' ' ' // PrivateInformationCreatorUID
+ fmi.Remove( gdcm::Tag(0x0002,0x0102) ); // ' ' ' // PrivateInformation
+
+ const gdcm::Pixmap &pixout = change.PixmapToPixmapFilter::GetOutput();
+ writer.SetPixmap( pixout );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+
+ }
+ else if( raw && false )
+ {
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read (pixmap): " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::Pixmap &ir = reader.GetPixmap();
+
+ gdcm::Pixmap image( ir );
+ const gdcm::TransferSyntax &ts = ir.GetTransferSyntax();
+ if( ts.IsExplicit() )
+ {
+ image.SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
+ }
+ else
+ {
+ assert( ts.IsImplicit() );
+ image.SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
+ }
+
+/*
+ image.SetNumberOfDimensions( ir.GetNumberOfDimensions() );
+
+ const unsigned int *dims = ir.GetDimensions();
+ image.SetDimension(0, dims[0] );
+ image.SetDimension(1, dims[1] );
+
+ const gdcm::PixelFormat &pixeltype = ir.GetPixelFormat();
+ image.SetPixelFormat( pixeltype );
+
+ const gdcm::PhotometricInterpretation &pi = ir.GetPhotometricInterpretation();
+ image.SetPhotometricInterpretation( pi );
+*/
+
+ unsigned long len = ir.GetBufferLength();
+ //assert( len = ir.GetBufferLength() );
+ std::vector buffer;
+ buffer.resize(len); // black image
+
+ ir.GetBuffer( &buffer[0] );
+ gdcm::ByteValue *bv = new gdcm::ByteValue(buffer);
+ gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
+ pixeldata.SetValue( *bv );
+ image.SetDataElement( pixeldata );
+
+ gdcm::PixmapWriter writer;
+ writer.SetFile( reader.GetFile() );
+ writer.SetPixmap( image );
+ writer.SetFileName( outfilename.c_str() );
+
+ if( !writer.Write() )
+ {
+ std::cerr << "could not write: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+ else
+ {
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ if( ignoreerrors )
+ {
+ std::cerr << "WARNING: an error was found during the reading of your DICOM file." << std::endl;
+ std::cerr << "gdcmconv will still try to continue and rewrite your DICOM file." << std::endl;
+ std::cerr << "There is absolutely no guarantee that your output file will be valid." << std::endl;
+ }
+ else
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+ }
+ gdcm::MediaStorage ms;
+ ms.SetFromFile( reader.GetFile() );
+ if( ms == gdcm::MediaStorage::MediaStorageDirectoryStorage )
+ {
+ std::cerr << "Sorry DICOMDIR is not supported" << std::endl;
+ return 1;
+ }
+
+#if 0
+ // if preamble create:
+ gdcm::File f(reader.GetFile());
+ gdcm::Preamble p;
+ p.Create();
+ f.SetPreamble(p);
+ gdcm::DataSet ds = reader.GetFile().GetDataSet();
+ SetSQToUndefined undef;
+ ds.ExecuteOperation(undef);
+
+ gdcm::File f(reader.GetFile());
+ f.SetDataSet(ds);
+#endif
+
+#if 0
+ gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ gdcm::DataElement de = ds.GetDataElement( gdcm::Tag(0x0010,0x0010) );
+ const char patname[] = "John^Doe";
+ de.SetByteValue(patname, strlen(patname));
+ std::cout << de << std::endl;
+
+ ds.Replace( de );
+ std::cout << ds.GetDataElement( gdcm::Tag(0x0010,0x0010) ) << std::endl;
+#endif
+
+ /*
+ //(0020,0032) DS [-158.135803\-179.035797\-75.699997] # 34, 3 ImagePositionPatient
+ //(0020,0037) DS [1.000000\0.000000\0.000000\0.000000\1.000000\0.000000] # 54, 6 ImageOrientationPatient
+ gdcm::Attribute<0x0020,0x0032> at = { -158.135803, -179.035797, -75.699997 };
+ gdcm::DataElement ipp = at.GetAsDataElement();
+ ds.Remove( at.GetTag() );
+ ds.Remove( ipp.GetTag() );
+ ds.Replace( ipp );
+ */
+
+ gdcm::Writer writer;
+ writer.SetFileName( outfilename.c_str() );
+ writer.SetCheckFileMetaInformation( (checkmeta > 0 ? true : false));
+ //writer.SetFile( f );
+ writer.SetFile( reader.GetFile() );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ // remove file to avoid any temptation
+ if( filename != outfilename )
+ {
+ gdcm::System::RemoveFile( outfilename.c_str() );
+ }
+ else
+ {
+ std::cerr << "gdcmconv just corrupted: " << filename << " for you (data lost)." << std::endl;
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmdictdump.cxx b/gdcm/Applications/Cxx/gdcmdictdump.cxx
new file mode 100644
index 0000000..5923e37
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmdictdump.cxx
@@ -0,0 +1,4 @@
+/*
+* TODO: Is this really usefull ?
+* Dump the dict from a DICOM file. Usefull for the private dict
+*/
diff --git a/gdcm/Applications/Cxx/gdcmdiff.cxx b/gdcm/Applications/Cxx/gdcmdiff.cxx
new file mode 100644
index 0000000..023d5a7
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmdiff.cxx
@@ -0,0 +1,255 @@
+/*=========================================================================
+
+ Program: gdcmdiff for GDCM (Grassroots DICOM)
+
+ Copyright (c) 2011 Andy Buckle
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+#include "gdcmReader.h"
+#include "gdcmAttribute.h"
+#include "gdcmDataSet.h"
+#include "gdcmDicts.h"
+#include "gdcmDict.h"
+#include "gdcmGlobal.h"
+#include "gdcmCSAHeader.h"
+#include "gdcmPrivateTag.h"
+
+#include
+
+static void usage();
+static void difference_of_datasets(const gdcm::DataSet& ds1, const gdcm::DataSet& ds2, int depthSQ);
+static void display_element(std::ostream& os, const gdcm::DataElement& de,
+ const gdcm::DictEntry& dictentry, const char *note, int depthSQ);
+static void underline(int depthSQ);
+static void difference_of_sequences(const gdcm::DataElement& sqde1,
+ const gdcm::DataElement& sqde2, const gdcm::DictEntry& dictentry, int depthSQ);
+
+// previous declaration of 'int truncate(const char*, __off_t)'
+static uint32_t Truncate=30; // trim dumped string values to this number of chars. zero means no trimming.
+static std::stringstream sq_disp; // store SQ output while recursing through: only displayed if a difference is found within SQ
+
+int main( int argc, const char* argv[] )
+{
+ // Check number of args
+ if (3 > argc)
+ {
+ std::cerr << "Must supply the filenames of 2 DICOM files\n" << std::endl;
+ usage();
+ return 1;
+ }
+ // Check last 2 args are readable DICOM files
+ gdcm::Reader reader1, reader2;
+ reader1.SetFileName( argv[argc-2] );
+ reader2.SetFileName( argv[argc-1] );
+ if( !reader1.Read() || !reader2.Read() )
+ {
+ std::cerr << "At least one of the DICOM files could not be read: " <<
+ '\"' << argv[argc-2] << "\", \"" << argv[argc-1] << '\"' << std::endl;
+ return 1;
+ }
+ // Parse other args
+ bool include_meta = false;
+ for(int i=1; i<(argc-2) ;i++)
+ {
+ std::string arg = std::string(argv[i]);
+ if ("-h" == arg || "--help" == arg)
+ {
+ usage();
+ return 0;
+ }
+ else if ("-m" == arg || "--meta" == arg)
+ {
+ include_meta = true;
+ }
+ else if ("-t" == arg || "--truncate" == arg)
+ {
+ Truncate = atoi(argv[++i]);
+ }
+ else
+ {
+ std::cerr << "Warning: command argument not understood: "
+ << arg << std::endl;
+ usage();
+ return 1;
+ }
+ }
+ // Start comparison
+ const gdcm::File &file1 = reader1.GetFile();
+ const gdcm::File &file2 = reader2.GetFile();
+
+ const gdcm::FileMetaInformation &hds1 = file1.GetHeader();
+ const gdcm::FileMetaInformation &hds2 = file2.GetHeader();
+
+ const gdcm::DataSet &ds1 = file1.GetDataSet();
+ const gdcm::DataSet &ds2 = file2.GetDataSet();
+
+ if(include_meta)
+ {
+ difference_of_datasets(hds1, hds2, 0);
+ }
+ difference_of_datasets(ds1, ds2, 0);
+
+ return 0;
+}
+
+static void usage()
+{
+ std::cout <<
+ "Usage: gdcmdiff [OPTIONS] DICOM_FILE1 DICOM_FILE2\n\n"
+ " -h --help (This) help and exit.\n"
+ " -m --meta Compare metainformation. Default is off.\n"
+ " -t --truncate String values trimmed to n characters.\n"
+ " 0 means no trimmming. Default 30." << std::endl;
+}
+
+static void difference_of_datasets(const gdcm::DataSet& ds1, const gdcm::DataSet& ds2, int depthSQ)
+{
+ gdcm::DataSet::ConstIterator it1 = ds1.Begin();
+ gdcm::DataSet::ConstIterator it2 = ds2.Begin();
+ do {
+ // find lowest value tag, being careful not to pick one for an iterator that has finished
+ const gdcm::Tag tag1 = (it1!=ds1.End()) ? it1->GetTag() : gdcm::Tag(0xffff,0xffff);
+ const gdcm::Tag tag2 = (it2!=ds2.End()) ? it2->GetTag() : gdcm::Tag(0xffff,0xffff);
+ gdcm::Tag tag= (tag1GetLength();
+ // error: operands to ?: have different types 'gdcm::VL' and 'uint32_t'
+ uint32_t val_vl = vl;
+ uint32_t trimto = (Truncate > val_vl ) ? val_vl : Truncate;
+ if (0 == Truncate)
+ {
+ trimto = de.GetByteValue()->GetLength();
+ }
+ os << " [" << std::string(de.GetByteValue()->GetPointer(),trimto) << ']';
+ }
+ else
+ {
+ os << " null";
+ }
+ }
+ else
+ {
+ os << " VR unknown";
+ }
+ os << " # " << dictentry.GetName() << std::endl ;
+}
+
+static void underline(int depthSQ)
+{
+ std::cout << std::string(depthSQ, ' '); //indent for SQ
+ std::cout << " -------------" << std::endl;
+}
+
+static void difference_of_sequences(const gdcm::DataElement& sqde1,
+ const gdcm::DataElement& sqde2, const gdcm::DictEntry& dictentry, int depthSQ)
+{
+ gdcm::SmartPointer sqi1 = sqde1.GetValueAsSQ();
+ gdcm::SmartPointer sqi2 = sqde2.GetValueAsSQ();
+ size_t n1 = sqi1->GetNumberOfItems();
+ size_t n2 = sqi2->GetNumberOfItems();
+ size_t n = (n1 < n2) ? n1 : n2 ;
+ std::stringstream sq_note;
+ if (n1 != n2)
+ {
+ sq_note << "[sequence, file 1 has " << n1 << " datasets, file 2 has " << n2
+ << " datasets]";
+ }
+ else
+ {
+ sq_note << "[sequence]";
+ }
+ display_element(sq_disp, sqde1, dictentry, sq_note.str().c_str(),depthSQ);
+ for(size_t i=1; i <= n; i++)
+ {
+ difference_of_datasets( sqi1->GetItem(i).GetNestedDataSet(),
+ sqi2->GetItem(i).GetNestedDataSet(), depthSQ+1);
+ }
+ sq_disp.str(std::string()); // clear stringstream
+}
diff --git a/gdcm/Applications/Cxx/gdcmdump.cxx b/gdcm/Applications/Cxx/gdcmdump.cxx
new file mode 100644
index 0000000..b29664c
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmdump.cxx
@@ -0,0 +1,1620 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * Simple command line tool to dump the layout/values of a DICOM file
+ * This is largely inspired by other tools available from other toolkit, namely:
+ * - dcdump (dicom3tools)
+ * - dcmdump (dcmtk)
+ * - dcmInfo (SIEMENS)
+ * - PrintFile (GDCM 1.x)
+ *
+ * For now all layout are harcoded (see --color/--xml-dict for instance)
+ *
+ * gdcmdump has some feature not described in the DICOM standard:
+ * --csa : to print CSA information (dcmInfo.exe compatible)
+ * --pdb : to print PDB information (GEMS private info)
+ * --elscint : to print ELSCINT information (ELSCINT private info)
+ *
+ *
+ * TODO: it would be nice to have custom printing, namely printing as HTML/XML
+ * it would be nice to have runtime dict (instead of compile time)
+ */
+
+#include "gdcmReader.h"
+#include "gdcmVersion.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmPrivateTag.h"
+#include "gdcmPrinter.h"
+#include "gdcmDumper.h"
+#include "gdcmDictPrinter.h"
+#include "gdcmValidate.h"
+#include "gdcmWriter.h"
+#include "gdcmSystem.h"
+#include "gdcmDirectory.h"
+#include "gdcmCSAHeader.h"
+#include "gdcmPDBHeader.h"
+#include "gdcmSequenceOfItems.h"
+#include "gdcmASN1.h"
+#include "gdcmAttribute.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+static int color = 0;
+
+static int ignoreerrors = 0;
+
+namespace cleanup
+{
+// {"1.3.46.670589.11.0.0.12.2" ,"Philips Private MR Series Data Storage"},
+enum {
+ TYPE_FLOAT = 0, // float
+ TYPE_INT32 = 1, // int32
+ TYPE_STRING = 2, // 80 bytes string (+1)
+ TYPE_UINT32 = 4 // uint32
+};
+
+template
+static void printvaluet(std::istream & is, uint32_t numels)
+{
+ T buffer;
+ for( uint32_t i = 0; i < numels; ++i )
+ {
+ if( i ) std::cout << "\\";
+ is.read( (char*)&buffer, sizeof(T) );
+ std::cout << buffer;
+ }
+}
+
+static void printvalue(std::istream &is, uint32_t type, uint32_t numels, uint32_t pos)
+{
+ assert( numels > 0 );
+ std::streampos start = is.tellg();
+ is.seekg( pos );
+ std::cout << "[";
+ typedef char (string81)[81]; // 80'th byte == 0
+ assert( sizeof( string81 ) == 81 );
+ switch( type )
+ {
+ case TYPE_FLOAT:
+ printvaluet(is, numels);
+ break;
+ case TYPE_INT32:
+ printvaluet(is, numels);
+ break;
+ case TYPE_STRING:
+ printvaluet(is, numels);
+ break;
+ case TYPE_UINT32:
+ printvaluet(is, numels);
+ break;
+ default:
+ assert( 0 );
+ }
+ std::cout << "]";
+ std::cout << " # " << numels;
+ is.seekg( start );
+}
+
+struct PDFElement
+{
+ const char *getname() const { return name; }
+ uint32_t gettype() const { return getvalue(0); }
+ uint32_t getnumelems() const { return getvalue(1); }
+ uint32_t getdummy() const { return getvalue(2); }
+ uint32_t getoffset() const { return getvalue(3); }
+private:
+ char name[50];
+ // type , numel and offset needs to be read starting from the end
+ // the data in between name and those value can contains garbage stuff
+ uint32_t getvalue(int n) const {
+ uint32_t val = 0;
+ memcpy( (char*)&val, name + 50 - 16 + n * 4, sizeof( val ) );
+ return val;
+ }
+};
+
+static void printbinary(std::istream &is, PDFElement const & pdfel )
+{
+ const char *bufferref = pdfel.getname();
+ std::cout << " " << bufferref << " ";
+ uint32_t type = pdfel.gettype();
+ uint32_t numels = pdfel.getnumelems();
+ uint32_t dummy = pdfel.getdummy();
+ assert( dummy == 0 ); (void)dummy;
+ uint32_t offset = pdfel.getoffset();
+ uint32_t pos = (uint32_t)(offset + is.tellg() - 4);
+ printvalue(is, type, numels, pos);
+}
+
+static void ProcessSDSData( std::istream & is )
+{
+ // havent been able to figure out what was the begin meant for
+ is.seekg( 0x20 - 8 );
+ uint32_t version = 0;
+ is.read( (char*)&version, sizeof(version) );
+ assert( version == 8 );
+ uint32_t numel = 0;
+ is.read( (char*)&numel, sizeof(numel) );
+ for( uint32_t el = 0; el < numel; ++el )
+ {
+ PDFElement pdfel;
+ assert( sizeof(pdfel) == 50 );
+ is.read( (char*)&pdfel, 50 );
+ if( *pdfel.getname() )
+ {
+ printbinary( is, pdfel );
+ std::cout << std::endl;
+ }
+ }
+
+}
+// PMS MR Series Data Storage
+static int DumpPMS_MRSDS(const gdcm::DataSet & ds)
+{
+ const gdcm::PrivateTag tdata(0x2005,0x32,"Philips MR Imaging DD 002");
+ if( !ds.FindDataElement( tdata ) ) return 1;
+ const gdcm::DataElement &data = ds.GetDataElement( tdata );
+ gdcm::SmartPointer sqi = data.GetValueAsSQ();
+ if( !sqi ) return 1;
+ std::cout << "PMS Dumping info from tag " << tdata << std::endl;
+ gdcm::SequenceOfItems::ConstIterator it = sqi->Begin();
+ for( ; it != sqi->End(); ++it )
+ {
+ const gdcm::Item & item = *it;
+ const gdcm::DataSet & nestedds = item.GetNestedDataSet();
+ const gdcm::PrivateTag tprotocoldataname(0x2005,0x37,"Philips MR Imaging DD 002");
+ const gdcm::DataElement & protocoldataname = nestedds.GetDataElement( tprotocoldataname );
+ const gdcm::ByteValue *bv1 = protocoldataname.GetByteValue();
+ const gdcm::PrivateTag tprotocoldatatype(0x2005,0x39,"Philips MR Imaging DD 002");
+ const gdcm::DataElement & protocoldatatype = nestedds.GetDataElement( tprotocoldatatype );
+ const gdcm::ByteValue *bv2 = protocoldatatype.GetByteValue();
+ const gdcm::PrivateTag tprotocoldatablock(0x2005,0x44,"Philips MR Imaging DD 002");
+ const gdcm::DataElement & protocoldatablock = nestedds.GetDataElement( tprotocoldatablock );
+ const gdcm::ByteValue *bv3 = protocoldatablock.GetByteValue();
+ const gdcm::PrivateTag tprotocoldatabool(0x2005,0x47,"Philips MR Imaging DD 002");
+ const gdcm::DataElement & protocoldatabool = nestedds.GetDataElement( tprotocoldatabool );
+ const gdcm::ByteValue *bv4 = protocoldatabool.GetByteValue();
+ std::string s1;
+ if( bv1 )
+ {
+ s1 = std::string( bv1->GetPointer(), bv1->GetLength() );
+ }
+ std::string s2;
+ if( bv2 )
+ {
+ s2 = std::string( bv2->GetPointer(), bv2->GetLength() );
+ }
+ std::string s3;
+ if( bv3 )
+ {
+ s3 = std::string( bv3->GetPointer(), bv3->GetLength() );
+ }
+ std::string s4;
+ if( bv4 )
+ {
+ s4 = std::string( bv4->GetPointer(), bv4->GetLength() );
+ }
+ std::istringstream is( s3 );
+ std::cout << "PMS/Item name: [" << s1 << "/" << s2 << "/" << s4 << "]" << std::endl;
+ ProcessSDSData( is );
+ }
+ return 0;
+}
+
+static int DumpTOSHIBA_MEC_CT3(const gdcm::DataSet & ds)
+{
+ const gdcm::PrivateTag tdata(0x7005,0x10,"TOSHIBA_MEC_CT3");
+ if( !ds.FindDataElement( tdata ) ) return 1;
+ const gdcm::DataElement &data = ds.GetDataElement( tdata );
+
+ const gdcm::ByteValue *bv = data.GetByteValue();
+ if( !bv ) return 1;
+
+ const int offset = 24;
+ if( bv->GetLength() < offset )
+ {
+ std::cerr << "Not enough header" << std::endl;
+ return 1;
+ }
+ std::istringstream is0;
+ const std::string str0 = std::string( bv->GetPointer(), offset );
+ is0.str( str0 );
+ gdcm::ImplicitDataElement ide0;
+ ide0.Read(is0);
+ gdcm::ImplicitDataElement ide1;
+ ide1.Read(is0);
+
+ gdcm::Attribute<0x0,0x0> at0;
+ at0.SetFromDataElement( ide0 );
+ if( at0.GetValue() != 12 )
+ {
+ std::cerr << "Bogus header value #0" << std::endl;
+ return 1;
+ }
+ gdcm::Attribute<0x0,0x1> at1;
+ at1.SetFromDataElement( ide1 );
+
+ const unsigned int dlen = bv->GetLength() - offset;
+ if( at1.GetValue() != dlen )
+ {
+ std::cerr << "Bogus header value #1" << std::endl;
+ return 1;
+ }
+ std::istringstream is1;
+ const std::string str1 = std::string( bv->GetPointer() + offset, bv->GetLength() - offset);
+ is1.str( str1 );
+
+ gdcm::Reader r;
+ r.SetStream( is1 );
+ if( !r.Read() )
+ {
+ std::cerr << "Could not read CT Private Data 2" << std::endl;
+ return 1;
+ }
+
+ gdcm::Printer printer;
+ printer.SetFile ( r.GetFile() );
+ printer.SetColor( color != 0 );
+ printer.Print( std::cout );
+
+ return 0;
+}
+
+// VEPRO
+/*
+[VIMDATA2]
+PrivateCreator = VEPRO VIM 5.0 DATA
+Group = 0x0055
+Element = 0x0020
+Data.ID = C|0|3
+Data.Version = C|3|3
+Data.UserName = C|6|32
+Data.UserAdress1 = C|38|32
+Data.UserAdress2 = C|70|32
+Data.UserAdress3 = C|102|32
+Data.UserAdress4 = C|134|32
+Data.UserAdress5 = C|166|32
+Data.RecDate = C|198|8
+Data.RecTime = C|206|6
+Data.RecPlace = C|212|64
+Data.RecSource = C|276|64
+Data.DF1 = C|340|64
+Data.DF2 = C|404|64
+Data.DF3 = C|468|64
+Data.DF4 = C|532|64
+Data.DF5 = C|596|64
+Data.DF6 = C|660|64
+Data.DF7 = C|724|64
+Data.DF8 = C|788|64
+Data.DF9 = C|852|64
+Data.DF10 = C|916|64
+Data.DF11 = C|980|64
+Data.DF12 = C|1044|64
+Data.DF13 = C|1108|64
+Data.DF14 = C|1172|64
+Data.DF15 = C|1236|64
+Data.DF16 = C|1300|64
+Data.DF17 = C|1364|64
+Data.DF18 = C|1428|64
+Data.DF19 = C|1492|64
+Data.DF20 = C|1556|64
+Data.StudyUID = C|1642|64
+Data.SeriesUID = C|1706|64
+Data.Modality = C|1770|16
+*/
+// TYPE[C/I] / OFFSET / LENGTH (in bytes)
+struct Data2
+{
+ char ID[3]; // Data.ID = C|0|3
+ char Version[3]; // Data.Version = C|3|3
+ char UserName[32]; // Data.UserName = C|6|32
+ char UserAdress1[32]; // Data.UserAdress1 = C|38|32
+ char UserAdress2[32]; // Data.UserAdress2 = C|70|32
+ char UserAdress3[32]; // Data.UserAdress3 = C|102|32
+ char UserAdress4[32]; // Data.UserAdress4 = C|134|32
+ char UserAdress5[32]; // Data.UserAdress5 = C|166|32
+ char RecDate[8]; // Data.RecDate = C|198|8
+ char RecTime[6]; // Data.RecTime = C|206|6
+ char RecPlace[64]; // Data.RecPlace = C|212|64
+ char RecSource[64];// Data.RecSource = C|276|64
+ char DF1[64]; // Data.DF1 = C|340|64
+ char DF2[64]; // Data.DF2 = C|404|64
+ char DF3[64]; // Data.DF3 = C|468|64
+ char DF4[64]; // Data.DF4 = C|532|64
+ char DF5[64]; // Data.DF5 = C|596|64
+ char DF6[64]; // Data.DF6 = C|660|64
+ char DF7[64]; // Data.DF7 = C|724|64
+ char DF8[64]; // Data.DF8 = C|788|64
+ char DF9[64]; // Data.DF9 = C|852|64
+ char DF10[64]; // Data.DF10 = C|916|64
+ char DF11[64]; // Data.DF11 = C|980|64
+ char DF12[64]; // Data.DF12 = C|1044|64
+ char DF13[64]; // Data.DF13 = C|1108|64
+ char DF14[64]; // Data.DF14 = C|1172|64
+ char DF15[64]; // Data.DF15 = C|1236|64
+ char DF16[64]; // Data.DF16 = C|1300|64
+ char DF17[64]; // Data.DF17 = C|1364|64
+ char DF18[64]; // Data.DF18 = C|1428|64
+ char DF19[64]; // Data.DF19 = C|1492|64
+ char DF20[64]; // Data.DF20 = C|1556|64
+ char Padding[22]; // ?????
+ char StudyUID[64]; // Data.StudyUID = C|1642|64
+ char SeriesUID[64]; // Data.SeriesUID = C|1706|64
+ char Modality[16]; // Data.Modality = C|1770|16
+
+ void Print( std::ostream &os )
+ {
+ os << " ID: " << std::string(ID,3) << "\n";
+ os << " Version: " << std::string(Version,3) << "\n";
+ os << " UserName: " << std::string(UserName,32) << "\n";
+ os << " UserAdress1: " << std::string(UserAdress1,32) << "\n";
+ os << " UserAdress2: " << std::string(UserAdress2,32) << "\n";
+ os << " UserAdress3: " << std::string(UserAdress3,32) << "\n";
+ os << " UserAdress4: " << std::string(UserAdress4,32) << "\n";
+ os << " UserAdress5: " << std::string(UserAdress5,32) << "\n";
+ os << " RecDate: " << std::string(RecDate,8) << "\n";
+ os << " RecTime: " << std::string(RecTime,64) << "\n";
+ os << " RecPlace: " << std::string(RecPlace,64) << "\n";
+ os << " RecSource: " << std::string(RecSource,64) << "\n";
+ os << " DF1: " << std::string(DF1,64) << "\n";
+ os << " DF2: " << std::string(DF2,64) << "\n";
+ os << " DF3: " << std::string(DF3,64) << "\n";
+ os << " DF4: " << std::string(DF4,64) << "\n";
+ os << " DF5: " << std::string(DF5,64) << "\n";
+ os << " DF6: " << std::string(DF6,64) << "\n";
+ os << " DF7: " << std::string(DF7,64) << "\n";
+ os << " DF8: " << std::string(DF8,64) << "\n";
+ os << " DF9: " << std::string(DF9,64) << "\n";
+ os << " DF10: " << std::string(DF10,64) << "\n";
+ os << " DF11: " << std::string(DF11,64) << "\n";
+ os << " DF12: " << std::string(DF12,64) << "\n";
+ os << " DF13: " << std::string(DF13,64) << "\n";
+ os << " DF14: " << std::string(DF14,64) << "\n";
+ os << " DF15: " << std::string(DF15,64) << "\n";
+ os << " DF16: " << std::string(DF16,64) << "\n";
+ os << " DF17: " << std::string(DF17,64) << "\n";
+ os << " DF18: " << std::string(DF18,64) << "\n";
+ os << " DF19: " << std::string(DF19,64) << "\n";
+ os << " DF20: " << std::string(DF20,64) << "\n";
+ //os << " Padding: " << std::string(Padding,22) << "\n";
+ os << " StudyUID: " << std::string(StudyUID,64) << "\n";
+ os << " SeriesUID: " << std::string(SeriesUID,64) << "\n";
+ os << " Modality: " << std::string(Modality,16) << "\n";
+ }
+};
+
+static bool ProcessData( const char *buf, size_t len )
+{
+ Data2 data2;
+ const size_t s = sizeof(data2);
+ assert( len >= s); (void)len;
+ // VIMDATA2 is generally 2048 bytes, while s = 1786
+ // the end is filled with \0 bytes
+ memcpy(&data2, buf, s);
+
+ data2.Print( std::cout );
+ return true;
+}
+
+static int DumpVEPRO(const gdcm::DataSet & ds)
+{
+ // 01f7,1026
+ const gdcm::ByteValue *bv2 = NULL;
+ const gdcm::PrivateTag tdata1(0x55,0x0020,"VEPRO VIF 3.0 DATA");
+ const gdcm::PrivateTag tdata2(0x55,0x0020,"VEPRO VIM 5.0 DATA");
+ // Prefer VIF over VIM ?
+ if( ds.FindDataElement( tdata1 ) )
+ {
+ std::cout << "VIF DATA: " << tdata1 << "\n";
+ const gdcm::DataElement &data = ds.GetDataElement( tdata1 );
+ bv2 = data.GetByteValue();
+ }
+ else if( ds.FindDataElement( tdata2 ) )
+ {
+ std::cout << "VIMDATA2: " << tdata2 << "\n";
+ const gdcm::DataElement &data = ds.GetDataElement( tdata2 );
+ bv2 = data.GetByteValue();
+ }
+
+ if( bv2 )
+ {
+ ProcessData( bv2->GetPointer(), bv2->GetLength() );
+ return 0;
+ }
+
+ return 1;
+}
+
+// ELSCINT1
+static bool readastring(std::string &out, const char *input )
+{
+ out.clear();
+ while( *input )
+ {
+ out.push_back( *input++ );
+ }
+ return true;
+}
+
+struct el
+{
+ std::string name;
+ uint32_t pad;
+ std::vector values;
+ size_t Size() const
+ {
+ size_t s = 0;
+ s += name.size() + 1;
+ s += sizeof(pad);
+ for( std::vector::const_iterator it = values.begin(); it != values.end(); ++it )
+ s += it->size() + 1;
+ return s;
+ }
+ void ReadFromString( const char * input )
+ {
+ readastring( name, input );
+ const char *p = input + 1+ name.size();
+ memcpy( &pad, p, sizeof( pad ) );
+ //assert( pad == 1 || pad == 2 || pad == 3 || pad == 6 );
+ values.resize( pad );
+ const char *pp = p + sizeof(uint32_t);
+ for( uint32_t pidx = 0; pidx < pad; ++pidx )
+ {
+ readastring( values[pidx], pp );
+ pp = pp + values[pidx].size() + 1;
+ }
+ }
+ void Print() const
+ {
+ //std::cout << " " << name << " : " << pad << " : (";
+ std::cout << " " << name << " [";
+ {
+ std::vector::const_iterator it = values.begin();
+ std::cout << *it++;
+ for(; it != values.end(); ++it )
+ {
+ std::cout << "\\";
+ std::cout << *it;
+ }
+ }
+ std::cout << "]" << std::endl;
+ }
+};
+
+
+struct info
+{
+ size_t Read(const char *in )
+ {
+ const char *m = in;
+ uint32_t h;
+ memcpy( &h, in, sizeof(h) );
+ in += sizeof(h);
+ std::string dummy;
+ readastring( dummy, in );
+ in += dummy.size();
+ in += 1;
+ if( h == 432154 ) // 0x6981a
+ {
+ // Single item
+ uint32_t nels;
+ memcpy( &nels, in, sizeof(nels) );
+ in += sizeof(nels);
+ //std::cout << " ELSCINT1/Item name: " << dummy << " : " << nels << std::endl;
+ std::cout << "ELSCINT1/Item name: [" << dummy << "]" << std::endl;
+ for( uint32_t i = 0; i < nels; ++i )
+ {
+ el e;
+ e.ReadFromString( in );
+ e.Print();
+ in += e.Size();
+ }
+ }
+ else if( h == 2341 ) // 0x925
+ {
+ // Multiple Item(s)
+ uint32_t d;
+ memcpy( &d, in, sizeof(d) );
+ in += sizeof(d);
+ //std::cout << " Info Name: " << dummy << " : " << d << std::endl;
+ std::cout << "ELSCINT1/Item name: " << dummy << std::endl;
+ for( uint32_t dix = 0; dix < d; ++dix )
+ {
+ uint32_t fixme;
+ memcpy( &fixme, in, sizeof(fixme) );
+ in += 4; //
+ //std::cout << " number of Subitems " << fixme << std::endl;
+ uint32_t nels = fixme;
+ if( nels )
+ std::cout << " SubItems #" << dix << std::endl;
+ else
+ std::cout << " No SubItems (Empty)" << std::endl;
+ for( uint32_t i = 0; i < nels; ++i )
+ {
+ el e;
+ e.ReadFromString( in );
+ e.Print();
+ in += e.Size();
+ }
+ }
+ // postcondition
+ uint32_t fixme;
+ memcpy( &fixme, in, sizeof(fixme) );
+ assert( fixme == 0x0006981A );
+ }
+ else
+ {
+ assert( 0 );
+ }
+ return in - m;
+ }
+};
+
+static int DumpEl2_new(const gdcm::DataSet & ds)
+{
+ // 01f7,1026
+ const gdcm::PrivateTag t01f7_26(0x01f7,0x1026,"ELSCINT1");
+ if( !ds.FindDataElement( t01f7_26 ) ) return 1;
+ const gdcm::DataElement& de01f7_26 = ds.GetDataElement( t01f7_26 );
+ if ( de01f7_26.IsEmpty() ) return 1;
+ const gdcm::ByteValue * bv = de01f7_26.GetByteValue();
+
+ const char *begin = bv->GetPointer();
+ uint32_t val0[3];
+ memcpy( &val0, begin, sizeof( val0 ) );
+ assert( val0[0] == 0xF22D );
+ begin += sizeof( val0 );
+
+ // 1A 98 06 00 -> start element
+ // Next is a string (can be NULL)
+ // then number of (nested) elements
+
+ std::cout << "ELSCINT1 Dumping info from tag " << t01f7_26 << std::endl;
+ info i;
+ size_t o;
+ assert( val0[1] == 0x1 );
+ for( uint32_t idx = 0; idx < val0[2]; ++idx )
+ {
+ o = i.Read( begin );
+ std::cout << std::endl;
+ begin += o;
+ }
+
+ return 0;
+}
+
+} // end namespace cleanup
+
+template
+static int DoOperation(const std::string & filename)
+{
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ bool success = reader.Read();
+ if( !success && !ignoreerrors )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ TPrinter printer;
+ printer.SetFile ( reader.GetFile() );
+ printer.SetColor( color != 0);
+ printer.Print( std::cout );
+
+ // Only return success when file read succeeded not depending whether or not we printed it
+ return success ? 0 : 1;
+}
+
+static int PrintASN1(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ gdcm::Tag tencryptedattributessequence(0x0400,0x0500);
+ if( !ds.FindDataElement( tencryptedattributessequence ) )
+ {
+ return 1;
+ }
+ const gdcm::DataElement &encryptedattributessequence = ds.GetDataElement( tencryptedattributessequence );
+ //const gdcm::SequenceOfItems * sqi = encryptedattributessequence.GetSequenceOfItems();
+ gdcm::SmartPointer sqi = encryptedattributessequence.GetValueAsSQ();
+ if( !sqi->GetNumberOfItems() )
+ {
+ return 1;
+ }
+ const gdcm::Item &item1 = sqi->GetItem(1);
+ const gdcm::DataSet &subds = item1.GetNestedDataSet();
+
+ gdcm::Tag tencryptedcontent(0x0400,0x0520);
+ if( !subds.FindDataElement( tencryptedcontent) )
+ {
+ return 1;
+ }
+ const gdcm::DataElement &encryptedcontent = subds.GetDataElement( tencryptedcontent );
+ const gdcm::ByteValue *bv = encryptedcontent.GetByteValue();
+
+ bool b = gdcm::ASN1::ParseDump( bv->GetPointer(), bv->GetLength() );
+ if( !b ) return 1;
+ return 0;
+}
+
+static int PrintELSCINT(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ int ret = cleanup::DumpEl2_new( ds );
+
+ return ret;
+}
+
+static int PrintVEPRO(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ int ret = cleanup::DumpVEPRO( ds );
+
+ return ret;
+}
+
+static int PrintSDS(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ int ret = cleanup::DumpPMS_MRSDS( ds );
+
+ return ret;
+}
+
+static int PrintCT3(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+ int ret = cleanup::DumpTOSHIBA_MEC_CT3( ds );
+
+ return ret;
+}
+
+static int PrintPDB(const std::string & filename, bool verbose)
+{
+ (void)verbose;
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ gdcm::PDBHeader pdb;
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+
+ const gdcm::PrivateTag &t1 = pdb.GetPDBInfoTag();
+
+ bool found = false;
+ int ret = 0;
+ if( ds.FindDataElement( t1 ) )
+ {
+ pdb.LoadFromDataElement( ds.GetDataElement( t1 ) );
+ pdb.Print( std::cout );
+ found = true;
+ }
+ if( !found )
+ {
+ std::cout << "no pdb tag found" << std::endl;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int PrintCSA(const std::string & filename)
+{
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ gdcm::CSAHeader csa;
+ const gdcm::DataSet& ds = reader.GetFile().GetDataSet();
+
+ const gdcm::PrivateTag &t1 = csa.GetCSAImageHeaderInfoTag();
+ const gdcm::PrivateTag &t2 = csa.GetCSASeriesHeaderInfoTag();
+ const gdcm::PrivateTag &t3 = csa.GetCSADataInfo();
+
+ bool found = false;
+ int ret = 0;
+ if( ds.FindDataElement( t1 ) )
+ {
+ csa.LoadFromDataElement( ds.GetDataElement( t1 ) );
+ csa.Print( std::cout );
+ found = true;
+ if( csa.GetFormat() == gdcm::CSAHeader::ZEROED_OUT )
+ {
+ std::cout << "CSA Header has been zero-out (contains only 0)" << std::endl;
+ ret = 1;
+ }
+ else if( csa.GetFormat() == gdcm::CSAHeader::DATASET_FORMAT )
+ {
+ gdcm::Printer p;
+ gdcm::File f;
+ f.SetDataSet( csa.GetDataSet() );
+ p.SetFile( f );
+ p.Print( std::cout );
+ }
+ }
+ if( ds.FindDataElement( t2 ) )
+ {
+ csa.LoadFromDataElement( ds.GetDataElement( t2 ) );
+ csa.Print( std::cout );
+ found = true;
+ if( csa.GetFormat() == gdcm::CSAHeader::ZEROED_OUT )
+ {
+ std::cout << "CSA Header has been zero-out (contains only 0)" << std::endl;
+ ret = 1;
+ }
+ else if( csa.GetFormat() == gdcm::CSAHeader::DATASET_FORMAT )
+ {
+ gdcm::Printer p;
+ gdcm::File f;
+ f.SetDataSet( csa.GetDataSet() );
+ p.SetFile( f );
+ p.Print( std::cout );
+ }
+ }
+ if( ds.FindDataElement( t3 ) )
+ {
+ csa.LoadFromDataElement( ds.GetDataElement( t3 ) );
+ csa.Print( std::cout );
+ found = true;
+ if( csa.GetFormat() == gdcm::CSAHeader::ZEROED_OUT )
+ {
+ std::cout << "CSA Header has been zero-out (contains only 0)" << std::endl;
+ ret = 1;
+ }
+ else if( csa.GetFormat() == gdcm::CSAHeader::INTERFILE )
+ {
+ const char *interfile = csa.GetInterfile();
+ if( interfile ) std::cout << interfile << std::endl;
+ }
+ else if( csa.GetFormat() == gdcm::CSAHeader::DATASET_FORMAT )
+ {
+ gdcm::Printer p;
+ gdcm::File f;
+ f.SetDataSet( csa.GetDataSet() );
+ p.SetFile( f );
+ p.Print( std::cout );
+ }
+ }
+ if( !found )
+ {
+ std::cout << "no csa tag found" << std::endl;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcmdump: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmdump [OPTION]... FILE..." << std::endl;
+ std::cout << "dumps a DICOM file, it will display the structure and values contained in the specified DICOM file\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input DICOM filename or directory" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -x --xml-dict generate the XML dict (only private elements for now)." << std::endl;
+ std::cout << " -r --recursive recursive." << std::endl;
+ std::cout << " -d --dump dump value (limited use)." << std::endl;
+ std::cout << " -p --print print value instead of simply dumping (default)." << std::endl;
+ std::cout << " -c --color print in color." << std::endl;
+ std::cout << " -C --csa print SIEMENS CSA Header (0029,[12]0,SIEMENS CSA HEADER)." << std::endl;
+ std::cout << " -P --pdb print GEMS Protocol Data Block (0025,1b,GEMS_SERS_01)." << std::endl;
+ std::cout << " --elscint print ELSCINT Protocol Information (01f7,26,ELSCINT1)." << std::endl;
+ std::cout << " --vepro print VEPRO Protocol Information (0055,20,VEPRO VIF 3.0 DATA)." << std::endl;
+ std::cout << " or VEPRO Protocol Information (0055,20,VEPRO VIM 5.0 DATA)." << std::endl;
+ std::cout << " --sds print Philips MR Series Data Storage (1.3.46.670589.11.0.0.12.2) Information (2005,32,Philips MR Imaging DD 002)." << std::endl;
+ std::cout << " --ct3 print CT Private Data 2 (7005,10,TOSHIBA_MEC_CT3)." << std::endl;
+ std::cout << " -A --asn1 print encapsulated ASN1 structure >(0400,0520)." << std::endl;
+ std::cout << " --map-uid-names map UID to names." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Special Options:" << std::endl;
+ std::cout << " -I --ignore-errors print even if file is corrupted." << std::endl;
+}
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string filename;
+ int printdict = 0;
+ int dump = 0;
+ int print = 0;
+ int printcsa = 0;
+ int printpdb = 0;
+ int printelscint = 0;
+ int printvepro = 0;
+ int printsds = 0; // MR Series Data Storage
+ int printct3 = 0; // TOSHIBA_MEC_CT3
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ int recursive = 0;
+ int printasn1 = 0;
+ int mapuidnames = 0;
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+/*
+ struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+*/
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"xml-dict", 0, &printdict, 1},
+ {"recursive", 0, &recursive, 1},
+ {"print", 0, &print, 1},
+ {"dump", 0, &dump, 1},
+ {"color", 0, &color, 1},
+ {"csa", 0, &printcsa, 1},
+ {"pdb", 0, &printpdb, 1},
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {"ignore-errors", 0, &ignoreerrors, 1},
+ {"asn1", 0, &printasn1, 1},
+ {"map-uid-names", 0, &mapuidnames, 1},
+ {"elscint", 0, &printelscint, 1},
+ {"vepro", 0, &printvepro, 1},
+ {"sds", 0, &printsds, 1},
+ {"ct3", 0, &printct3, 1},
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:xrpdcCPAVWDEhvI";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'x':
+ //printf ("option d with value '%s'\n", optarg);
+ printdict = 1;
+ break;
+
+ case 'r':
+ recursive = 1;
+ break;
+
+ case 'p':
+ //printf ("option p with value '%s'\n", optarg);
+ print = 1;
+ break;
+
+ case 'd':
+ dump = 1;
+ break;
+
+ case 'c':
+ color = 1;
+ break;
+
+ case 'C':
+ printcsa = 1;
+ break;
+
+ case 'A':
+ printasn1 = 1;
+ break;
+
+ case 'P':
+ printpdb = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case 'I':
+ ignoreerrors = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: %d", optind );
+ //while (optind < argc)
+ // {
+ // printf ("%s\n", argv[optind++]);
+ // }
+ //printf ("\n");
+ // Ok there is only one arg, easy, it's the filename:
+ int v = argc - optind;
+ if( v == 1 )
+ {
+ filename = argv[optind];
+ }
+ }
+
+ //
+ //gdcm::System::SetArgv0( argv[0] );
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ // check if d or p are passed, only one at a time
+ if( print || printdict )
+ {
+ if ( print && printdict )
+ {
+ std::cerr << "d or p" << std::endl;
+ return 1;
+ }
+ }
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( debug != 0);
+ gdcm::Trace::SetWarning( warning != 0);
+ gdcm::Trace::SetError( error != 0);
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( verbose != 0);
+ gdcm::Trace::SetError( verbose!= 0);
+ }
+
+ if( mapuidnames )
+ {
+ std::cerr << "Not handled for now" << std::endl;
+ }
+
+ // else
+ int res = 0;
+ if( !gdcm::System::FileExists(filename.c_str()) )
+ {
+ std::cerr << "no such file: " << filename << std::endl;
+ return 1;
+ }
+ else if( gdcm::System::FileIsDirectory( filename.c_str() ) )
+ {
+ gdcm::Directory d;
+ d.Load(filename, recursive!= 0);
+ gdcm::Directory::FilenamesType const &filenames = d.GetFilenames();
+ for( gdcm::Directory::FilenamesType::const_iterator it = filenames.begin(); it != filenames.end(); ++it )
+ {
+ if( printdict )
+ {
+ res += DoOperation(*it);
+ }
+ else if( printasn1 )
+ {
+ res += PrintASN1(*it, verbose!= 0);
+ }
+ else if( printvepro )
+ {
+ res += PrintVEPRO(*it, verbose!= 0);
+ }
+ else if( printsds )
+ {
+ res += PrintSDS(*it, verbose!= 0);
+ }
+ else if( printct3 )
+ {
+ res += PrintCT3(*it, verbose!= 0);
+ }
+ else if( printelscint )
+ {
+ res += PrintELSCINT(*it, verbose!= 0);
+ }
+ else if( printpdb )
+ {
+ res += PrintPDB(*it, verbose!= 0);
+ }
+ else if( printcsa )
+ {
+ res += PrintCSA(*it);
+ }
+ else if( dump )
+ {
+ res += DoOperation(*it);
+ }
+ else
+ {
+ res += DoOperation(*it);
+ }
+ if( verbose ) std::cerr << *it << std::endl;
+ }
+ if( verbose ) std::cerr << "Total: " << filenames.size() << " files were processed" << std::endl;
+ }
+ else
+ {
+ assert( gdcm::System::FileExists(filename.c_str()) );
+ if( printdict )
+ {
+ res += DoOperation(filename);
+ }
+ else if( printasn1 )
+ {
+ res += PrintASN1(filename, verbose!= 0);
+ }
+ else if( printvepro )
+ {
+ res += PrintVEPRO(filename, verbose!= 0);
+ }
+ else if( printsds )
+ {
+ res += PrintSDS(filename, verbose!= 0);
+ }
+ else if( printct3 )
+ {
+ res += PrintCT3(filename, verbose!= 0);
+ }
+ else if( printelscint )
+ {
+ res += PrintELSCINT(filename, verbose!= 0);
+ }
+ else if( printpdb )
+ {
+ res += PrintPDB(filename, verbose!= 0);
+ }
+ else if( printcsa )
+ {
+ res += PrintCSA(filename);
+ }
+ else if( dump )
+ {
+ res += DoOperation(filename);
+ }
+ else
+ {
+ res += DoOperation(filename);
+ }
+ // ...
+ if ( verbose )
+ std::cerr << "Filename: " << filename << std::endl;
+ }
+
+ return res;
+}
+
+/*
+ * Harvested data:
+ * A lot of them are still non-obvious
+
+Most obvious ones:
+ETL -> Echo Train Length
+FLIPANG -> Flip Angle
+MATRIXX / MATRIXY -> Acquisition Matrix
+SLTHICK -> Slice Thickness
+
+ ENTRY "Feet First"
++ POSITION "Supine"
+--------------------
+= Patient Position
+
+
+Full list:
+
+ANREF "IC"
+ANREF "NA"
+ANREF "SN"
+AUTOCF "Water"
+AUTOSCIC "0"
+AUTOSCIC "2"
+AUTOSHIM "Auto"
+AUTOSHIM "Off"
+AUTOSHIM "Yes"
+AUTOSUBOPTIONS "0"
+AUTOTRGTYPE "0"
+AUTOTRIGWIN "0"
+AUTOVOICE "0"
+B4PAUSE "0"
+BPMMODE "0"
+BWRT "0"
+BWRT "-1"
+CLOC1 "0.0"
+CLOC1 "L4.7"
+CLOC1 "L5.9"
+CLOC2 "0.0"
+CLOC2 "P20.0"
+CLOC2 "P42.2"
+CLOC2 "P44.5"
+CLOC3 "0.0"
+CLOC3 "S7.0"
+CLOC3 "S8.2"
+COIL "5GP"
+COIL "8HRBRAIN"
+COIL "HEAD"
+COIL "LOOP2CM"
+CONTAG "GAD"
+CONTAM "10 GAD"
+CONTAM "No "
+CONTAM "Yes "
+CONTRAST "No"
+CONTRAST "Yes"
+DELACQ "Minimum"
+DUMACQ "0"
+ELOC1 "L12.4"
+ELOC1 "L142.9"
+ELOC1 "L1.6"
+ELOC1 "L2.2"
+ELOC1 "L4.9"
+ELOC1 "L5.9"
+ELOC1 "L80.1"
+ELOC1 "L84.1"
+ELOC1 "L99.3"
+ELOC1 "S65.4"
+ELOC1 "S66.5"
+ELOC1 "S89.0"
+ELOC2 "0.0"
+ELOC2 "A18.3"
+ELOC2 "A43.5"
+ELOC2 "A79.2"
+ELOC2 "A87.6"
+ELOC2 "L6.9"
+ELOC2 "P27.4"
+ELOC2 "P38.8"
+ELOC2 "P48.8"
+ELOC2 "P49.4"
+ELOC3 "A12.8"
+ELOC3 "I111.9"
+ELOC3 "I27.7"
+ELOC3 "I7.1"
+ELOC3 "P21.2"
+ELOC3 "P31.1"
+ELOC3 "S12.3"
+ELOC3 "S1.7"
+ELOC3 "S31.8"
+ELOC3 "S5.4"
+ELOC3 "S7.0"
+ELOC3 "S9.8"
+ENTRY "Head First"
+ETL "17"
+ETL "2"
+ETL "24"
+ETL "3"
+ETL "6"
+ETL "8"
+ETL "9"
+FILTCHOICE "None"
+FLDIR "Slice"
+FLIPANG "12"
+FLIPANG "17"
+FLIPANG "20"
+FLIPANG "36"
+FLIPANG "8"
+FLIPANG "90"
+FOV "12"
+FOV "14"
+FOV "24"
+FOV "24.0"
+FOV "3"
+FOV "30"
+FOV "4"
+FOV "6"
+FOV "8"
+FOVCNT1 "0.0"
+FOVCNT2 "0.0"
+FOVCNT2 "P21.2"
+FOVCNT2 "P31.1"
+GRADMODE "WHOLE"
+GRADMODE "ZOOM"
+GRIP_NUMPSCVOL "0"
+GRIP_NUMSLGROUPS "0"
+GRIP_NUMSLGROUPS "1"
+GRIP_PSCVOL1 "0"
+GRIP_PSCVOL2 "0"
+GRIP_PSCVOLFOV "0"
+GRIP_PSCVOLFOV "0.000000"
+GRIP_PSCVOLTHICK "0"
+GRIP_PSCVOLTHICK "0.000000"
+GRIP_SATGROUP1 "0"
+GRIP_SATGROUP2 "0"
+GRIP_SATGROUP3 "0"
+GRIP_SATGROUP4 "0"
+GRIP_SATGROUP5 "0"
+GRIP_SATGROUP6 "0"
+GRIP_SLGROUP1 "0.000000 -21.170785 -13.463666 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 -1.000000 0.000000 1 0.000000 1 0"
+GRIP_SLGROUP1 "0.000000 -31.122005 2.926577 0.000000 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 0.000000 0.000000 26 0.000000 1 0"
+GRIP_SLGROUP1 "-13.163267 0.000000 25.592358 0.005670 0.000000 -0.999984 0.999984 0.000000 0.005670 0.000000 -1.000000 0.000000 56 0.000000 1 0 1"
+GRIP_SLGROUP1 "3.135807 14.667716 -32.340976 -0.997518 0.043626 0.055276 -0.056814 -0.962372 -0.265728 0.041603 -0.268209 0.962462 1 0.000000 1 0 1"
+GRIP_SPECTRO "0"
+GRIP_TRACKER "0"
+GRXOPT "0"
+GRXOPT "2"
+IEC_ACCEPT "ON"
+IMODE "2D"
+IMODE "3D"
+INITSTATE "0"
+IOPT "EDR, Fast, IrP"
+IOPT "EPI, FMRI"
+IOPT "Fast, IrP"
+IOPT "Fast, ZIP512, FR"
+IOPT "FC, EDR, TRF, Fast, ZIP512"
+IOPT "FC, VBw, EDR"
+IOPT "None"
+IOPT "NPW, Seq, VBw, TRF, Fast"
+IOPT "NPW, TRF, Fast, ZIP512, FR"
+IOPT "NPW, VBw, EDR, Fast, ZIP2"
+IOPT "NPW, VBw, Fast"
+IOPT "NPW, ZIP512"
+IOPT "TRF, Fast"
+IOPT "VBw, EDR, Fast"
+IOPT "VBw, Fast"
+MASKPAUSE "0"
+MASKPHASE "0"
+MATRIXX "192"
+MATRIXX "256"
+MATRIXX "288"
+MATRIXX "320"
+MATRIXX "416"
+MATRIXX "512"
+MATRIXX "64"
+MATRIXY "128"
+MATRIXY "160"
+MATRIXY "192"
+MATRIXY "224"
+MATRIXY "256"
+MATRIXY "320"
+MATRIXY "64"
+MONSAR "y"
+NECHO "1"
+NEX "1.00"
+NEX "1.50"
+NEX "2.00"
+NEX "3.00"
+NEX "4.00"
+NOSLC "1"
+NOSLC "12"
+NOSLC "15"
+NOSLC "19"
+NOSLC "20"
+NOSLC "21"
+NOSLC "24"
+NOSLC "26"
+NOSLC "56"
+NOTES ".pn/_2"
+NOTES ".pn/_3"
+NOTES ".pn/_4"
+NUMACCELFACTOR "1.00"
+NUMACCELFACTOR "Recommended"
+NUMACQS "0"
+NUMACQS "2"
+NUMSHOTS "1"
+OVLPLOC "0"
+PAUSEDELMASKACQ "1"
+PDGMSTR "None"
+PHASEASSET "1.00"
+PHASECORR "No"
+PHASECORR "Yes"
+PHASEFOV "0.75"
+PHASEFOV "1.00"
+PLANE "3-PLANE"
+PLANE "AXIAL"
+PLANE "OBLIQUE"
+PLUG "0"
+PLUG "11"
+PLUG "14"
+PLUG "22"
+PLUG "23"
+PLUG "45"
+PLUG "5"
+PLUG "6"
+PLUG "9"
+POSITION "Prone"
+POSITION "Supine"
+PRESETDELAY "0.0"
+PSDNAME "fse-xl"
+PSDTRIG "0"
+PSEQ "FRFSE-XL"
+PSEQ "FSE-XL"
+PSEQ "Gradient Echo"
+PSEQ "IR"
+PSEQ "Localizer"
+PSEQ "SPGR"
+PSEQ "Spin Echo"
+RBW "12.50"
+RBW "14.71"
+RBW "15.63"
+RBW "17.86"
+RBW "20.83"
+RBW "22.73"
+RBW "25.00"
+RBW "31.25"
+SATLOCZ1 "9990"
+SATLOCZ2 "9990"
+SATTHICKZ1 "40.0"
+SATTHICKZ2 "40.0"
+SEDESC "3D FSPGR IR"
+SEDESC "3DIR PREP"
+SEDESC "3 plane loc"
+SEDESC "AX FSE T1"
+SEDESC "AX FSE T2"
+SEDESC "AX T2*"
+SEDESC "FATSAT T2 FSE Scout"
+SEDESCFLAG "1"
+SEDESC "LOCALIZER-RATCOIL"
+SEDESC "O-Ax FATSAT T2 FSE high Res"
+SEDESC "Oblique PD AX"
+SEDESC "Oblique STIR"
+SEDESC "Oblique T1 AX +C"
+SEDESC "Oblique T1-SAG"
+SEDESC "Oblique T1-SAG+C"
+SEDESC "Oblique T2 AX."
+SEDESC "O-Cor T1 "
+SEDESC "RUN 1"
+SEDESC "SPGR3D-HRES-Brasch"
+SEDESC "SPGR3D-LRES-Brasch"
+SEPSERIES "0"
+SL3PLANE "0"
+SL3PLANE "1"
+SL3PLANE1 "0"
+SL3PLANE1 "5"
+SL3PLANE2 "0"
+SL3PLANE2 "5"
+SL3PLANE3 "0"
+SL3PLANE3 "5"
+SLABLOC "128"
+SLABLOC "144"
+SLABLOC "64"
+SLABLOC "80"
+SLICEASSET "1.00"
+SLICEORDER "1"
+SLOC1 "I35.2"
+SLOC1 "I59.6"
+SLOC1 "I93.4"
+SLOC1 "L13.9"
+SLOC1 "L1.6"
+SLOC1 "L2.1"
+SLOC1 "L5.0"
+SLOC1 "L5.9"
+SLOC1 "L84.1"
+SLOC1 "L85.9"
+SLOC1 "L99.2"
+SLOC1 "R86.3"
+SLOC2 "0.0"
+SLOC2 "A11.0"
+SLOC2 "A115.0"
+SLOC2 "A79.2"
+SLOC2 "A80.8"
+SLOC2 "P34.4"
+SLOC2 "P37.0"
+SLOC2 "P46.5"
+SLOC2 "P50.2"
+SLOC3 "I27.8"
+SLOC3 "I37.0"
+SLOC3 "I7.1"
+SLOC3 "S12.3"
+SLOC3 "S163.1"
+SLOC3 "S1.7"
+SLOC3 "S5.4"
+SLOC3 "S7.0"
+SLPERLOC "274"
+SLTHICK "0.2"
+SLTHICK "0.7"
+SLTHICK "1.2"
+SLTHICK "1.3"
+SLTHICK "3.0"
+SLTHICK "4.0"
+SLTHICK "5.0"
+SLTHICK "5.5"
+SPC "0.0"
+SPC "1.5"
+SPCPERPLANE1 "0.0"
+SPCPERPLANE1 "1.5"
+SPCPERPLANE2 "0.0"
+SPCPERPLANE2 "1.5"
+SPCPERPLANE3 "0.0"
+SPCPERPLANE3 "1.5"
+STATION "0"
+SUPPTQ "1"
+SWAPPF "A/P"
+SWAPPF "R/L"
+SWAPPF "S/I"
+SWAPPF "Unswap"
+TAG_SPACE "7"
+TAG_TYPE "None"
+TBLDELTA "0.00"
+TE "100.0"
+TE "102.0"
+TE "15.0"
+TE "30.0"
+TE "50.0"
+TE "Min Full"
+TE "Minimum"
+TI "150"
+TI "450"
+TI "500"
+TOTALNOSTATION "0"
+TR "2000.0"
+TR "3000.0"
+TR "4000.0"
+TR "4125.0"
+TR "4575.0"
+TR "475.0"
+TR "500.0"
+TR "5200.0"
+TR "525.0"
+TR "5325.0"
+TR "6600.0"
+TRACKLEN "200.0"
+TRACKTHICK "20.0"
+TRACTIVE "0"
+TRACTIVE "4"
+TRICKSIMG "1"
+TRREST "0"
+TRREST "4"
+USERCV0 "0.00"
+USERCV0 "1.00"
+USERCV21 "0.00"
+USERCV23 "100.00"
+USERCV4 "0.00"
+USERCV6 "0.00"
+USERCV7 "0.00"
+USERCV_MASK "0"
+USERCV_MASK "1"
+USERCV_MASK "128"
+USERCV_MASK "192"
+USERCV_MASK "2097344"
+USERCV_MASK "6144"
+USERCV_MASK "64"
+USERCV_MASK "8388688"
+VIEWORDER "1"
+
+*/
diff --git a/gdcm/Applications/Cxx/gdcmfile.cxx b/gdcm/Applications/Cxx/gdcmfile.cxx
new file mode 100644
index 0000000..fc8db9f
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmfile.cxx
@@ -0,0 +1,83 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+#include
+#include
+#include
+
+// fstat
+#include
+#include
+#include
+
+// open
+#include
+#include
+#include
+
+// mmap
+#include
+
+#include "gdcmFile.h"
+#include "gdcmObject.h"
+#include "gdcmDataSet.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmSmartPointer.h"
+#include "gdcmDeflateStream.h"
+#include "gdcmDumper.h"
+#include "gdcmDirectory.h"
+#include "gdcmSystem.h"
+
+
+int DoOperation(std::string const & path)
+{
+ //std::cout << path << "\n";
+ std::ifstream is( path.c_str(), std::ios::binary );
+ is.seekg(0, std::ios::end );
+ std::streampos size = is.tellg();
+ is.seekg(0,std::ios::beg);
+ std::vector buffer;
+ buffer.resize(size);
+ is.read(&buffer[0], size );
+ char k = buffer[(size_t)size-1]; // at least read one char to avoid compiler optimization
+ is.close();
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if( argc < 2 )
+ {
+ return 1;
+ }
+ std::string filename = argv[1];
+
+ int res = 0;
+ if( gdcm::System::FileIsDirectory( filename.c_str() ) )
+ {
+ gdcm::Directory d;
+ d.Load(filename);
+ gdcm::Directory::FilenamesType const &filenames = d.GetFilenames();
+ for( gdcm::Directory::FilenamesType::const_iterator it = filenames.begin(); it != filenames.end(); ++it )
+ {
+ res += DoOperation(*it);
+ }
+ }
+ else
+ {
+ res += DoOperation(filename);
+ }
+
+ return res;
+}
diff --git a/gdcm/Applications/Cxx/gdcmgendir.cxx b/gdcm/Applications/Cxx/gdcmgendir.cxx
new file mode 100644
index 0000000..1be3b43
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmgendir.cxx
@@ -0,0 +1,349 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * Implementation of General Purpose CD-R Interchange / STD-GEN-CD DICOMDIR in GDCM
+ */
+#include "gdcmReader.h"
+#include "gdcmWriter.h"
+#include "gdcmVersion.h"
+#include "gdcmSystem.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmGlobal.h"
+#include "gdcmDefs.h"
+#include "gdcmDirectory.h"
+#include "gdcmDICOMDIRGenerator.h"
+
+#include
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcmgendir: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmgendir [OPTION]... FILE..." << std::endl;
+ std::cout << "create DICOMDIR" << std::endl;
+ std::cout << "Parameter:" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -i --input DICOM filename or directory" << std::endl;
+ std::cout << " -o --output DICOM filename or directory" << std::endl;
+ std::cout << " -r --recursive recursive." << std::endl;
+ std::cout << " --descriptor descriptor." << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ std::string filename;
+ std::string outfilename;
+ gdcm::Directory::FilenamesType filenames;
+ std::string xmlpath;
+ int verbose = 0;
+ int warning = 0;
+ int help = 0;
+ int recursive = 0;
+ int version = 0;
+ int debug = 0;
+ int error = 0;
+ int resourcespath = 0;
+ int rootuid = 0;
+ int descriptor = 0;
+ std::string descriptor_str;
+ std::string root;
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0}, // o
+ {"recursive", 0, &recursive, 1},
+ {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
+ {"resources-path", 1, &resourcespath, 1},
+ {"descriptor", 1, &descriptor, 1},
+
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:o:rVWDEhv";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 1 ) /* output */
+ {
+ assert( strcmp(s, "output") == 0 );
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ }
+ else if( option_index == 3 ) /* root-uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ root = optarg;
+ }
+ else if( option_index == 4 ) /* resources-path */
+ {
+ assert( strcmp(s, "resources-path") == 0 );
+ assert( xmlpath.empty() );
+ xmlpath = optarg;
+ }
+ else if( option_index == 5 ) /* descriptor */
+ {
+ assert( strcmp(s, "descriptor") == 0 );
+ assert( descriptor_str.empty() );
+ descriptor_str = optarg;
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ case 'r':
+ recursive = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: %d", optind );
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() >= 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0].c_str();
+ filenames = files;
+ outfilename = files[ files.size() - 1 ].c_str();
+ filenames.pop_back();
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty()
+ || outfilename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( debug != 0 );
+ gdcm::Trace::SetWarning( warning != 0 );
+ gdcm::Trace::SetError( error != 0 );
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( verbose != 0 );
+ gdcm::Trace::SetError( verbose != 0);
+ }
+
+ if( !gdcm::System::FileExists(filename.c_str()) )
+ {
+ return 1;
+ }
+
+/*
+ gdcm::Global& g = gdcm::Global::GetInstance();
+ // First thing we need to locate the XML dict
+ // did the user requested to look XML file in a particular directory ?
+ if( !resourcespath )
+ {
+ const char *xmlpathenv = getenv("GDCM_RESOURCES_PATH");
+ if( xmlpathenv )
+ {
+ // Make sure to look for XML dict in user explicitly specified dir first:
+ xmlpath = xmlpathenv;
+ resourcespath = 1;
+ }
+ }
+ if( resourcespath )
+ {
+ // xmlpath is set either by the cmd line option or the env var
+ if( !g.Prepend( xmlpath.c_str() ) )
+ {
+ std::cerr << "specified Resources Path is not valid: " << xmlpath << std::endl;
+ return 1;
+ }
+ }
+
+ // All set, then load the XML files:
+ if( !g.LoadResourcesFiles() )
+ {
+ return 1;
+ }
+
+ const gdcm::Defs &defs = g.GetDefs();
+*/
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmgendir" );
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ int res = 0;
+ unsigned int nfiles = 1;
+ gdcm::DICOMDIRGenerator gen;
+ if( gdcm::System::FileIsDirectory(filename.c_str()) )
+ {
+ gdcm::Directory dir;
+ nfiles = dir.Load(filename, recursive!= 0); (void)nfiles;
+ filenames = dir.GetFilenames();
+ gen.SetRootDirectory( filename );
+ }
+ else
+ {
+ // should be all set !
+ }
+ (void)nfiles;
+
+ gen.SetFilenames( filenames );
+ gen.SetDescriptor( descriptor_str.c_str() );
+ if( !gen.Generate() )
+ {
+ std::cerr << "Problem during generation" << std::endl;
+ return 1;
+ }
+
+ gdcm::Writer writer;
+ writer.SetFile( gen.GetFile() );
+ writer.SetFileName( outfilename.c_str() );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return res;
+}
diff --git a/gdcm/Applications/Cxx/gdcmimg.cxx b/gdcm/Applications/Cxx/gdcmimg.cxx
new file mode 100644
index 0000000..f3c2584
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmimg.cxx
@@ -0,0 +1,1237 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * TODO: Merging (burnin) of overlay would be nice (merge 0x60xx overlay into PixelData)
+ * TODO: --add-thumbnail / --remove-thumbnail
+ * convert: -thumbnail geometry create a thumbnail of the image
+ * convert: -crop geometry cut out a rectangular region of the image
+ * -floodfill geometry color
+ * floodfill the image with color
+ * 1. Create a DICOM file from a 'raw' input:
+ * 2. Create a blob (jpeg,pgm/pnm,j2k,rle) from input
+ * - binary blob(s) (grayscale / RGB) input
+ * - jpeg(s)
+ * - j2k(s)
+ *
+ * Mapping is:
+ *
+ * DICOM RAW <-> pnm/pgm
+ * DICOM jpg <-> jpg
+ * DICOM ljpg <-> ljpg
+ * DICOM jls <-> jls
+ * DICOM j2k <-> j2k
+ * DICOM rle <-> Utah RLE ??
+ *
+ * ??:
+ * DICOM avi <-> avi
+ * DICOM wav <-> wav
+ * DICOM pdf <-> pdf
+ * Todo: check compat API with jhead
+ */
+#include "gdcmFilename.h"
+#include "gdcmDirectory.h"
+#include "gdcmMediaStorage.h"
+#include "gdcmSmartPointer.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmSequenceOfFragments.h"
+#include "gdcmSystem.h"
+#include "gdcmReader.h"
+#include "gdcmPixmapWriter.h"
+#include "gdcmPixmapReader.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmAttribute.h"
+#include "gdcmPNMCodec.h"
+#include "gdcmPGXCodec.h"
+#include "gdcmJPEGCodec.h"
+#include "gdcmJPEGLSCodec.h"
+#include "gdcmJPEG2000Codec.h"
+#include "gdcmRLECodec.h"
+#include "gdcmRAWCodec.h"
+#include "gdcmVersion.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+#ifdef _MSC_VER
+#define atoll _atoi64
+#endif
+
+static unsigned int readsize(const char *str, unsigned int * size)
+{
+ int n = sscanf( str, "%i,%i,%i", size, size+1, size+2);
+ return n == EOF ? 0 : (unsigned int)n;
+}
+
+static bool readgeometry(const char *geometry, unsigned int * region)
+{
+ int n = sscanf( geometry, "%i,%i,%i,%i,%i,%i", region, region+1, region+2, region+3, region+4, region+5);
+ if( n != 6 ) return false;
+ return true;
+}
+
+template
+static void FillRegionWithColor(T *p, const unsigned int *dims, const unsigned int * region, unsigned int color, unsigned int nsamples)
+{
+ unsigned int xmin = region[0];
+ unsigned int xmax = region[1];
+ unsigned int ymin = region[2];
+ unsigned int ymax = region[3];
+ unsigned int zmin = region[4];
+ unsigned int zmax = region[5];
+
+ for( unsigned int x = xmin; x <= xmax; ++x)
+ {
+ for( unsigned int y = ymin; y <= ymax; ++y)
+ {
+ for( unsigned int z = zmin; z <= zmax; ++z)
+ {
+ for( unsigned int sample = 0; sample < nsamples; ++sample)
+ {
+ p[x*nsamples+y*dims[0]*nsamples+z*dims[0]*dims[1]*nsamples+sample] = (T)color;
+ }
+ }
+ }
+ }
+}
+
+static void PrintVersion()
+{
+ std::cout << "gdcmimg: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmimg [OPTION]... FILE..." << std::endl;
+ std::cout << "Manipulate DICOM image file:" << std::endl;
+ std::cout << " 1. Convert to and from other file format (jpg, jp2, pnm...)" << std::endl;
+ std::cout << " 2. Anonymize burn-in annotation (rect region fill with pixel value)" << std::endl;
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input Input filename" << std::endl;
+ std::cout << " -o --output Output filename" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " --endian %s Endianness (LSB/MSB)." << std::endl;
+ std::cout << " -d --depth %d Depth (Either 8/16/32 or BitsAllocated eg. 12 when known)." << std::endl;
+ std::cout << " --sign %s Pixel sign (0/1)." << std::endl;
+ std::cout << " --spp %d Sample Per Pixel (1/3)." << std::endl;
+ std::cout << " --pc [01] Change planar configuration." << std::endl;
+ std::cout << " --pi [str] Change photometric interpretation." << std::endl;
+ std::cout << " --pf %d,%d,%d Change pixel format: (BA,BS,HB)." << std::endl;
+ std::cout << " -s --size %d,%d,%d Size." << std::endl;
+ std::cout << " --offset %ull Start Offset." << std::endl;
+ std::cout << " -C --sop-class-uid SOP Class UID (name or value)." << std::endl;
+ std::cout << " -T --study-uid Study UID." << std::endl;
+ std::cout << " -S --series-uid Series UID." << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ std::cout << "Fill Options:" << std::endl;
+ std::cout << " -R --region %d,%d Region." << std::endl;
+ std::cout << " -F --fill %d Fill with pixel value specified." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+/*
+ * Default behavior for root UID is:
+ * By default the GDCM one is used
+ * If GDCM_ROOT_UID is set, then use this one instead
+ * If --root-uid is explicitly set on the command line, it will override any other defined behavior
+ */
+}
+
+static bool AddContentDateTime(gdcm::DataSet &ds, const char *filename )
+{
+ time_t studydatetime = gdcm::System::FileTime( filename );
+ char date[22];
+ gdcm::System::FormatDateTime(date, studydatetime);
+ const size_t datelen = 8;
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0023) ); // Content Date
+ // Do not copy the whole cstring:
+ de.SetByteValue( date, datelen );
+ de.SetVR( gdcm::Attribute<0x0008,0x0023>::GetVR() );
+ ds.Insert( de );
+ }
+ // StudyTime
+ const size_t timelen = 6; // get rid of milliseconds
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0033) ); // Content Time
+ // Do not copy the whole cstring:
+ de.SetByteValue( date+datelen, timelen );
+ de.SetVR( gdcm::Attribute<0x0008,0x0033>::GetVR() );
+ ds.Insert( de );
+ }
+ return true;
+}
+// Set Study Date/Time to the file time:
+static bool AddStudyDateTime(gdcm::DataSet &ds, const char *filename )
+{
+ // StudyDate
+ char date[22];
+ const size_t datelen = 8;
+ int res = gdcm::System::GetCurrentDateTime(date);
+ if( !res ) return false;
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0020) );
+ // Do not copy the whole cstring:
+ de.SetByteValue( date, datelen );
+ de.SetVR( gdcm::Attribute<0x0008,0x0020>::GetVR() );
+ ds.Insert( de );
+ }
+ // StudyTime
+ const size_t timelen = 6; // get rid of milliseconds
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0030) );
+ // Do not copy the whole cstring:
+ de.SetByteValue( date+datelen, timelen );
+ de.SetVR( gdcm::Attribute<0x0008,0x0030>::GetVR() );
+ ds.Insert( de );
+ }
+ return AddContentDateTime(ds, filename);
+}
+
+
+static bool AddUIDs(int sopclassuid, std::string const & sopclass, std::string const & study_uid, std::string const & series_uid, gdcm::PixmapWriter& writer)
+{
+ gdcm::DataSet & ds = writer.GetFile().GetDataSet();
+ gdcm::MediaStorage ms = gdcm::MediaStorage::MS_END;
+ if( sopclassuid )
+ {
+ // Is it by value or by name ?
+ if( gdcm::UIDGenerator::IsValid( sopclass.c_str() ) )
+ {
+ ms = gdcm::MediaStorage::GetMSType( sopclass.c_str() );
+ }
+ else
+ {
+ std::cerr << "not implemented" << std::endl;
+ }
+ if( !gdcm::MediaStorage::IsImage(ms) )
+ {
+ std::cerr << "invalid media storage (no pixel data): " << sopclass << std::endl;
+ return false;
+ }
+
+ const char* msstr = gdcm::MediaStorage::GetMSString(ms);
+ if( !msstr )
+ {
+ std::cerr << "problem with media storage: " << sopclass << std::endl;
+ return false;
+ }
+ gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016 ) );
+ de.SetByteValue( msstr, (uint32_t)strlen(msstr) );
+ de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() );
+ ds.Insert( de );
+ }
+ else
+ {
+ // FIXME we are copying the default behavior of gdcm.PixmapWriter here:
+ ms = gdcm::MediaStorage::SecondaryCaptureImageStorage;
+ }
+
+ gdcm::Pixmap &image = writer.GetPixmap();
+ if( ms.GetModalityDimension() < image.GetNumberOfDimensions() )
+ {
+ std::cerr << "Could not find Modality" << std::endl;
+ return false;
+ }
+
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0020,0x000d) ); // Study
+ de.SetByteValue( study_uid.c_str(), (uint32_t)study_uid.size() );
+ de.SetVR( gdcm::Attribute<0x0020, 0x000d>::GetVR() );
+ ds.Insert( de );
+ }
+
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0020,0x000e) ); // Series
+ de.SetByteValue( series_uid.c_str(), (uint32_t)series_uid.size() );
+ de.SetVR( gdcm::Attribute<0x0020, 0x000e>::GetVR() );
+ ds.Insert( de );
+ }
+
+ return true;
+}
+
+static bool PopulateSingeFile( gdcm::PixmapWriter & writer, gdcm::SequenceOfFragments *sq , gdcm::ImageCodec & jpeg, const char *filename, std::streampos const pos = 0 )
+{
+ /*
+ * FIXME: when JPEG contains JFIF marker, we should only read them
+ * during header parsing but discard them when copying the JPG byte stream into
+ * the encapsulated Pixel Data Element...
+ */
+ std::ifstream is(filename, std::ios::binary);
+ gdcm::TransferSyntax ts;
+ bool b = jpeg.GetHeaderInfo( is, ts );
+ if( !b )
+ {
+ std::cerr << "Could not read: " << filename << std::endl;
+ return false;
+ }
+
+ gdcm::Pixmap &image = writer.GetPixmap();
+ image.SetDimensions( jpeg.GetDimensions() );
+ image.SetPixelFormat( jpeg.GetPixelFormat() );
+ image.SetPhotometricInterpretation( jpeg.GetPhotometricInterpretation() );
+ image.SetPlanarConfiguration( jpeg.GetPlanarConfiguration() );
+ image.SetTransferSyntax( ts );
+
+ AddStudyDateTime( writer.GetFile().GetDataSet(), filename );
+
+ size_t len = gdcm::System::FileSize(filename);
+ if( ts.IsEncapsulated() )
+ {
+ is.seekg(0, std::ios::beg );// rewind !
+ }
+ else
+ {
+ len = image.GetBufferLength();
+ // do not rewind file should be just at right offset
+ }
+ char *buf = new char[len];
+ if( pos )
+ {
+ is.seekg( pos, std::ios::beg );
+ }
+ is.read(buf, len);
+ gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
+
+ if( ts.IsEncapsulated() )
+ {
+ gdcm::Fragment frag;
+ frag.SetByteValue( buf, (uint32_t)len );
+ sq->AddFragment( frag );
+ pixeldata.SetValue( *sq );
+ }
+ else
+ {
+ pixeldata.SetByteValue( buf, (uint32_t)len );
+ }
+ delete[] buf;
+ image.SetDataElement( pixeldata );
+
+ return true;
+}
+
+static bool Populate( gdcm::PixmapWriter & writer, gdcm::ImageCodec & jpeg, gdcm::Directory::FilenamesType const & filenames, unsigned int ndim = 2, std::streampos const & pos = 0 )
+{
+ std::vector::const_iterator it = filenames.begin();
+ bool b = true;
+ gdcm::Pixmap &image = writer.GetPixmap();
+ image.SetNumberOfDimensions( ndim );
+ if( filenames.size() > 1 )
+ {
+ image.SetNumberOfDimensions( 3 );
+ }
+
+ gdcm::SmartPointer sq = new gdcm::SequenceOfFragments;
+ for(; it != filenames.end(); ++it)
+ {
+ b = b && PopulateSingeFile( writer, sq, jpeg, it->c_str(), pos );
+ }
+ if( filenames.size() > 1 )
+ {
+ image.SetDimension(2, (unsigned int)filenames.size() );
+ }
+
+ return b;
+}
+
+
+static bool GetPixelFormat( gdcm::PixelFormat & pf, int depth, int bpp, int sign, int pixelsign, int spp = 0, int pixelspp = 1 )
+{
+ if( depth )
+ {
+ if( bpp <= 8 )
+ {
+ pf = gdcm::PixelFormat::UINT8;
+ }
+ else if( bpp > 8 && bpp <= 16 )
+ {
+ pf = gdcm::PixelFormat::UINT16;
+ }
+ else if( bpp > 16 && bpp <= 32 )
+ {
+ pf = gdcm::PixelFormat::UINT32;
+ }
+ else
+ {
+ std::cerr << "Invalid depth: << " << bpp << std::endl;
+ return false;
+ }
+ pf.SetBitsStored( (short)bpp );
+ }
+ if( sign )
+ {
+ pf.SetPixelRepresentation( (unsigned short)pixelsign );
+ }
+ if( spp )
+ {
+ pf.SetSamplesPerPixel( (unsigned short)pixelspp );
+ }
+
+ return true;
+}
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string root;
+ int rootuid = 0;
+ gdcm::Filename filename;
+ gdcm::Directory::FilenamesType filenames;
+ gdcm::Filename outfilename;
+ unsigned int region[6] = {}; // Rows & Columns are VR=US anyway...
+ unsigned int color = 0;
+ int bregion = 0;
+ int fill = 0;
+ int sign = 0;
+ int spp = 0;
+ int pconf = 0; // planar configuration
+ int studyuid = 0;
+ int seriesuid = 0;
+ unsigned int size[3] = {0,0,0};
+ unsigned int ndimension = 2;
+ int depth = 0;
+ int endian = 0;
+ int bpp = 0;
+ int pixelsign = 0;
+ int pixelspp = 0;
+ std::string sopclass;
+ std::string lsb_msb;
+ int sopclassuid = 0;
+ int pinter = 0;
+ std::string pinterstr;
+ int pformat = 0;
+ std::string pformatstr;
+ int poffset = 0;
+ size_t start_pos = 0;
+
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+
+ gdcm::UIDGenerator uid;
+ // Too early for UID Generation
+ std::string series_uid; // = uid.Generate();
+ std::string study_uid; // = uid.Generate();
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ // provide convert-like command line args:
+ {"depth", 1, &depth, 1},
+ {"size", 1, 0, 0},
+ {"region", 1, &bregion, 1},
+ {"fill", 1, &fill, 1},
+ {"study-uid", 1, &studyuid, 1},
+ {"series-uid", 1, &seriesuid, 1},
+ {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
+ {"sop-class-uid", 1, &sopclassuid, 1}, // specific SOP Class UID
+ {"endian", 1, &endian, 1}, //
+ {"sign", 1, &sign, 1}, //
+ {"spp", 1, &spp, 1}, //
+ {"pc", 1, &pconf, 1}, //
+ {"pi", 1, &pinter, 1}, //
+ {"pf", 1, &pformat, 1}, //
+ {"offset", 1, &poffset, 1}, //
+
+// General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {0, 0, 0, 0}
+ };
+
+ // i -> input file
+ // I -> input directory
+ // o -> output file
+ // O -> output directory
+ c = getopt_long (argc, argv, "i:o:I:O:d:s:R:C:F:T:S:VWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.IsEmpty() );
+ filename = optarg;
+ }
+ else if( option_index == 2 ) /* depth */
+ {
+ assert( strcmp(s, "depth") == 0 );
+ bpp = atoi(optarg);
+ }
+ else if( option_index == 3 ) /* size */
+ {
+ assert( strcmp(s, "size") == 0 );
+ ndimension = readsize(optarg, size);
+ }
+ else if( option_index == 4 ) /* region */
+ {
+ assert( strcmp(s, "region") == 0 );
+ readgeometry(optarg, region);
+ }
+ else if( option_index == 5 ) /* fill */
+ {
+ assert( strcmp(s, "fill") == 0 );
+ color = atoi(optarg);
+ }
+ else if( option_index == 6 ) /* study-uid */
+ {
+ assert( strcmp(s, "study-uid") == 0 );
+ study_uid = optarg;
+ }
+ else if( option_index == 7 ) /* series-uid */
+ {
+ assert( strcmp(s, "series-uid") == 0 );
+ series_uid = optarg;
+ }
+ else if( option_index == 8 ) /* root-uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ root = optarg;
+ }
+ else if( option_index == 9 ) /* sop-class-uid */
+ {
+ assert( strcmp(s, "sop-class-uid") == 0 );
+ sopclass = optarg;
+ }
+ else if( option_index == 10 ) /* endian */
+ {
+ assert( strcmp(s, "endian") == 0 );
+ lsb_msb = optarg;
+ }
+ else if( option_index == 11 ) /* sign */
+ {
+ assert( strcmp(s, "sign") == 0 );
+ pixelsign = atoi(optarg);
+ }
+ else if( option_index == 12 ) /* spp */
+ {
+ assert( strcmp(s, "spp") == 0 );
+ pixelspp = atoi(optarg);
+ }
+ else if( option_index == 13 ) /* pconf */
+ {
+ assert( strcmp(s, "pc") == 0 );
+ pconf = atoi(optarg);
+ }
+ else if( option_index == 14 ) /* pinter */
+ {
+ assert( strcmp(s, "pi") == 0 );
+ pinter = 1;
+ pinterstr = optarg;
+ }
+ else if( option_index == 15 ) /* pformat */
+ {
+ assert( strcmp(s, "pf") == 0 );
+ pformat = 1;
+ pformatstr = optarg;
+ }
+ else if( option_index == 16 ) /* start_pos */
+ {
+ assert( strcmp(s, "offset") == 0 );
+ poffset = 1;
+ start_pos = atoll(optarg);
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.IsEmpty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ //printf ("option o with value '%s'\n", optarg);
+ assert( outfilename.IsEmpty() );
+ outfilename = optarg;
+ break;
+
+ case 'd': // depth
+ bpp = atoi(optarg);
+ depth = 1;
+ break;
+
+ case 's': // size
+ ndimension = readsize(optarg, size);
+ break;
+
+ case 'T':
+ studyuid = 1;
+ study_uid = optarg;
+ break;
+
+ case 'S':
+ seriesuid = 1;
+ series_uid = optarg;
+ break;
+
+ case 'C':
+ sopclassuid = 1;
+ sopclass = optarg;
+ break;
+
+ case 'R': // region
+ //outfilename = optarg;
+ readgeometry(optarg, region);
+ break;
+
+ case 'F': // fill
+ color = atoi( optarg );
+ fill = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ // For now only support one input / one output
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: ");
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() >= 2
+ && filename.IsEmpty()
+ && outfilename.IsEmpty()
+ )
+ {
+ filename = files[0].c_str();
+ filenames = files;
+ outfilename = files[ files.size() - 1 ].c_str();
+ filenames.pop_back();
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filenames.empty() && filename.IsEmpty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+ if( outfilename.IsEmpty() )
+ {
+ //std::cerr << "Need output file (-o)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Ok so we are about to write a DICOM file, do not forget to stamp it GDCM !
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmimg" );
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+ if( study_uid.empty() )
+ {
+ study_uid = uid.Generate();
+ }
+ if( !gdcm::UIDGenerator::IsValid( study_uid.c_str() ) )
+ {
+ std::cerr << "Invalid UID for Study UID: " << study_uid << std::endl;
+ return 1;
+ }
+
+ if( series_uid.empty() )
+ {
+ series_uid = uid.Generate();
+ }
+ if( !gdcm::UIDGenerator::IsValid( series_uid.c_str() ) )
+ {
+ std::cerr << "Invalid UID for Series UID: " << series_uid << std::endl;
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ if( depth )
+ {
+ if( bpp < 1 || bpp > 32 )
+ {
+ std::cerr << "Invalid depth for pixel: " << bpp << std::endl;
+ return 1;
+ }
+ }
+ if( sign )
+ {
+ if( pixelsign != 0 && pixelsign != 1 ) return 1;
+ }
+ if( spp )
+ {
+ if( pixelspp != 1 && pixelspp != 3 ) return 1;
+ }
+ if( pconf != 0 && pconf != 1 ) return 1;
+ if( pconf )
+ {
+ if( pixelspp != 3 ) return 1;
+ }
+ gdcm::PixelFormat pfref = gdcm::PixelFormat::UINT8;
+ if( pformat )
+ {
+ int ba, bs, hb;
+ int n = sscanf( pformatstr.c_str(), "%d,%d,%d", &ba, &bs, &hb );
+ if( n != 3 ) return 1;
+ pfref.SetBitsAllocated( (unsigned short)ba );
+ pfref.SetBitsStored( (unsigned short)bs );
+ pfref.SetHighBit( (unsigned short)hb );
+ if( spp )
+ pfref.SetSamplesPerPixel( (unsigned short)pixelspp );
+ if( sign )
+ pfref.SetPixelRepresentation( (unsigned short)pixelsign );
+ }
+ gdcm::PhotometricInterpretation::PIType refpi = gdcm::PhotometricInterpretation::MONOCHROME2;
+ if( pinter )
+ {
+ refpi = gdcm::PhotometricInterpretation::GetPIType( pinterstr.c_str() );
+ if( refpi == gdcm::PhotometricInterpretation::UNKNOW
+ || refpi == gdcm::PhotometricInterpretation::PI_END )
+ {
+ std::cerr << "Invalid PI: " << pinterstr << std::endl;
+ return 1;
+ }
+ }
+
+ const char *inputextension = filename.GetExtension();
+ const char *outputextension = outfilename.GetExtension();
+ //if( !inputextension || !outputextension ) return 1;
+ if( inputextension )
+ {
+ if( gdcm::System::StrCaseCmp(inputextension,".raw") == 0 // watch out that .raw for kakadu means big-endian
+ || gdcm::System::StrCaseCmp(inputextension,".rawl") == 0 // kakadu convention for raw little endian
+ || gdcm::System::StrCaseCmp(inputextension,".gray") == 0 // imagemagick convention
+ || gdcm::System::StrCaseCmp(inputextension,".bin") == 0 // openjp3d convention for raw little endian
+ || gdcm::System::StrCaseCmp(inputextension,".rgb") == 0 ) // imagemagick convention
+ {
+ if( !size[0] || !size[1] )
+ {
+ std::cerr << "need to specify size of image stored in RAW file" << std::endl;
+ return 1;
+ }
+ gdcm::RAWCodec raw;
+ gdcm::PixmapWriter writer;
+ // Because the RAW stream is not self sufficient, we need to pass in some extra
+ // user info:
+ unsigned int dims[3] = {};
+ dims[0] = size[0];
+ dims[1] = size[1];
+ if( ndimension == 3 )
+ {
+ dims[2] = size[2];
+ }
+ raw.SetDimensions( dims );
+ gdcm::PixelFormat pf = gdcm::PixelFormat::UINT8;
+ gdcm::PhotometricInterpretation pi = refpi;
+ if( gdcm::System::StrCaseCmp(inputextension,".rgb") == 0 )
+ {
+ pi = gdcm::PhotometricInterpretation::RGB;
+ spp = 1;
+ pixelspp = 3;
+ }
+ if( !GetPixelFormat( pf, depth, bpp, sign, pixelsign, spp, pixelspp ) ) return 1;
+ raw.SetPixelFormat( pf );
+ if( spp )
+ {
+ if( pixelspp == 3 ) pi = gdcm::PhotometricInterpretation::RGB;
+ }
+ raw.SetPhotometricInterpretation( pi );
+ raw.SetNeedByteSwap( false );
+ raw.SetPlanarConfiguration( pconf );
+ if( endian )
+ {
+ if( lsb_msb == "LSB" || lsb_msb == "MSB" )
+ {
+ if( lsb_msb == "MSB" )
+ {
+ raw.SetNeedByteSwap( true );
+ }
+ }
+ else
+ {
+ std::cerr << "Unrecognized endian: " << lsb_msb << std::endl;
+ return 1;
+ }
+ }
+
+ if( !Populate( writer, raw, filenames, ndimension, start_pos ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".rle") == 0 )
+ {
+ if( !size[0] || !size[1] )
+ {
+ std::cerr << "need to specify size of image stored in RLE file" << std::endl;
+ return 1;
+ }
+ gdcm::RLECodec rle;
+ gdcm::PixmapWriter writer;
+ // Because the RLE stream is not self sufficient, we need to pass in some extra
+ // user info:
+ unsigned int dims[3] = {};
+ dims[0] = size[0];
+ dims[1] = size[1];
+ rle.SetDimensions( dims );
+ gdcm::PixelFormat pf = gdcm::PixelFormat::UINT8;
+ if( !GetPixelFormat( pf, depth, bpp, sign, pixelsign ) ) return 1;
+ rle.SetPixelFormat( pf );
+ gdcm::PhotometricInterpretation pi = refpi;
+ rle.SetPhotometricInterpretation( pi );
+
+ if( !Populate( writer, rle, filenames ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".pgm") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".pnm") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".ppm") == 0 )
+ {
+ gdcm::PNMCodec pnm;
+ // Let's handle the case where user really wants to specify the data:
+ gdcm::PixelFormat pf = gdcm::PixelFormat::UINT8;
+ if( !GetPixelFormat( pf, depth, bpp, sign, pixelsign ) ) return 1;
+
+ gdcm::PixmapWriter writer;
+ if( !Populate( writer, pnm, filenames ) ) return 1;
+ // populate will guess pixel format and photometric inter from file, need
+ // to override after calling Populate:
+ if( pformat )
+ {
+ writer.GetPixmap().SetPixelFormat( pfref );
+ }
+ if( pinter )
+ {
+ writer.GetPixmap().SetPhotometricInterpretation( refpi );
+ }
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".pgx") == 0 )
+ {
+ gdcm::PGXCodec pnm;
+ gdcm::PixmapWriter writer;
+ if( !Populate( writer, pnm, filenames ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".jls") == 0 )
+ {
+ gdcm::JPEGLSCodec jpeg;
+ gdcm::PixmapWriter writer;
+ if( !Populate( writer, jpeg, filenames ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".jp2") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".j2k") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".j2c") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".jpx") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".jpc") == 0 )
+ {
+ /*
+ * FIXME: Same problem as in classic JPEG: JP2 is NOT a J2K byte stream
+ * need to chop off all extra header information...
+ */
+ gdcm::JPEG2000Codec jpeg;
+ gdcm::PixmapWriter writer;
+ if( !Populate( writer, jpeg, filenames ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".jpg") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".jpeg") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".ljpg") == 0
+ || gdcm::System::StrCaseCmp(inputextension,".ljpeg") == 0 )
+ {
+ gdcm::JPEGCodec jpeg;
+ // Let's handle the case where user really wants to specify signess of data:
+ gdcm::PixelFormat pf = gdcm::PixelFormat::UINT8;
+ if( !GetPixelFormat( pf, depth, bpp, sign, pixelsign ) ) return 1;
+ jpeg.SetPixelFormat( pf );
+ gdcm::PixmapWriter writer;
+ if( !Populate( writer, jpeg, filenames ) ) return 1;
+ if( !AddUIDs(sopclassuid, sopclass, study_uid, series_uid, writer ) ) return 1;
+
+ writer.SetFileName( outfilename );
+ if( !writer.Write() )
+ {
+ std::cerr << "Problem during DICOM steps" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+// else safely assume that if no inputextension matched then it is a DICOM file
+
+ gdcm::PixmapReader reader;
+ reader.SetFileName( filename );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::Pixmap &imageori = reader.GetPixmap();
+ const gdcm::File &file = reader.GetFile();
+
+ if ( outputextension )
+ {
+ if( gdcm::System::StrCaseCmp(outputextension,".pgm") == 0
+ || gdcm::System::StrCaseCmp(outputextension,".pnm") == 0
+ || gdcm::System::StrCaseCmp(outputextension,".ppm") == 0 )
+ {
+ gdcm::PNMCodec pnm;
+ pnm.SetDimensions( imageori.GetDimensions() );
+ pnm.SetPixelFormat( imageori.GetPixelFormat() );
+ pnm.SetPhotometricInterpretation( imageori.GetPhotometricInterpretation() );
+ pnm.SetPlanarConfiguration( imageori.GetPlanarConfiguration() );
+ pnm.SetLUT( imageori.GetLUT() );
+ const gdcm::DataElement& in = imageori.GetDataElement();
+ bool b = pnm.Write( outfilename, in );
+ if( !b )
+ {
+ std::cerr << "Problem writing PNM file" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+ if( gdcm::System::StrCaseCmp(outputextension,".pgx") == 0 )
+ {
+ gdcm::PGXCodec pnm;
+ pnm.SetDimensions( imageori.GetDimensions() );
+ pnm.SetPixelFormat( imageori.GetPixelFormat() );
+ pnm.SetPhotometricInterpretation( imageori.GetPhotometricInterpretation() );
+ pnm.SetPlanarConfiguration( imageori.GetPlanarConfiguration() );
+ pnm.SetLUT( imageori.GetLUT() );
+ const gdcm::DataElement& in = imageori.GetDataElement();
+ bool b = pnm.Write( outfilename, in );
+ if( !b )
+ {
+ std::cerr << "Problem writing PNM file" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+// else safely assume that if no outputextension matched then it is a DICOM file
+
+ gdcm::PixmapWriter writer;
+ writer.SetFile( file );
+ writer.SetImage( imageori );
+ writer.SetFileName( outfilename );
+
+
+ gdcm::DataSet &ds = writer.GetFile().GetDataSet();
+ if( fill )
+ {
+ const gdcm::PixelFormat &pixeltype = imageori.GetPixelFormat();
+ assert( imageori.GetNumberOfDimensions() == 2 || imageori.GetNumberOfDimensions() == 3 );
+ unsigned long len = imageori.GetBufferLength();
+ gdcm::SmartPointer image = new gdcm::Pixmap;
+ image->SetNumberOfDimensions( 2 ); // good default
+ const unsigned int *dims = imageori.GetDimensions();
+ if ( region[0] > region[1]
+ || region[2] > region[3]
+ || region[4] > region[5]
+ || region[1] > dims[0]
+ || region[3] > dims[1]
+ || (imageori.GetNumberOfDimensions() > 2 && region[5] > dims[2]) )
+ {
+ if( imageori.GetNumberOfDimensions() == 2 )
+ {
+ std::cerr << "bogus region. Should be at most: (" << dims[0] << "," << dims[1] << ","
+ /*<< dims[2]*/ << ")" << std::endl;
+ }
+ else
+ {
+ std::cerr << "bogus region. Should be at most: (" << dims[0] << "," << dims[1] << ","
+ << dims[2] << ")" << std::endl;
+ }
+ return 1;
+ }
+ image->SetDimension(0, dims[0] );
+ image->SetDimension(1, dims[1] );
+ if( imageori.GetNumberOfDimensions() == 3 )
+ {
+ image->SetNumberOfDimensions( 3 );
+ image->SetDimension(2, dims[2] );
+ }
+ image->SetPhotometricInterpretation( imageori.GetPhotometricInterpretation() );
+ image->SetPixelFormat( imageori.GetPixelFormat() );
+ image->SetPlanarConfiguration( imageori.GetPlanarConfiguration() );
+ image->SetLUT( imageori.GetLUT() );
+ image->SetLossyFlag( imageori.IsLossy() );
+ // FIXME what is overlay is in pixel data ?
+ gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
+ gdcm::ByteValue *bv = new gdcm::ByteValue();
+ bv->SetLength( (uint32_t)len );
+ //memcpy( bv->GetPointer(), imageori
+ imageori.GetBuffer( (char*)bv->GetPointer() );
+ // Rub out pixels:
+ char *p = (char*)bv->GetPointer();
+ switch(pixeltype)
+ {
+ case gdcm::PixelFormat::UINT8:
+ FillRegionWithColor ((uint8_t*)p, dims, region, color, pixeltype.GetSamplesPerPixel());
+ break;
+ case gdcm::PixelFormat::INT8:
+ FillRegionWithColor ((int8_t*)p, dims, region, color, pixeltype.GetSamplesPerPixel());
+ break;
+ case gdcm::PixelFormat::UINT16:
+ FillRegionWithColor((uint16_t*)p, dims, region, color, pixeltype.GetSamplesPerPixel());
+ break;
+ case gdcm::PixelFormat::INT16:
+ FillRegionWithColor ((int16_t*)p, dims, region, color, pixeltype.GetSamplesPerPixel());
+ break;
+ default:
+ std::cerr << "not implemented" << std::endl;
+ return 1;
+ }
+
+ pixeldata.SetValue( *bv );
+ image->SetDataElement( pixeldata );
+ const gdcm::TransferSyntax &ts = imageori.GetTransferSyntax();
+ // FIXME: for now we do not know how to recompress the image...
+ if( ts.IsExplicit() )
+ {
+ image->SetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
+ }
+ else
+ {
+ assert( ts.IsImplicit() );
+ image->SetTransferSyntax( gdcm::TransferSyntax::ImplicitVRLittleEndian );
+ }
+ //imageori.Print( std::cout );
+ //image.Print( std::cout );
+
+ // Set our filled image instead:
+ writer.SetImage( *image );
+#if 0
+ //
+ gdcm::Attribute<0x0028,0x0301> at;
+ at.SetValue( "NO" ); // 'YES'
+ ds.Replace( at.GetAsDataElement() );
+ // (0008,2111) ST [MedCom Resample v] # 18, 1 DerivationDescriptio
+ gdcm::Attribute<0x0008,0x2111> at2;
+ std::ostringstream os;
+ os << "Fill Region ["
+ << region[0] << "," << region[1] << ","
+ << region[2] << "," << region[3] << ","
+ << region[4] << "," << region[5] << "] with color value=" << std::hex << (int)color;
+ at2.SetValue( os.str() );
+ ds.Replace( at2.GetAsDataElement() );
+#else
+#endif
+/*
+> 1. Replace Value #1 of Image Type by 'DERIVED'
+
+Don't do that ... leave Image Type alone (unless you are changing
+the UID ... vide infra).
+*/
+#if 0
+ // (0008,0008) CS [ORIGINAL\SECONDARY] # 18, 2 ImageType
+ gdcm::Attribute<0x0008,0x0008> at3;
+ static const gdcm::CSComp values[] = {"DERIVED","SECONDARY"};
+ at3.SetValues( values, 2, true ); // true => copy data !
+ if( ds.FindDataElement( at3.GetTag() ) )
+ {
+ const gdcm::DataElement &de = ds.GetDataElement( at3.GetTag() );
+ at3.SetFromDataElement( de );
+ // Make sure that value #1 is at least 'DERIVED', so override in all cases:
+ at3.SetValue( 0, values[0] );
+ }
+ ds.Replace( at3.GetAsDataElement() );
+#endif
+ // Make sure to recompute Planar Configuration:
+ ds.Remove( gdcm::Tag(0x0028, 0x0004) );
+ }
+ // ds.Remove( gdcm::Tag(0x0,0x0) ); // FIXME
+
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcminfo.cxx b/gdcm/Applications/Cxx/gdcminfo.cxx
new file mode 100644
index 0000000..6f52cd9
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcminfo.cxx
@@ -0,0 +1,747 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * TODO:
+ * Should implement the gdcmiodvfy here
+ * I need to implement gdcmoverlay here (print info on overlay / img / LUT ...)
+ */
+#include "gdcmReader.h"
+#include "gdcmImageReader.h"
+#include "gdcmMediaStorage.h"
+#include "gdcmFile.h"
+#include "gdcmDataSet.h"
+#include "gdcmUIDs.h"
+#include "gdcmGlobal.h"
+#include "gdcmModules.h"
+#include "gdcmDefs.h"
+#include "gdcmOrientation.h"
+#include "gdcmVersion.h"
+#include "gdcmMD5.h"
+#include "gdcmSystem.h"
+#include "gdcmDirectory.h"
+
+#ifdef GDCM_USE_SYSTEM_POPPLER
+#include
+#include
+#include
+#include
+#include
+#endif // GDCM_USE_SYSTEM_POPPLER
+
+#include "puff.h"
+
+#include
+
+#include /* for printf */
+#include
+#include /* for exit */
+#include
+#include
+#include
+#include
+
+
+static int checkmagick(unsigned char *input)
+{
+ if( input[128+0] == 'D'
+ && input[128+1] == 'I'
+ && input[128+2] == 'C'
+ && input[128+3] == 'M' )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+static int checkdeflated(const char *name)
+{
+ int ret;
+ unsigned char *source;
+ unsigned long len, sourcelen, destlen;
+
+ unsigned long size;
+ unsigned long size1;
+ unsigned char *buf;
+ FILE *in;
+ struct stat s;
+ //const char *name = 0;
+ union { uint32_t tag; uint16_t tags[2]; char bytes[4]; } tag;
+ char vr[3];
+ uint16_t vl;
+ uint32_t value;
+
+ //if (argc < 2) return 2;
+ //name = argv[1];
+
+ len = 0;
+ if (stat(name, &s))
+ {
+ fprintf( stderr, "Cannot stat: %s\n", name );
+ return 1;
+ }
+ if ((s.st_mode & S_IFMT) != S_IFREG)
+ {
+ fprintf( stderr, "not a regular file\n" );
+ return 1;
+ }
+ size = (unsigned long)(s.st_size);
+ if (size == 0 || (off_t)size != s.st_size)
+ {
+ fprintf( stderr, "size mismatch\n" );
+ return 1;
+ }
+ in = fopen(name, "r");
+ if (in == NULL)
+ {
+ fprintf( stderr, "in is NULL\n" );
+ return 1;
+ }
+ buf = (unsigned char*)malloc(size);
+ if (buf != NULL && (size1 = fread(buf, 1, size, in)) != size) {
+ free(buf);
+ buf = NULL;
+ fprintf( stderr, "could not fread: %lu bytes != %lu\n", size, size1 );
+ fprintf( stderr, "feof: %i ferror %i\n", feof(in), ferror(in) );
+ }
+ fclose(in);
+ len = size;
+ source = buf;
+ if( source == NULL ) {
+ fprintf( stderr, "source is NULL\n" );
+ return 1;
+ }
+ sourcelen = len;
+
+ if( !checkmagick(source) )
+ {
+ fprintf( stderr, "checkmagick failed\n" );
+ return 1;
+ }
+ // magick succeed so skip header:
+ source += 128 + 4;
+ sourcelen -= 128 + 4;
+
+ memcpy(&tag, source, sizeof(tag) );
+ fprintf( stdout,"tag: %d, %d\n", tag.tags[0], tag.tags[1] );
+ source += sizeof(tag);
+ sourcelen -= sizeof(tag);
+
+ vr[2] = 0;
+ memcpy(vr, source, 2);
+ printf( "vr: %s\n", vr);
+
+ source += 2;
+ sourcelen -= 2;
+
+ memcpy(&vl, source, sizeof(vl));
+ printf( "vl: %d\n", vl);
+
+ source += sizeof(vl);
+ sourcelen -= sizeof(vl);
+
+ memcpy(&value, source, sizeof(value));
+ printf( "value: %d\n", value);
+
+ source += sizeof(value);
+ sourcelen -= sizeof(value);
+
+ source += value;
+ sourcelen -= value;
+
+ len = sourcelen;
+ if( len % 2 )
+ {
+ printf( "len of bit stream is odd: %lu. Continuing anyway\n", len );
+ }
+ else
+ {
+ printf( "deflate stream has proper length: %lu\n", len );
+ }
+
+ ret = puff(NULL, &destlen, source, &sourcelen);
+
+ if (ret)
+ fprintf(stdout,"puff() failed with return code %d\n", ret);
+ else {
+ fprintf(stdout,"puff() succeeded uncompressing %lu bytes\n", destlen);
+ if (sourcelen < len) printf("%lu compressed bytes unused\n",
+ len - sourcelen);
+ }
+ free(buf);
+ return ret;
+}
+
+#ifdef GDCM_USE_SYSTEM_POPPLER
+static std::string getInfoDate(Dict *infoDict, const char *key)
+{
+ Object obj;
+ char *s;
+ int year, mon, day, hour, min, sec, n;
+ struct tm tmStruct;
+ //char buf[256];
+ std::string out;
+
+ if (infoDict->lookup((char*)key, &obj)->isString())
+ {
+ s = obj.getString()->getCString();
+ if (s[0] == 'D' && s[1] == ':')
+ {
+ s += 2;
+ }
+ if ((n = sscanf(s, "%4d%2d%2d%2d%2d%2d",
+ &year, &mon, &day, &hour, &min, &sec)) >= 1)
+ {
+ switch (n)
+ {
+ case 1: mon = 1;
+ case 2: day = 1;
+ case 3: hour = 0;
+ case 4: min = 0;
+ case 5: sec = 0;
+ }
+ tmStruct.tm_year = year - 1900;
+ tmStruct.tm_mon = mon - 1;
+ tmStruct.tm_mday = day;
+ tmStruct.tm_hour = hour;
+ tmStruct.tm_min = min;
+ tmStruct.tm_sec = sec;
+ tmStruct.tm_wday = -1;
+ tmStruct.tm_yday = -1;
+ tmStruct.tm_isdst = -1;
+/*
+ // compute the tm_wday and tm_yday fields
+ if (mktime(&tmStruct) != (time_t)-1 &&
+ strftime(buf, sizeof(buf), "%c", &tmStruct)) {
+ fputs(buf, stdout);
+ } else {
+ fputs(s, stdout);
+ }
+ } else {
+ fputs(s, stdout);
+*/
+ }
+ //fputc('\n', stdout);
+ char date[22];
+ time_t t = mktime(&tmStruct);
+ if( t != -1 )
+ {
+ if( gdcm::System::FormatDateTime(date, t) )
+ out = date;
+ }
+ }
+ obj.free();
+ return out;
+}
+
+static std::string getInfoString(Dict *infoDict, const char *key, UnicodeMap *uMap)
+{
+ Object obj;
+ GooString *s1;
+ GBool isUnicode;
+ Unicode u;
+ char buf[8];
+ int i, n;
+ std::string out;
+
+ if (infoDict->lookup((char*)key, &obj)->isString())
+ {
+ s1 = obj.getString();
+ if ((s1->getChar(0) & 0xff) == 0xfe &&
+ (s1->getChar(1) & 0xff) == 0xff)
+ {
+ isUnicode = gTrue;
+ i = 2;
+ }
+ else
+ {
+ isUnicode = gFalse;
+ i = 0;
+ }
+ while (i < obj.getString()->getLength())
+ {
+ if (isUnicode)
+ {
+ u = ((s1->getChar(i) & 0xff) << 8) |
+ (s1->getChar(i+1) & 0xff);
+ i += 2;
+ }
+ else
+ {
+ u = pdfDocEncoding[s1->getChar(i) & 0xff];
+ ++i;
+ }
+ n = uMap->mapUnicode(u, buf, sizeof(buf));
+ //fwrite(buf,1,n,stdout);
+ out.append( std::string(buf, n) );
+ }
+ }
+ obj.free();
+ return out;
+}
+#endif
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcminfo: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcminfo [OPTION]... FILE..." << std::endl;
+ std::cout << "display meta info about the input DICOM file" << std::endl;
+ std::cout << "Parameter:" << std::endl;
+ std::cout << " -i --input DICOM filename or directory" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -r --recursive recursive." << std::endl;
+ std::cout << " -d --check-deflated check if file is proper deflated syntax." << std::endl;
+ std::cout << " --resources-path Resources path." << std::endl;
+ std::cout << " --md5sum Compute md5sum of Pixel Data attribute value." << std::endl;
+ std::cout << " --check-compression check the encapsulated stream compression (lossless/lossy)." << std::endl;
+ // the following options would require an advanced MediaStorage::SetFromFile ... sigh
+ //std::cout << " --media-storage-uid return media storage uid only." << std::endl;
+ //std::cout << " --media-storage-name return media storage name only (when possible)." << std::endl;
+// std::cout << " -b --check-big-endian check if file is ." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_RESOURCES_PATH path pointing to resources files (Part3.xml, ...)" << std::endl;
+}
+
+ int deflated = 0; // check deflated
+ int checkcompression = 0;
+ int md5sum = 0;
+
+static int ProcessOneFile( std::string const & filename, gdcm::Defs const & defs )
+{
+ (void)defs;
+ if( deflated )
+ {
+ return checkdeflated(filename.c_str());
+ }
+
+ //const char *filename = argv[1];
+ //std::cout << "filename: " << filename << std::endl;
+ gdcm::Reader reader0;
+ reader0.SetFileName( filename.c_str() );
+ if( !reader0.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::File &file = reader0.GetFile();
+ gdcm::MediaStorage ms;
+ ms.SetFromFile(file);
+ /*
+ * Until gdcm::MediaStorage is fixed only *compile* time constant will be handled
+ * see -> http://chuckhahm.com/Ischem/Zurich/XX_0134
+ * which make gdcm::UIDs useless :(
+ */
+ if( ms.IsUndefined() )
+ {
+ std::cerr << "Unknown MediaStorage" << std::endl;
+ return 1;
+ }
+
+ gdcm::UIDs uid;
+ uid.SetFromUID( ms.GetString() );
+ std::cout << "MediaStorage is " << ms << " [" << uid.GetName() << "]" << std::endl;
+ const gdcm::TransferSyntax &ts = file.GetHeader().GetDataSetTransferSyntax();
+ uid.SetFromUID( ts.GetString() );
+ std::cout << "TransferSyntax is " << ts << " [" << uid.GetName() << "]" << std::endl;
+
+ if( gdcm::MediaStorage::IsImage( ms ) )
+ {
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read image from: " << filename << std::endl;
+ return 1;
+ }
+ //const gdcm::File &file = reader.GetFile();
+ //const gdcm::DataSet &ds = file.GetDataSet();
+ const gdcm::Image &image = reader.GetImage();
+ const double *dircos = image.GetDirectionCosines();
+ gdcm::Orientation::OrientationType type = gdcm::Orientation::GetType(dircos);
+ const char *label = gdcm::Orientation::GetLabel( type );
+ image.Print( std::cout );
+ std::cout << "Orientation Label: " << label << std::endl;
+ if( checkcompression )
+ {
+ bool lossy = image.IsLossy();
+ std::cout << "Encapsulated Stream was found to be: " << (lossy ? "lossy" : "lossless") << std::endl;
+ }
+
+ if( md5sum )
+ {
+ char *buffer = new char[ image.GetBufferLength() ];
+ if( image.GetBuffer( buffer ) )
+ {
+ char digest[33] = {};
+ gdcm::MD5::Compute( buffer, image.GetBufferLength(), digest );
+ std::cout << "md5sum: " << digest << std::endl;
+ }
+ else
+ {
+ std::cout << "Problem decompressing file: " << filename << std::endl;
+ }
+ delete[] buffer;
+ }
+ }
+ else if ( ms == gdcm::MediaStorage::EncapsulatedPDFStorage )
+ {
+#ifdef GDCM_USE_SYSTEM_POPPLER
+ const gdcm::DataSet &ds = file.GetDataSet();
+ const gdcm::DataElement& de = ds.GetDataElement( gdcm::Tag(0x42,0x11) );
+ const gdcm::ByteValue* bv = de.GetByteValue();
+ const char *p = bv->GetPointer(); (void)p;
+ Object appearDict;
+ //appearDict.initDict(xref);
+ //appearDict.dictAdd(copyString("Length"),
+ // obj1.initInt(appearBuf->getLength()));
+ //appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ //obj1.initArray(xref);
+ //obj1.arrayAdd(obj2.initReal(0));
+ //obj1.arrayAdd(obj2.initReal(0));
+ //obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ //obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ //appearDict.dictAdd(copyString("BBox"), &obj1);
+
+ MemStream *appearStream;
+
+ appearStream = new MemStream((char*)bv->GetPointer(), 0,
+ bv->GetLength(), &appearDict);
+ GooString *ownerPW, *userPW;
+ ownerPW = NULL;
+ userPW = NULL;
+
+ PDFDoc *doc;
+ doc = new PDFDoc(appearStream, ownerPW, userPW);
+
+ std::string title;
+ std::string subject;
+ std::string keywords;
+ std::string author;
+ std::string creator;
+ std::string producer;
+ std::string creationdate;
+ std::string moddate;
+
+ UnicodeMap *uMap;
+#ifdef LIBPOPPLER_GLOBALPARAMS_CSTOR_HAS_PARAM
+ globalParams = new GlobalParams(0);
+#else
+ globalParams = new GlobalParams();
+#endif
+ uMap = globalParams->getTextEncoding();
+
+ Object info;
+ if (doc->isOk())
+ {
+ doc->getDocInfo(&info);
+ if (info.isDict())
+ {
+ title = getInfoString(info.getDict(), "Title", uMap);
+ subject = getInfoString(info.getDict(), "Subject", uMap);
+ keywords = getInfoString(info.getDict(), "Keywords", uMap);
+ author = getInfoString(info.getDict(), "Author", uMap);
+ creator = getInfoString(info.getDict(), "Creator", uMap);
+ producer = getInfoString(info.getDict(), "Producer", uMap);
+ creationdate = getInfoDate( info.getDict(), "CreationDate" );
+ moddate = getInfoDate( info.getDict(), "ModDate" );
+ info.free();
+ }
+ const char *tagged = doc->getStructTreeRoot()->isDict() ? "yes" : "no";
+ int pages = doc->getNumPages();
+ const char *encrypted = doc->isEncrypted() ? "yes" : "no";
+ // printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
+ // doc->okToPrint(gTrue) ? "yes" : "no",
+ // doc->okToCopy(gTrue) ? "yes" : "no",
+ // doc->okToChange(gTrue) ? "yes" : "no",
+ // doc->okToAddNotes(gTrue) ? "yes" : "no");
+
+ // print linearization info
+ const char *optimized = doc->isLinearized() ? "yes" : "no";
+
+ // print PDF version
+#ifdef LIBPOPPLER_PDFDOC_HAS_PDFVERSION
+ float pdfversion = doc->getPDFVersion();
+#else
+ const double pdfversion = doc->getPDFMajorVersion() + 0.1 * doc->getPDFMinorVersion();
+#endif
+
+
+ // print page count
+ printf("Pages: %d\n", doc->getNumPages());
+
+ std::cout << "PDF Info:" << std::endl;
+ std::cout << " Title: " << title << std::endl;
+ std::cout << " Subject: " << subject << std::endl;
+ std::cout << " Keywords: " << keywords << std::endl;
+ std::cout << " Author: " << author << std::endl;
+ std::cout << " Creator: " << creator << std::endl;
+ std::cout << " Producer: " << producer << std::endl;
+ std::cout << " CreationDate: " << creationdate << std::endl;
+ std::cout << " ModDate: " << moddate << std::endl;
+ std::cout << " Tagged: " << tagged << std::endl;
+ std::cout << " Pages: " << pages << std::endl;
+ std::cout << " Encrypted: " << encrypted << std::endl;
+ //std::cout << "Page size: " << subject << std::endl;
+ std::cout << " File size: " << bv->GetLength() << std::endl;
+ std::cout << " Optimized: " << optimized << std::endl;
+ std::cout << " PDF version: " << pdfversion << std::endl;
+ }
+ else
+ {
+ std::cout << "Problem reading Encapsulated PDF " << std::endl;
+ }
+
+#else // GDCM_USE_SYSTEM_POPPLER
+ std::cout << " Encapsulated PDF File" << std::endl;
+#endif // GDCM_USE_SYSTEM_POPPLER
+ }
+ // Do the IOD verification !
+ //bool v = defs.Verify( file );
+ //std::cerr << "IOD Verification: " << (v ? "succeed" : "failed") << std::endl;
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c;
+ std::string filename;
+ std::string xmlpath;
+ int resourcespath = 0;
+ int verbose = 0;
+ int warning = 0;
+ int help = 0;
+ int recursive = 0;
+ int version = 0;
+ int debug = 0;
+ int error = 0;
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"recursive", 0, &recursive, 1},
+ {"check-deflated", 0, &deflated, 1},
+ {"resources-path", 0, &resourcespath, 1},
+ {"md5sum", 0, &md5sum, 1},
+ {"check-compression", 0, &checkcompression, 1},
+
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:rdVWDEhv";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 3 ) /* resources-path */
+ {
+ assert( strcmp(s, "resources-path") == 0 );
+ assert( xmlpath.empty() );
+ xmlpath = optarg;
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'r':
+ recursive = 1;
+ break;
+
+ case 'd':
+ deflated = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: %d", optind );
+ //while (optind < argc)
+ // {
+ // printf ("%s\n", argv[optind++]);
+ // }
+ //printf ("\n");
+ // Ok there is only one arg, easy, it's the filename:
+ int v = argc - optind;
+ if( v == 1 )
+ {
+ filename = argv[optind];
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( debug != 0);
+ gdcm::Trace::SetWarning( warning != 0);
+ gdcm::Trace::SetError( error != 0);
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( verbose != 0);
+ gdcm::Trace::SetError( verbose != 0);
+ }
+
+ if( !gdcm::System::FileExists(filename.c_str()) )
+ {
+ return 1;
+ }
+
+ gdcm::Global& g = gdcm::Global::GetInstance();
+ // First thing we need to locate the XML dict
+ // did the user requested to look XML file in a particular directory ?
+ if( !resourcespath )
+ {
+ const char *xmlpathenv = getenv("GDCM_RESOURCES_PATH");
+ if( xmlpathenv )
+ {
+ // Make sure to look for XML dict in user explicitly specified dir first:
+ xmlpath = xmlpathenv;
+ resourcespath = 1;
+ }
+ }
+ if( resourcespath )
+ {
+ // xmlpath is set either by the cmd line option or the env var
+ if( !g.Prepend( xmlpath.c_str() ) )
+ {
+ std::cerr << "specified Resources Path is not valid: " << xmlpath << std::endl;
+ return 1;
+ }
+ }
+
+ // All set, then load the XML files:
+ if( !g.LoadResourcesFiles() )
+ {
+ std::cerr << "Could not load XML file from specified path" << std::endl;
+ return 1;
+ }
+
+ const gdcm::Defs &defs = g.GetDefs();
+
+ int res = 0;
+ if( gdcm::System::FileIsDirectory(filename.c_str()) )
+ {
+ gdcm::Directory d;
+ d.Load(filename, recursive!= 0);
+ gdcm::Directory::FilenamesType const &filenames = d.GetFilenames();
+ for( gdcm::Directory::FilenamesType::const_iterator it = filenames.begin(); it != filenames.end(); ++it )
+ {
+ std::cout << "filename: " << *it << std::endl;
+ res += ProcessOneFile(*it, defs);
+ }
+ }
+ else
+ {
+ res += ProcessOneFile( filename, defs );
+ }
+
+
+ return res;
+}
diff --git a/gdcm/Applications/Cxx/gdcmkey.cxx b/gdcm/Applications/Cxx/gdcmkey.cxx
new file mode 100644
index 0000000..ab829d8
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmkey.cxx
@@ -0,0 +1,34 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+// DCKEY(1) DICOM PS3 - Extract attribute values DCKEY(1)
+//
+// NAME
+// dckey - ACR/NEMA DICOM PS3 ... Extract attribute values
+//
+// SYNOPSIS
+// dckey [ -v|verbose ] [ -describe ] [ -key|k attributename
+// ]
+//
+// DESCRIPTION
+// dckey reads the named dicom input file and displays the
+// values of the selected attributes.
+//
+// Binary attributes are written in hexadecimal with a pre-
+// ceding "0x". Numeric string attributes are written in dec-
+// imal.
+//
+int main(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmoverlay.cxx b/gdcm/Applications/Cxx/gdcmoverlay.cxx
new file mode 100644
index 0000000..120874b
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmoverlay.cxx
@@ -0,0 +1,192 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+
+#include "gdcmReader.h"
+#include "gdcmImageReader.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmPrinter.h"
+#include "gdcmDictPrinter.h"
+#include "gdcmValidate.h"
+#include "gdcmWriter.h"
+#include "gdcmDumper.h"
+#include "gdcmSystem.h"
+#include "gdcmDirectory.h"
+#include "gdcmOverlay.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+template
+int DoOperation(const std::string & filename)
+{
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ // Image part:
+ const gdcm::Image& img = reader.GetImage();
+ img.Print( std::cout );
+
+ // Overlay part:
+ unsigned int n = reader.GetImage().GetNumberOfOverlays();
+ std::cout << "Num of Overlays: " << n << std::endl;
+ for(unsigned int i = 0; i < n; ++i )
+ {
+ const gdcm::Overlay& o = reader.GetImage().GetOverlay(i);
+ o.Print( std::cout );
+ }
+
+ return 0;
+}
+
+
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string filename;
+ bool printdict = false;
+ bool verbose = false;
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"dict", 1, 0, 0},
+ {"verbose", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:dv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name;
+ printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ printf (" with arg %s", optarg);
+ }
+ printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ printf ("option o with value '%s'\n", optarg);
+ break;
+
+ case 'd':
+ //printf ("option d with value '%s'\n", optarg);
+ printdict = true;
+ break;
+
+ case 'v':
+ //printf ("option d with value '%s'\n", optarg);
+ verbose = true;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ {
+ printf ("%s ", argv[optind++]);
+ }
+ printf ("\n");
+ }
+
+ if( filename.empty() )
+ {
+ std::cerr << "Need input file (-i)\n";
+ return 1;
+ }
+ // else
+ int res = 0;
+ if( gdcm::System::FileIsDirectory( filename.c_str() ) )
+ {
+ gdcm::Directory d;
+ d.Load(filename);
+ gdcm::Directory::FilenamesType const &filenames = d.GetFilenames();
+ for( gdcm::Directory::FilenamesType::const_iterator it = filenames.begin(); it != filenames.end(); ++it )
+ {
+ if( printdict )
+ {
+ res += DoOperation(*it);
+ }
+ else
+ {
+ res += DoOperation(*it);
+ }
+ if( verbose ) std::cerr << *it << std::endl;
+ }
+ if( verbose ) std::cerr << "Total: " << filenames.size() << " files were processed" << std::endl;
+ }
+ else
+ {
+ assert( gdcm::System::FileExists(filename.c_str()) );
+ if( printdict )
+ {
+ res += DoOperation(filename);
+ }
+ else
+ {
+ res += DoOperation(filename);
+ }
+ // ...
+ if ( verbose )
+ std::cerr << "Filename: " << filename << std::endl;
+ }
+
+ return res;
+}
diff --git a/gdcm/Applications/Cxx/gdcmpap3.cxx b/gdcm/Applications/Cxx/gdcmpap3.cxx
new file mode 100644
index 0000000..00831aa
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmpap3.cxx
@@ -0,0 +1,622 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * Command line tool to deal with legacy PAPYRUS 3.0 file
+ * The command line tool can be compiled in two flavour:
+ *
+ * 1. without papyrus 3.0 (more portable)
+ * 2. with papyrus 3.0
+ *
+ * The (2) is only required when dealing with invalid JPEG Lossless compressed
+ * PAPYRUS 3.0 files
+ */
+#include "gdcmReader.h"
+#include "gdcmDirectionCosines.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmVersion.h"
+#include "gdcmWriter.h"
+#include "gdcmAttribute.h"
+#include "gdcmTrace.h"
+#include "gdcmImageHelper.h"
+#include "gdcmSequenceOfItems.h"
+
+#include
+
+#ifdef GDCM_USE_SYSTEM_PAPYRUS3
+extern "C" {
+#include
+}
+#endif
+
+static void PrintVersion()
+{
+ std::cout << "gdcmpap3: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmpap3 [OPTION] input.pa3 output.dcm" << std::endl;
+ std::cout << "Convert a PAPYRUS 3.0 file into another DICOM file.\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input PAPYRUS 3.0 filename" << std::endl;
+ std::cout << " -o --output DICOM filename" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -S --split Split single PAPYRUS 3.0 file into multiples DICOM files." << std::endl;
+ std::cout << " --decomp-pap3 Use PAPYRUS 3.0 for decompressing (can be combined with --split)." << std::endl;
+ std::cout << " --check-iop Check that the Image Orientation (Patient) Attribute is ok (see --split)." << std::endl;
+ std::cout << " --root-uid Specify Root UID." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+}
+
+static bool InitPapyrus3( const char *filename, int & outfileNb)
+{
+ outfileNb = -1;
+#ifdef GDCM_USE_SYSTEM_PAPYRUS3
+ /* initialisation of the Papyrus toolkit v3.6 */
+ Papy3Init();
+
+ /* open the pap3 file */
+ PapyShort fileNb = Papy3FileOpen ((char*)filename, (PAPY_FILE) 0, TRUE, 0);
+ if( fileNb < 0 )
+ {
+ PAPY3PRINTERRMSG();
+ return false;
+ }
+ outfileNb = fileNb;
+ return true;
+#else
+ (void)filename;
+ (void)outfileNb;
+ std::cerr << "No PAPYRUS 3.0 library found" << std::endl;
+ return false;
+#endif
+}
+
+static bool DecompressPapyrus3( int pap3handle, int itemnum, gdcm::TransferSyntax const & ts, gdcm::File & file )
+{
+#ifdef GDCM_USE_SYSTEM_PAPYRUS3
+ PapyShort fileNb = (PapyShort)pap3handle;
+ PapyShort imageNb = (PapyShort)(itemnum + 1);
+
+ if( ts == gdcm::TransferSyntax::JPEGLosslessProcess14_1 )
+ {
+ SElement *group;
+ PapyUShort *theImage;
+
+ std::vector dims = gdcm::ImageHelper::GetDimensionsValue(file);
+ gdcm::PixelFormat pf = gdcm::ImageHelper::GetPixelFormatValue(file);
+
+ gdcm::DataSet & nested = file.GetDataSet();
+
+ /* position the file pointer to the begining of the data set */
+ PapyShort err = Papy3GotoNumber (fileNb, (PapyShort)imageNb, DataSetID);
+
+ gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
+
+ /* then goto group 0x7FE0 */
+ if ((err = Papy3GotoGroupNb (fileNb, 0x7FE0)) == 0)
+ {
+ /* read group 0x7FE0 from the file */
+ if ((err = Papy3GroupRead (fileNb, &group)) > 0)
+ {
+ /* PIXEL DATA */
+ theImage = (PapyUShort *)Papy3GetPixelData (fileNb, imageNb, group, ImagePixel);
+
+ //assert( dims[0] == 512 );
+ //assert( dims[1] == 512 );
+ //assert( pf.GetPixelSize() == 2 );
+ const size_t imglen = dims[0] * dims[1] * pf.GetPixelSize();
+ pixeldata.SetByteValue( (char*)theImage, (uint32_t)imglen );
+
+ /* free group 7FE0 */
+ err = Papy3GroupFree (&group, TRUE);
+ } /* endif ...group 7FE0 read */
+ else
+ {
+ PAPY3PRINTERRMSG ();
+ }
+ } /* endif ...group 7FE0 found */
+ else
+ {
+ PAPY3PRINTERRMSG ();
+ }
+ nested.Replace( pixeldata );
+ }
+ else
+ {
+ std::cerr << "TransferSyntax: " << ts << " is not handled at this point" << std::endl;
+ return false;
+ }
+ return true;
+#else
+ (void)pap3handle; (void)itemnum; (void)ts; (void)file;
+ std::cerr << "No PAPYRUS 3.0 library found" << std::endl;
+ return false;
+#endif
+}
+
+static bool CleanupPapyrus3( int pap3handle )
+{
+#ifdef GDCM_USE_SYSTEM_PAPYRUS3
+ PapyShort fileNb = (PapyShort)pap3handle;
+ /* close and free the file and the associated allocated memory */
+ Papy3FileClose (fileNb, TRUE);
+
+ /* free the allocated global value in the toolkit */
+ Papy3FreeDataSetModules ();
+
+ return true;
+#else
+ (void)pap3handle;
+ return false;
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+
+ std::string filename;
+ std::string outfilename;
+ std::string root;
+ int rootuid = 0;
+ int split = 0;
+ int decomp_pap3 = 0;
+ int check_iop = 0;
+
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
+ {"split", 0, &split, 1},
+ {"decomp-pap3", 0, &decomp_pap3, 1},
+ {"check-iop", 0, &check_iop, 1},
+
+// General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:S:VWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 1 ) /* output */
+ {
+ assert( strcmp(s, "output") == 0 );
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ }
+ else if( option_index == 2 ) /* root-uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ root = optarg;
+ }
+ //printf (" with arg %s, index = %d", optarg, option_index);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ //printf ("option o with value '%s'\n", optarg);
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ //
+ case 'S':
+ split = 1;
+ break;
+
+ // General option
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ // For now only support one input / one output
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: ");
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() == 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0];
+ outfilename = files[1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+ if( outfilename.empty() )
+ {
+ //std::cerr << "Need output file (-o)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmpap3" );
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ // root is set either by the cmd line option or the env var
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Could not read: " << filename << std::endl;
+ return 1;
+ }
+
+ gdcm::File & file = reader.GetFile();
+ gdcm::FileMetaInformation & header = file.GetHeader();
+ gdcm::DataSet & ds = file.GetDataSet();
+
+ gdcm::MediaStorage ms = header.GetMediaStorage(); (void)ms;
+ const gdcm::TransferSyntax & ts = header.GetDataSetTransferSyntax();
+ //std::cout << ts << std::endl;
+ std::string msstr = header.GetMediaStorageAsString();
+ //std::cout << msstr << std::endl;
+
+ int pap3handle;
+ if( decomp_pap3 )
+ {
+ if( !InitPapyrus3( filename.c_str(), pap3handle ) )
+ {
+ std::cerr << "Problem during init of PAPYRUS 3.0. File was: " << filename << std::endl;
+ return 1;
+ }
+ }
+
+ gdcm::PrivateTag pt(0x0041,0x50,"PAPYRUS 3.0");
+ const gdcm::DataElement &depap = ds.GetDataElement( pt );
+ gdcm::SmartPointer sq = depap.GetValueAsSQ();
+
+ if( !split )
+ {
+ gdcm::Writer w;
+ w.CheckFileMetaInformationOff();
+ w.SetFileName( outfilename.c_str() );
+ w.SetFile( reader.GetFile() );
+
+ if( decomp_pap3 )
+ {
+ gdcm::TransferSyntax outts = ts;
+ for( gdcm::SequenceOfItems::SizeType i = 0; i < sq->GetNumberOfItems(); ++i )
+ {
+ gdcm::Item & it = sq->GetItem( i + 1 );
+ gdcm::DataSet & nested = it.GetNestedDataSet();
+ gdcm::File f;
+ f.SetDataSet( nested );
+ if( !DecompressPapyrus3( pap3handle, i, ts, f ) )
+ {
+ std::cerr << "Could not decompress frame #" << i << " from file: " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::DataElement & pixeldata = f.GetDataSet().GetDataElement( gdcm::Tag(0x7fe0,0x0010) );
+ nested.Replace( pixeldata );
+ }
+
+ // make sq as undefined length (avoid length computation):
+ gdcm::DataElement de_dup = depap;
+ de_dup.SetValue( *sq );
+ de_dup.SetVLToUndefined();
+ ds.Replace( de_dup );
+
+ gdcm::FileMetaInformation & h = w.GetFile().GetHeader();
+ // pap3 returns image as decompressed:
+ outts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
+ gdcm::Attribute<0x0002, 0x0010> TransferSyntaxUID;
+ const char *tsstr = gdcm::TransferSyntax::GetTSString( outts );
+ TransferSyntaxUID.SetValue( tsstr );
+ h.Replace( TransferSyntaxUID.GetAsDataElement() );
+ gdcm::Attribute<0x0002, 0x0000> filemetagrouplength;
+ h.Remove( filemetagrouplength.GetTag() ); // important
+ unsigned int glen = h.GetLength();
+ filemetagrouplength.SetValue( glen );
+ h.Insert( filemetagrouplength.GetAsDataElement() );
+ }
+
+ if( !w.Write() )
+ {
+ std::cerr << "Could not write output file: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+ else
+ {
+ if( !gdcm::System::FileIsDirectory(outfilename.c_str()) )
+ {
+ std::cerr << "Output is not a directory: " << outfilename << std::endl;
+ return 1;
+ }
+#if 1
+ gdcm::UIDGenerator uid;
+
+ const std::string seriesstr = uid.Generate();
+
+ for( gdcm::SequenceOfItems::SizeType i = 0; i < sq->GetNumberOfItems(); ++i )
+ {
+ gdcm::Item & it = sq->GetItem( i + 1 );
+ gdcm::DataSet & nested = it.GetNestedDataSet();
+
+ std::stringstream ss;
+ ss << outfilename;
+ ss << "/IMG";
+ ss << std::setw(4) << std::setfill( '0') << i;
+ ss << ".dcm";
+ gdcm::Writer w;
+ // 1.2.840.10008.1.2.4.70
+ w.CheckFileMetaInformationOn();
+ const std::string & outfn = ss.str();
+ w.SetFileName( outfn.c_str() );
+ gdcm::TransferSyntax outts;
+#ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION
+ if( ts == gdcm::TransferSyntax::WeirdPapryus )
+ {
+ outts = gdcm::TransferSyntax::ImplicitVRLittleEndian;
+ }
+ else
+#endif
+ {
+ outts = ts;
+ }
+
+ w.GetFile().SetDataSet( nested );
+
+ if( decomp_pap3 )
+ {
+ if( !DecompressPapyrus3( pap3handle, i, ts, w.GetFile() ) )
+ {
+ std::cerr << "Could not decompress frame #" << i << " from file: " << filename << std::endl;
+ return 1;
+ }
+ // pap3 returns image as decompressed:
+ outts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
+ }
+ w.GetFile().GetHeader().SetDataSetTransferSyntax( outts );
+
+ if( check_iop )
+ {
+ bool erroriop = false;
+ std::vector iop_orig;
+ iop_orig.resize( 6 );
+ // gdcm::ImageHelper::GetDirectionCosinesValue( w.GetFile() );
+ if( !gdcm::ImageHelper::GetDirectionCosinesFromDataSet(w.GetFile().GetDataSet(), iop_orig) )
+ {
+ erroriop = true;
+ gdcm::DirectionCosines dc( &iop_orig[0] );
+ assert( !dc.IsValid() );
+ {
+ gdcm::Attribute<0x0008,0x0008> imagetype;
+ imagetype.Set( w.GetFile().GetDataSet() );
+ if( imagetype.GetNumberOfValues() > 2 )
+ {
+ const std::string &str = imagetype.GetValue( 2 );
+ gdcm::Attribute<0x0020,0x0037> at_axial = {{1,0,0,0,1,0}}; // default value for AXIAL
+ if( str == "AXIAL" )
+ {
+ w.GetFile().GetDataSet().Replace( at_axial.GetAsDataElement() );
+ erroriop = false; // error has been corrected
+ }
+ else if( str == "LOCALIZER" )
+ {
+ static const double fake_axial[] = { 1, 0, 0, 0, 0, 0 };
+ assert( memcmp( &iop_orig[0], fake_axial, 6 * sizeof( double ) ) == 0 ); (void)fake_axial;
+ w.GetFile().GetDataSet().Replace( at_axial.GetAsDataElement() );
+ erroriop = false; // error has been corrected
+ }
+ }
+ assert( !erroriop ); // did our heuristic failed ?
+ }
+ }
+ if( erroriop )
+ {
+ std::cerr << "Error IOP (could not correct) for frame #" << i << " value : ("
+ << iop_orig[0] << ","
+ << iop_orig[1] << ","
+ << iop_orig[2] << ","
+ << iop_orig[3] << ","
+ << iop_orig[4] << ","
+ << iop_orig[5] << ")" << std::endl;
+ return 1;
+ }
+ }
+
+#if 0
+ gdcm::Attribute<0x0008,0x0016> outms;
+ outms.SetValue( "1.2.840.10008.5.1.4.1.1.2" );
+ nested.Replace( outms.GetAsDataElement() );
+
+ gdcm::Attribute<0x028,0x0102> highbits = { 15 };
+ nested.Replace( highbits.GetAsDataElement() );
+
+ gdcm::Attribute<0x0028,0x0030> pixelspacing = { 0.57, 0.57 };
+ nested.Replace( pixelspacing.GetAsDataElement() );
+
+ gdcm::Attribute<0x0020,0x000e> seriesuid;
+ seriesuid.SetValue( seriesstr );
+ nested.Insert( seriesuid.GetAsDataElement() ); // do not replace if exists
+
+ gdcm::Attribute<0x0008,0x0018> instanceuid;
+ instanceuid.SetValue( uid.Generate() );
+ nested.Replace( instanceuid.GetAsDataElement() );
+
+ // ???
+ gdcm::Attribute<0x0020,0x0032> ipp = {{0,0, i * 0.57}}; // default value
+ nested.Replace( ipp.GetAsDataElement() );
+
+ gdcm::Attribute<0x0020,0x0037> iop = {{1,0,0,0,1,0}}; // default value
+ nested.Replace( iop.GetAsDataElement() );
+#endif
+
+ //std::cout << w.GetFile().GetDataSet( ) << std::endl;
+ if( !w.Write() )
+ {
+ std::cerr << "Problem writing output file: " << outfn << std::endl;
+ return 1;
+ }
+ }
+#endif
+ }
+
+ if( decomp_pap3 )
+ {
+ if( !CleanupPapyrus3( pap3handle ) )
+ {
+ std::cerr << "Problem during PAPYRUS 3.0 cleanup" << std::endl;
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmpdf.cxx b/gdcm/Applications/Cxx/gdcmpdf.cxx
new file mode 100644
index 0000000..4c7a7bb
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmpdf.cxx
@@ -0,0 +1,666 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ */
+#include "gdcmVersion.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmWriter.h"
+#include "gdcmAttribute.h"
+#include "gdcmSystem.h"
+
+#ifdef GDCM_USE_SYSTEM_POPPLER
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+static std::string getInfoDate(Dict *infoDict, const char *key)
+{
+ Object obj;
+ char *s;
+ int year, mon, day, hour, min, sec, n;
+ struct tm tmStruct;
+ //char buf[256];
+ std::string out;
+
+ if (infoDict->lookup((char*)key, &obj)->isString())
+ {
+ s = obj.getString()->getCString();
+ if (s[0] == 'D' && s[1] == ':')
+ {
+ s += 2;
+ }
+ if ((n = sscanf(s, "%4d%2d%2d%2d%2d%2d",
+ &year, &mon, &day, &hour, &min, &sec)) >= 1)
+ {
+ switch (n)
+ {
+ case 1: mon = 1;
+ case 2: day = 1;
+ case 3: hour = 0;
+ case 4: min = 0;
+ case 5: sec = 0;
+ }
+ tmStruct.tm_year = year - 1900;
+ tmStruct.tm_mon = mon - 1;
+ tmStruct.tm_mday = day;
+ tmStruct.tm_hour = hour;
+ tmStruct.tm_min = min;
+ tmStruct.tm_sec = sec;
+ tmStruct.tm_wday = -1;
+ tmStruct.tm_yday = -1;
+ tmStruct.tm_isdst = -1;
+/*
+ // compute the tm_wday and tm_yday fields
+ if (mktime(&tmStruct) != (time_t)-1 &&
+ strftime(buf, sizeof(buf), "%c", &tmStruct)) {
+ fputs(buf, stdout);
+ } else {
+ fputs(s, stdout);
+ }
+ } else {
+ fputs(s, stdout);
+*/
+ }
+ //fputc('\n', stdout);
+ char date[22];
+ time_t t = mktime(&tmStruct);
+ if( t != -1 )
+ {
+ if( gdcm::System::FormatDateTime(date, t) )
+ out = date;
+ }
+ }
+ obj.free();
+ return out;
+}
+
+static std::string getInfoString(Dict *infoDict, const char *key, UnicodeMap *uMap, GBool & unicode)
+{
+ Object obj;
+ GooString *s1;
+ GBool isUnicode = gFalse;
+ Unicode u;
+ char buf[8];
+ int i, n;
+ std::string out;
+
+ if (infoDict->lookup((char*)key, &obj)->isString())
+ {
+ s1 = obj.getString();
+ if ((s1->getChar(0) & 0xff) == 0xfe &&
+ (s1->getChar(1) & 0xff) == 0xff)
+ {
+ isUnicode = gTrue;
+ i = 2;
+ }
+ else
+ {
+ isUnicode = gFalse;
+ i = 0;
+ }
+ while (i < obj.getString()->getLength())
+ {
+ if (isUnicode)
+ {
+ u = ((s1->getChar(i) & 0xff) << 8) |
+ (s1->getChar(i+1) & 0xff);
+ i += 2;
+ }
+ else
+ {
+ u = pdfDocEncoding[s1->getChar(i) & 0xff];
+ ++i;
+ }
+ n = uMap->mapUnicode(u, buf, sizeof(buf));
+ //fwrite(buf,1,n,stdout);
+ out.append( std::string(buf, n) );
+ }
+ }
+ obj.free();
+ unicode = unicode || isUnicode;
+ return out;
+}
+
+static void PrintVersion()
+{
+ std::cout << "gdcmpdf: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmpdf [OPTION]... FILE..." << std::endl;
+ std::cout << "Convert a PDF file to DICOM/PDF\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input PDF filename" << std::endl;
+ std::cout << " -o --output DICOM filename" << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+}
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string filename;
+ std::string outfilename;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+/*
+ struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+*/
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:o:VWDEhv";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: %d", optind );
+ //while (optind < argc)
+ // {
+ // printf ("%s\n", argv[optind++]);
+ // }
+ //printf ("\n");
+ int v = argc - optind;
+ if( v == 2 )
+ {
+ filename = argv[optind];
+ outfilename = argv[optind+1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+ if( filename.empty() || outfilename.empty() )
+ {
+ PrintHelp();
+ return 1;
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ GooString *ownerPW, *userPW;
+ GooString *fileName;
+ PDFDoc *doc;
+ Object info;
+ UnicodeMap *uMap;
+ ownerPW = NULL;
+ userPW = NULL;
+#ifdef LIBPOPPLER_GLOBALPARAMS_CSTOR_HAS_PARAM
+ globalParams = new GlobalParams(0);
+#else
+ globalParams = new GlobalParams();
+#endif
+ uMap = globalParams->getTextEncoding();
+
+ //const char *filename = argv[1];
+ if( !gdcm::System::FileExists(filename.c_str()) )
+ {
+ return 1;
+ }
+ // get length of file:
+ size_t length = gdcm::System::FileSize(filename.c_str());
+ // PDF doc is stored in an OB element, check that 32bits length is fine:
+ if( length > gdcm::VL::GetVL32Max() )
+ {
+ return 1;
+ }
+
+ //const char *outfilename = argv[2];
+ fileName = new GooString( filename.c_str() );
+ //ownerPW = new GooString( "toto" );
+ Object obj;
+
+ obj.initNull();
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+
+ if (doc->isEncrypted())
+ {
+ std::string password;
+ std::cout << "Enter password:" << std::endl;
+ // http://www.daniweb.com/code/snippet1174.html
+ std::cin >> password;
+ //std::cout << "Enter password:" << password << std::endl;
+/*
+#include
+#include
+
+int mygetch(void)
+{
+struct termios oldt,
+newt;
+int ch;
+tcgetattr( STDIN_FILENO, &oldt );
+newt = oldt;
+newt.c_lflag &= ~( ICANON | ECHO );
+tcsetattr( STDIN_FILENO, TCSANOW, &newt );
+ch = getchar();
+tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
+return ch;
+
+http://msdn.microsoft.com/en-us/library/078sfkak(VS.80).aspx
+}
+ */
+ ownerPW = new GooString( password.c_str() );
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ }
+
+ std::string title;
+ std::string subject;
+ std::string keywords;
+ std::string author;
+ std::string creator;
+ std::string producer;
+ std::string creationdate;
+ std::string moddate;
+
+ GBool isUnicode = gFalse;
+ if (doc->isOk())
+ {
+ doc->getDocInfo(&info);
+ if (info.isDict())
+ {
+ title = getInfoString(info.getDict(), "Title", uMap, isUnicode);
+ subject = getInfoString(info.getDict(), "Subject", uMap, isUnicode);
+ keywords = getInfoString(info.getDict(), "Keywords", uMap, isUnicode);
+ author = getInfoString(info.getDict(), "Author", uMap, isUnicode);
+ creator = getInfoString(info.getDict(), "Creator", uMap, isUnicode);
+ producer = getInfoString(info.getDict(), "Producer", uMap, isUnicode);
+ creationdate = getInfoDate( info.getDict(), "CreationDate" );
+ moddate = getInfoDate( info.getDict(), "ModDate" );
+ info.free();
+ }
+ }
+
+ gdcm::Writer writer;
+ gdcm::DataSet &ds = writer.GetFile().GetDataSet();
+{
+ gdcm::DataElement de( gdcm::Tag(0x42,0x11) );
+ de.SetVR( gdcm::VR::OB );
+ std::ifstream is;
+ is.open (filename.c_str(), std::ios::binary );
+
+ char *buffer = new char [length];
+
+ // read data as a block:
+ is.read (buffer,length);
+ is.close();
+
+ de.SetByteValue( buffer, (uint32_t)length );
+ delete[] buffer;
+
+ gdcm::FileMetaInformation &fmi = writer.GetFile().GetHeader();
+ gdcm::TransferSyntax ts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
+ fmi.SetDataSetTransferSyntax( ts );
+ ds.Insert( de );
+}
+
+
+{
+ char date[22];
+ const size_t datelen = 8;
+ int res = gdcm::System::GetCurrentDateTime(date);
+ if( !res ) return false;
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0020) );
+ // Do not copy the whole cstring:
+ de.SetByteValue( date, datelen );
+ de.SetVR( gdcm::Attribute<0x0008,0x0020>::GetVR() );
+ ds.Insert( de );
+ }
+ // StudyTime
+ const size_t timelen = 6 + 1 + 6; // time + milliseconds
+ (void)timelen;
+ {
+ gdcm::Attribute<0x8,0x30> at;
+ at.SetValue( date+datelen );
+ ds.Insert( at.GetAsDataElement() );
+ //gdcm::DataElement de( gdcm::Tag(0x0008,0x0030) );
+ // Do not copy the whole cstring:
+ //de.SetByteValue( date+datelen, timelen );
+ //de.SetVR( gdcm::Attribute<0x0008,0x0030>::GetVR() );
+ //ds.Insert( de );
+ }
+
+}
+
+ gdcm::UIDGenerator uid;
+{
+ const char *sop = uid.Generate();
+ gdcm::DataElement de( gdcm::Tag(0x0008,0x0018) );
+ de.SetByteValue( sop, (uint32_t)strlen(sop) );
+ de.SetVR( gdcm::Attribute<0x0008, 0x0018>::GetVR() );
+ ds.Insert( de );
+}
+
+ gdcm::MediaStorage ms = gdcm::MediaStorage::EncapsulatedPDFStorage;
+ {
+ gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016) );
+ const char* msstr = gdcm::MediaStorage::GetMSString(ms);
+ de.SetByteValue( msstr, (uint32_t)strlen(msstr) );
+ de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() );
+ ds.Insert( de );
+ }
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmpdf" );
+
+ char date[22];
+ const size_t datelen = 8;
+ bool b = gdcm::System::GetCurrentDateTime(date); (void)b;
+ //std::cout << date << std::endl;
+
+{
+ gdcm::Attribute<0x0008, 0x0005> at;
+ const char s[] = "ISO_IR 100";
+ const char s_unicode[] = "ISO_IR 192";
+ at.SetNumberOfValues( 1 );
+ if( isUnicode )
+ at.SetValue( s_unicode );
+ else
+ at.SetValue( s );
+ ds.Insert( at.GetAsDataElement() );
+}
+{
+ gdcm::Attribute<0x0008, 0x0012> at;
+ std::string tmp( date, datelen );
+ at.SetValue( tmp.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+
+ const size_t timelen = 6 + 1 + 6; // TM + milliseconds
+{
+ gdcm::Attribute<0x0008, 0x0013> at;
+ std::string tmp( date+datelen, timelen);
+ at.SetValue( tmp.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0020) DA (no value available) # 0, 0 StudyDate
+{
+ gdcm::Attribute<0x0008, 0x0020> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0023) DA (no value available) # 0, 0 ContentDate
+{
+ gdcm::Attribute<0x0008, 0x0023> at;
+ std::string tmp( creationdate.c_str(), datelen );
+ at.SetValue( tmp.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,002a) DT (no value available) # 0, 0 AcquisitionDatetime
+{
+ gdcm::Attribute<0x0008, 0x002a> at;
+ time_t studydatetime = gdcm::System::FileTime( filename.c_str() );
+ char date2[22];
+ gdcm::System::FormatDateTime(date2, studydatetime);
+ at.SetValue( date2 );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0030) TM (no value available) # 0, 0 StudyTime
+{
+ gdcm::Attribute<0x0008, 0x0030> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0033) TM (no value available) # 0, 0 ContentTime
+{
+ gdcm::Attribute<0x0008, 0x0033> at;
+ std::string tmp( creationdate.c_str() + datelen, timelen);
+ at.SetValue( tmp.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0050) SH (no value available) # 0, 0 AccessionNumber
+{
+ gdcm::Attribute<0x0008, 0x0050> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0060) CS [DOC] # 2, 1 Modality
+{
+ gdcm::Attribute<0x0008, 0x0060> at;
+ at.SetValue( "DOC " );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0064) CS [WSD] # 4, 1 ConversionType
+{
+ gdcm::Attribute<0x0008, 0x0064> at;
+ at.SetValue( "WSD" );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0070) LO (no value available) # 0, 0 Manufacturer
+{
+ gdcm::Attribute<0x0008, 0x0070> at;
+ at.SetValue( creator.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0008,0090) PN (no value available) # 0, 0 ReferringPhysiciansName
+{
+ gdcm::Attribute<0x0008, 0x0090> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+
+// In past DICOM implementation there used to be those neat tags:
+// (0088,0904) Topic Title TopicTitle LO 1 RET
+// (0088,0906) Topic Subject TopicSubject ST 1 RET
+// (0088,0910) Topic Author TopicAuthor LO 1 RET
+// (0088,0912) Topic Keywords TopicKeywords LO 1-32 RET
+// However they are now deprecated...
+
+//(0010,0010) PN (no value available) # 0, 0 PatientsName
+{
+ gdcm::Attribute<0x0010, 0x0010> at;
+ at.SetValue( author.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0010,0020) LO (no value available) # 0, 0 PatientID
+{
+ gdcm::Attribute<0x0010, 0x0020> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0010,0030) DA (no value available) # 0, 0 PatientsBirthDate
+{
+ gdcm::Attribute<0x0010, 0x0030> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0010,0040) CS (no value available) # 0, 0 PatientsSex
+{
+ gdcm::Attribute<0x0010, 0x0040> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+{
+ gdcm::Attribute<0x0018, 0x1020> at;
+ at.SetNumberOfValues( 1 );
+ at.SetValue( producer.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0020,000d) UI [1.2.276.0.7230010.3.1.4.8323329.511.1228064157.1] # 48, 1 StudyInstanceUID
+{
+ gdcm::Attribute<0x0020, 0x000d> at;
+ at.SetValue( uid.Generate() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0020,000e) UI [1.2.276.0.7230010.3.1.4.8323329.511.1228064157.2] # 48, 1 SeriesInstanceUID
+{
+ gdcm::Attribute<0x0020, 0x000e> at;
+ at.SetValue( uid.Generate() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0020,0010) SH (no value available) # 0, 0 StudyID
+{
+ gdcm::Attribute<0x0020, 0x0010> at;
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0020,0011) IS [1] # 2, 1 SeriesNumber
+{
+ gdcm::Attribute<0x0020, 0x0011> at = { 1 };
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0020,0013) IS [1] # 2, 1 InstanceNumber
+{
+ gdcm::Attribute<0x0020, 0x0013> at = { 1 };
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0028,0301) CS [YES] # 4, 1 BurnedInAnnotation
+{
+ gdcm::Attribute<0x0028, 0x0301> at;
+ at.SetValue( "YES" );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0040,a043) SQ (Sequence with explicit length #=0) # 0, 1 ConceptNameCodeSequence
+//(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
+{
+ gdcm::Attribute<0x0040, 0xa043> at;
+ gdcm::DataElement de( at.GetTag() );
+ de.SetVR( at.GetVR() );
+ //ds.Insert( at.GetAsDataElement() );
+ ds.Insert( de );
+}
+//(0042,0010) ST (no value available) # 0, 0 DocumentTitle
+{
+ gdcm::Attribute<0x0042, 0x0010> at;
+ at.SetValue( title.c_str() );
+ ds.Insert( at.GetAsDataElement() );
+}
+//(0042,0011) OB 25\50\44\46\2d\31\2e\34\0a\25\e7\f3\cf\d3\0a\32\34\35\38\38\20\30... # 6861900, 1 EncapsulatedDocument
+//(0042,0012) LO [application/pdf] # 16, 1 MIMETypeOfEncapsulatedDocument
+{
+ gdcm::Attribute<0x0042, 0x0012> at;
+ at.SetValue( "application/pdf" );
+ ds.Insert( at.GetAsDataElement() );
+}
+
+
+ writer.SetFileName( outfilename.c_str() );
+ if( !writer.Write() )
+ {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmraw.cxx b/gdcm/Applications/Cxx/gdcmraw.cxx
new file mode 100644
index 0000000..594d71b
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmraw.cxx
@@ -0,0 +1,413 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * gdcmraw - ACR/NEMA DICOM PS3 ... DICOM PS3 - DICOM image to raw file
+ * Synopsis:
+ * gdcmraw [ -t | --tag Tag# (default: 07fe,0010) ] -i inputfile
+ * Description:
+ * gdcmraw
+ * reads the named dicom or acr-nema input file and copies the raw image
+ * pixel data to a raw binary file without a header of any kind.
+ * The byte order, packing or encapsulation of the raw result is dependent
+ * only on the encoding of the input file and cannot be changed.
+*/
+
+#include "gdcmReader.h"
+#include "gdcmImageReader.h"
+#include "gdcmImage.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmTag.h"
+#include "gdcmByteValue.h"
+#include "gdcmSequenceOfFragments.h"
+#include "gdcmFragment.h"
+#include "gdcmFilename.h"
+#include "gdcmFilenameGenerator.h"
+#include "gdcmVersion.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+static void PrintVersion()
+{
+ std::cout << "gdcmraw: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmraw [OPTION]... FILE..." << std::endl;
+ std::cout << "Extract Data Element Value Field" << std::endl;
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input DICOM filename" << std::endl;
+ std::cout << " -o --output DICOM filename" << std::endl;
+ std::cout << " -t --tag Specify tag to extract value from." << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -S --split-frags Split fragments into multiple files." << std::endl;
+ std::cout << " -p --pattern Specify trailing file pattern (see split-frags)." << std::endl;
+ std::cout << " -P --pixel-data Pixel Data trailing 0." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ gdcm::Tag rawTag(0x7fe0, 0x0010); // Default to Pixel Data
+ std::string filename;
+ std::string outfilename;
+ std::string pattern;
+ int splitfrags = 0;
+ int pixeldata = 0;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0}, // i
+ {"output", 1, 0, 0}, // o
+ {"tag", 1, 0, 0}, // t
+ {"split-frags", 0, &splitfrags, 1}, // f
+/*
+ * pixel-data flag is important for image like DermaColorLossLess.dcm since the bytevalue is
+ * 63532, because of the DICOM \0 padding, but we would rather have the image buffer instead
+ * which is simply one byte shorter, so add a special flag that simply mimic what TestImageReader
+ * would expect
+ */
+ {"pixel-data", 0, &pixeldata, 1}, // P
+ {"pattern", 1, 0, 0}, // p
+
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:t:Sp:PVWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 2 ) /* tag */
+ {
+ assert( strcmp(s, "tag") == 0 );
+ rawTag.ReadFromCommaSeparatedString(optarg);
+ }
+ else if( option_index == 5 ) /* pattern */
+ {
+ assert( strcmp(s, "pattern") == 0 );
+ assert( pattern.empty() );
+ pattern = optarg;
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ //printf ("option o with value '%s'\n", optarg);
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ case 'P':
+ pixeldata = 1;
+ break;
+
+ case 'S':
+ splitfrags = 1;
+ break;
+
+ case 'p':
+ assert( pattern.empty() );
+ pattern = optarg;
+ break;
+
+ case 't':
+ //printf ("option t with value '%s'\n", optarg);
+ rawTag.ReadFromCommaSeparatedString(optarg);
+ //std::cerr << rawTag << std::endl;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() == 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0];
+ outfilename = files[1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( debug != 0);
+ gdcm::Trace::SetWarning( warning != 0);
+ gdcm::Trace::SetError( error != 0);
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( verbose != 0);
+ gdcm::Trace::SetError( verbose!= 0);
+ }
+
+ // else
+ //std::cout << "Filename: " << filename << std::endl;
+
+ // very special option, handle it first:
+ if( pixeldata )
+ {
+ if( rawTag != gdcm::Tag(0x7fe0,0x0010) )
+ {
+ return 1;
+ }
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+ const gdcm::Image& image = reader.GetImage();
+ unsigned long len = image.GetBufferLength();
+ char * buf = new char[len];
+ image.GetBuffer( buf );
+
+ std::ofstream output(outfilename.c_str(), std::ios::binary);
+ output.write( buf, len );
+
+ delete[] buf;
+ return 0;
+ }
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "Failed to read: " << filename << std::endl;
+ return 1;
+ }
+
+ //const gdcm::FileMetaInformation &h = reader.GetFile().GetHeader();
+ //std::cout << h << std::endl;
+
+ const gdcm::DataSet &ds = reader.GetFile().GetDataSet();
+ //std::cout << ds << std::endl;
+
+ if( !ds.FindDataElement( rawTag ) )
+ {
+ std::cerr << "Cannot find Tag: " << rawTag << std::endl;
+ return 1;
+ }
+
+ if( outfilename.empty() )
+ {
+ std::cerr << "Need output file (-o)\n";
+ return 1;
+ }
+ gdcm::Filename fn1(filename.c_str()), fn2(outfilename.c_str());
+ if( fn1.IsIdentical(fn2) )
+ {
+ std::cerr << "Output is Input\n";
+ return 1;
+ }
+
+ const gdcm::DataElement& pdde = ds.GetDataElement( rawTag );
+ const gdcm::ByteValue *bv = pdde.GetByteValue();
+ const gdcm::SequenceOfFragments *sf = pdde.GetSequenceOfFragments();
+ if( bv )
+ {
+ std::ofstream output(outfilename.c_str(), std::ios::binary);
+ bv->WriteBuffer(output);
+ }
+ else if( sf )
+ {
+ if( splitfrags )
+ {
+ size_t nfrags = sf->GetNumberOfFragments();
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( nfrags );
+ fg.SetPrefix( outfilename.c_str() );
+ fg.SetPattern( pattern.c_str() );
+ if(!fg.Generate())
+ {
+ std::cerr << "Could not generate" << std::endl;
+ return 1;
+ }
+ for(unsigned int i = 0; i < nfrags; ++i)
+ {
+ const gdcm::Fragment& frag = sf->GetFragment(i);
+ const gdcm::ByteValue *fragbv = frag.GetByteValue();
+ const char *outfilenamei = fg.GetFilename(i);
+ std::ofstream outputi(outfilenamei, std::ios::binary);
+ fragbv->WriteBuffer(outputi);
+ }
+ }
+ else
+ {
+ std::ofstream output(outfilename.c_str(), std::ios::binary);
+ sf->WriteBuffer(output);
+ }
+ }
+ else
+ {
+ const gdcm::Value &value = pdde.GetValue();
+ const gdcm::Value * v = &value;
+ const gdcm::SequenceOfItems *sqi = dynamic_cast( v );
+ if( sqi )
+ {
+ //std::ofstream output(outfilename.c_str(), std::ios::binary);
+ //sqi->Write(output);
+ size_t nfrags = sqi->GetNumberOfItems();
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( nfrags );
+ fg.SetPrefix( outfilename.c_str() );
+ fg.SetPattern( pattern.c_str() );
+ if(!fg.Generate())
+ {
+ std::cerr << "Could not generate" << std::endl;
+ return 1;
+ }
+ for(unsigned int i = 0; i < nfrags; ++i)
+ {
+ const gdcm::Item& frag = sqi->GetItem(i+1);
+ const gdcm::DataSet &subds = frag.GetNestedDataSet();
+ const char *outfilenamei = fg.GetFilename(i);
+ std::ofstream outputi(outfilenamei, std::ios::binary);
+ // Let's imagine we found an undefined length Pixel Data attribute in
+ // this sequence. Let's pick ExplicitDataElement for writing out then
+ subds.Write(outputi);
+ }
+
+ }
+ else
+ {
+ std::cerr << "Unhandled" << std::endl;
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmscanner.cxx b/gdcm/Applications/Cxx/gdcmscanner.cxx
new file mode 100644
index 0000000..6b42222
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmscanner.cxx
@@ -0,0 +1,283 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * a Scanner application
+ * Usage:
+ *
+ * $ gdcmscanner -d /images/ -t 0020,000d -t 0020,000e
+ *
+ * Options:
+ * -d : directory
+ * -t : tag (can be specified multiple times)
+ * -p : print
+ * -r : recursive (enter subdir of main directory)
+ *
+ * TODO:
+ * --bench...
+ */
+
+#include "gdcmScanner.h"
+#include "gdcmTrace.h"
+#include "gdcmVersion.h"
+#include "gdcmSimpleSubjectWatcher.h"
+
+#include
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+static void PrintVersion()
+{
+ std::cout << "gdcmscanner: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmscanner [OPTION] -d directory -t tag(s)" << std::endl;
+ std::cout << "Scan a directory containing DICOM files.\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -d --dir DICOM directory" << std::endl;
+ std::cout << " -t --tag %d,%d DICOM tag(s) to look for" << std::endl;
+ std::cout << " -P --private-tag %d,%d,%s DICOM private tag(s) to look for" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " -p --print Print output." << std::endl;
+ std::cout << " -r --recursive Recusively descend directory." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ bool print = false;
+ bool recursive = false;
+ std::string dirname;
+ typedef std::vector VectorTags;
+ typedef std::vector VectorPrivateTags;
+ VectorTags tags;
+ VectorPrivateTags privatetags;
+ gdcm::Tag tag;
+ gdcm::PrivateTag privatetag;
+
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"dir", 1, 0, 0},
+ {"tag", 1, 0, 0},
+ {"recursive", 1, 0, 0},
+ {"print", 1, 0, 0},
+ {"private-tag", 1, 0, 0},
+
+// General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "d:t:rpP:VWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ //const char *s = long_options[option_index].name;
+ //printf ("option %s", s);
+ //if (optarg)
+ // {
+ // if( option_index == 0 ) /* input */
+ // {
+ // assert( strcmp(s, "input") == 0 );
+ // }
+ // printf (" with arg %s", optarg);
+ // }
+ //printf ("\n");
+ }
+ break;
+
+ case 'd':
+ dirname = optarg;
+ break;
+
+ case 't':
+ tag.ReadFromCommaSeparatedString(optarg);
+ tags.push_back( tag );
+ //std::cerr << optarg << std::endl;
+ break;
+
+ case 'P':
+ privatetag.ReadFromCommaSeparatedString(optarg);
+ privatetags.push_back( privatetag );
+ //std::cerr << optarg << std::endl;
+ break;
+
+ case 'r':
+ recursive = true;
+ break;
+
+ case 'p':
+ print = true;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+/*
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ {
+ printf ("%s ", argv[optind++]);
+ }
+ printf ("\n");
+*/
+ PrintHelp();
+ return 1;
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( dirname.empty() )
+ {
+ //std::cerr << "Need dir (-d)\n";
+ PrintHelp();
+ return 1;
+ }
+ if( tags.empty() && privatetags.empty() )
+ {
+ //std::cerr << "Need tags (-t)\n";
+ PrintHelp();
+ return 1;
+ }
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ if( verbose )
+ {
+ std::cout << "Will parse: " << dirname << std::endl;
+ std::cout << "Looking for tags: \n";
+ std::copy(tags.begin(), tags.end(),
+ std::ostream_iterator( std::cout, "\n"));
+ std::copy(privatetags.begin(), privatetags.end(),
+ std::ostream_iterator( std::cout, "\n"));
+ //std::cout << std::endl;
+ }
+
+ gdcm::Directory d;
+ unsigned int nfiles = d.Load( dirname.c_str(), recursive );
+ if( verbose ) d.Print( std::cout );
+ std::cout << "done retrieving file list " << nfiles << " files found." << std::endl;
+
+ gdcm::SmartPointer ps = new gdcm::Scanner;
+ gdcm::Scanner &s = *ps;
+ //gdcm::SimpleSubjectWatcher watcher(ps, "Scanner");
+ for( VectorTags::const_iterator it = tags.begin(); it != tags.end(); ++it)
+ {
+ s.AddTag( *it );
+ }
+ for( VectorPrivateTags::const_iterator it = privatetags.begin(); it != privatetags.end(); ++it)
+ {
+ s.AddPrivateTag( *it );
+ }
+ bool b = s.Scan( d.GetFilenames() );
+ if( !b )
+ {
+ std::cerr << "Scanner failed" << std::endl;
+ return 1;
+ }
+ if (print) s.Print( std::cout );
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmscu.cxx b/gdcm/Applications/Cxx/gdcmscu.cxx
new file mode 100644
index 0000000..d48902a
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmscu.cxx
@@ -0,0 +1,745 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * Simple command line tool to echo/store/find/move DICOM using
+ * DICOM Query/Retrieve
+ * This is largely inspired by other tool available from other toolkit, namely:
+ * echoscu (DCMTK)
+ * findscu (DCMTK)
+ * movescu (DCMTK)
+ * storescu (DCMTK)
+ */
+
+#include "gdcmCompositeNetworkFunctions.h"
+
+#include
+#include
+#include
+#include
+#include "gdcmVersion.h"
+#include "gdcmGlobal.h"
+#include "gdcmSystem.h"
+#include "gdcmDirectory.h"
+#include "gdcmDataSet.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmUIDGenerator.h"
+
+#include "gdcmBaseRootQuery.h"
+#include "gdcmQueryFactory.h"
+#include "gdcmPrinter.h"
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcmscu: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmscu [OPTION]...[OPERATION]...HOSTNAME...[PORT]..." << std::endl;
+ std::cout << "Execute a DICOM Q/R operation to HOSTNAME, using port PORT (104 when not specified)\n";
+ std::cout << "Options:" << std::endl;
+ std::cout << " -H --hostname Hostname." << std::endl;
+ std::cout << " -p --port Port number." << std::endl;
+ std::cout << " --aetitle Set Calling AE Title." << std::endl;
+ std::cout << " --call Set Called AE Title." << std::endl;
+ std::cout << "Mode Options:" << std::endl;
+ std::cout << " --echo C-ECHO (default when none)." << std::endl;
+ std::cout << " --store C-STORE." << std::endl;
+ std::cout << " --find C-FIND." << std::endl;
+ std::cout << " --move C-MOVE." << std::endl;
+ std::cout << " --get C-GET." << std::endl;
+ std::cout << "C-STORE Options:" << std::endl;
+ std::cout << " -i --input DICOM filename" << std::endl;
+ std::cout << " -r --recursive recursively process (sub-)directories." << std::endl;
+ std::cout << " --store-query Store constructed query in file." << std::endl;
+ std::cout << "C-FIND Options:" << std::endl;
+ //std::cout << " --worklist C-FIND Worklist Model." << std::endl;//!!not supported atm
+ std::cout << " --patientroot C-FIND Patient Root Model." << std::endl;
+ std::cout << " --studyroot C-FIND Study Root Model." << std::endl;
+ std::cout << " --patient C-FIND Query on Patient Info (cannot be used with --studyroot)" << std::endl;
+ std::cout << " --study C-FIND Query on Study Info." << std::endl;
+ std::cout << " --series C-FIND Query on Series Info." << std::endl;
+ std::cout << " --image C-FIND Query on Image Info." << std::endl;
+ //std::cout << " --psonly C-FIND Patient/Study Only Model." << std::endl;
+ std::cout << " --key 0123,4567=VALUE for specifying search criteria (wildcard allowed)." << std::endl;
+ std::cout << " With --key, leave blank (ie, --key 10,10="") to retrieve values" << std::endl;
+ std::cout << "C-MOVE Options:" << std::endl;
+ std::cout << " -o --output DICOM output directory." << std::endl;
+ std::cout << " --port-scp Port used for incoming association." << std::endl;
+ std::cout << " --key 0123,4567=VALUE for specifying search criteria (wildcard not allowed)." << std::endl;
+ std::cout << " Note that C-MOVE supports the same queries as C-FIND, but no wildcards are allowed." << std::endl;
+ std::cout << "C-GET Options:" << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " --queryhelp print query help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << " -L --log-file set log file (instead of cout)." << std::endl;
+
+ try
+ {
+ std::locale l("");
+ std::string loc = l.name();
+ std::cout << std::endl;
+ std::cout << "Local Name: " << loc << std::endl;
+ }
+ catch( const std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+ std::cout << "Local Character Set: " << gdcm::System::GetLocaleCharset() << std::endl;
+ std::vector charsettype;
+ charsettype.push_back( gdcm::QueryFactory::GetCharacterFromCurrentLocale() );
+ gdcm::DataElement de = gdcm::QueryFactory::ProduceCharacterSetDataElement(charsettype);
+ const gdcm::ByteValue *bv = de.GetByteValue();
+ std::string s( bv->GetPointer(), bv->GetLength() );
+ std::cout << "DICOM Character Set: [" << s << "]" << std::endl;
+}
+
+static void PrintQueryHelp(int inFindPatientRoot)
+{
+ gdcm::BaseRootQuery* theBase;
+ if (inFindPatientRoot)
+ {
+ std::cout << "To find the help for a study-level query, type" <WriteHelpFile(std::cout);
+ delete theBase;
+ }
+ else
+ {
+ std::cout << "To find the help for a patient-level query, type" <WriteHelpFile(std::cout);
+ delete theBase;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ std::string shostname;
+ std::string callingaetitle = "GDCMSCU";
+ std::string callaetitle = "ANY-SCP";
+ int port = 104; // default
+ int portscp = 0;
+ int outputopt = 0;
+ int portscpnum = 0;
+ gdcm::Directory::FilenamesType filenames;
+ std::string outputdir;
+ int storequery = 0;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int queryhelp = 0;
+ int version = 0;
+ int echomode = 0;
+ int storemode = 0;
+ int findmode = 0;
+ int movemode = 0;
+ int getmode = 0;
+ int findworklist = 0;
+ int findpatientroot = 0;
+ int findstudyroot = 0;
+ int patientquery = 0;
+ int studyquery = 0;
+ int seriesquery = 0;
+ int imagequery = 0;
+ int findpsonly = 0;
+ std::string queryfile;
+ std::string root;
+ int rootuid = 0;
+ int recursive = 0;
+ int logfile = 0;
+ std::string logfilename;
+ gdcm::Tag tag;
+ std::vector< std::pair > keys;
+
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ /*
+ struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+ */
+ static struct option long_options[] = {
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {"hostname", 1, 0, 0}, // -h
+ {"aetitle", 1, 0, 0}, //
+ {"call", 1, 0, 0}, //
+ {"port", 0, &port, 1}, // -p
+ {"input", 1, 0, 0}, // dcmfile-in
+ {"echo", 0, &echomode, 1}, // --echo
+ {"store", 0, &storemode, 1}, // --store
+ {"find", 0, &findmode, 1}, // --find
+ {"move", 0, &movemode, 1}, // --move
+ {"key", 1, 0, 0}, // (15) --key
+ {"worklist", 0, &findworklist, 1}, // --worklist
+ {"patientroot", 0, &findpatientroot, 1}, // --patientroot
+ {"studyroot", 0, &findstudyroot, 1}, // --studyroot
+ {"psonly", 0, &findpsonly, 1}, // --psonly
+ {"port-scp", 1, &portscp, 1}, // (20) --port-scp
+ {"output", 1, &outputopt, 1}, // --output
+ {"recursive", 0, &recursive, 1},
+ {"store-query", 1, &storequery, 1},
+ {"queryhelp", 0, &queryhelp, 1},
+ {"patient", 0, &patientquery, 1}, // --patient
+ {"study", 0, &studyquery, 1}, // --study
+ {"series", 0, &seriesquery, 1}, // --series
+ {"image", 0, &imagequery, 1}, // --image
+ {"log-file", 1, &logfile, 1}, // --log-file
+ {"get", 0, &getmode, 1}, // --get
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:H:p:L:VWDEhvk:o:r";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ filenames.push_back( optarg );
+ }
+ else if( option_index == 7 ) /* calling aetitle */
+ {
+ assert( strcmp(s, "aetitle") == 0 );
+ //assert( callingaetitle.empty() );
+ callingaetitle = optarg;
+ }
+ else if( option_index == 8 ) /* called aetitle */
+ {
+ assert( strcmp(s, "call") == 0 );
+ //assert( callaetitle.empty() );
+ callaetitle = optarg;
+ }
+ else if( option_index == 15 ) /* key */
+ {
+ assert( strcmp(s, "key") == 0 );
+ if( !tag.ReadFromCommaSeparatedString(optarg) )
+ {
+ std::cerr << "Could not read Tag: " << optarg << std::endl;
+ return 1;
+ }
+ std::stringstream ss;
+ ss.str( optarg );
+ uint16_t dummy;
+ char cdummy; // comma
+ ss >> std::hex >> dummy;
+ assert( tag.GetGroup() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' );
+ ss >> std::hex >> dummy;
+ assert( tag.GetElement() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' || cdummy == '=' );
+ std::string str;
+ //ss >> str;
+ std::getline(ss, str); // do not skip whitespace
+ keys.push_back( std::make_pair(tag, str) );
+ }
+ else if( option_index == 20 ) /* port-scp */
+ {
+ assert( strcmp(s, "port-scp") == 0 );
+ portscpnum = atoi(optarg);
+ }
+ else if( option_index == 21 ) /* output */
+ {
+ assert( strcmp(s, "output") == 0 );
+ outputdir = optarg;
+ }
+ else if( option_index == 23 ) /* store-query */
+ {
+ assert( strcmp(s, "store-query") == 0 );
+ queryfile = optarg;
+ }
+ else if( option_index == 29 ) /* log-file */
+ {
+ assert( strcmp(s, "log-file") == 0 );
+ logfilename = optarg;
+ }
+ else
+ {
+ // If you reach here someone mess-up the index and the argument in
+ // the getopt table
+ assert( 0 );
+ }
+ //printf (" with arg %s", optarg);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'k':
+ {
+ if( !tag.ReadFromCommaSeparatedString(optarg) )
+ {
+ std::cerr << "Could not read Tag: " << optarg << std::endl;
+ return 1;
+ }
+ std::stringstream ss;
+ ss.str( optarg );
+ uint16_t dummy;
+ char cdummy; // comma
+ ss >> std::hex >> dummy;
+ assert( tag.GetGroup() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' );
+ ss >> std::hex >> dummy;
+ assert( tag.GetElement() == dummy );
+ ss >> cdummy;
+ assert( cdummy == ',' || cdummy == '=' );
+ std::string str;
+ std::getline(ss, str); // do not skip whitespace
+ keys.push_back( std::make_pair(tag, str) );
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ filenames.push_back( optarg );
+ break;
+
+ case 'r':
+ recursive = 1;
+ break;
+
+ case 'o':
+ assert( outputdir.empty() );
+ outputdir = optarg;
+ break;
+
+ case 'H':
+ shostname = optarg;
+ break;
+
+ case 'p':
+ port = atoi( optarg );
+ break;
+
+ case 'L':
+ logfile = 1;
+ logfilename = optarg;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'q':
+ queryhelp = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ int v = argc - optind;
+ // hostname port filename
+ if( v == 1 )
+ {
+ shostname = argv[optind++];
+ }
+ else if( v == 2 )
+ {
+ shostname = argv[optind++];
+ port = atoi( argv[optind++] );
+ }
+ else if( v >= 3 )
+ {
+ shostname = argv[optind++];
+ port = atoi( argv[optind++] );
+ std::vector files;
+ while (optind < argc)
+ {
+ files.push_back( argv[optind++] );
+ }
+ filenames = files;
+ }
+ else
+ {
+ return 1;
+ }
+ assert( optind == argc );
+ }
+
+ if( version )
+ {
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ PrintHelp();
+ return 0;
+ }
+ if(queryhelp)
+ {
+ PrintQueryHelp(findpatientroot);
+ return 0;
+ }
+ const bool theDebug = debug != 0;
+ const bool theWarning = warning != 0;
+ const bool theError = error != 0;
+ const bool theVerbose = verbose != 0;
+ const bool theRecursive = recursive != 0;
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( theDebug );
+ gdcm::Trace::SetWarning( theWarning );
+ gdcm::Trace::SetError( theError );
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( theVerbose );
+ gdcm::Trace::SetError( theVerbose);
+ }
+ if( logfile )
+ {
+ gdcm::Trace::SetStreamToFile( logfilename.c_str() );
+ }
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( callaetitle.c_str() );
+ if( !rootuid )
+ {
+ // only read the env var if no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ // root is set either by the cmd line option or the env var
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ if( shostname.empty() )
+ {
+ //std::cerr << "Hostname missing" << std::endl;
+ PrintHelp(); // needed to display help message when no arg
+ return 1;
+ }
+ if( port == 0 )
+ {
+ std::cerr << "Problem with port number" << std::endl;
+ return 1;
+ }
+ // checkout outputdir opt:
+ if( outputopt )
+ {
+ if( !gdcm::System::FileIsDirectory( outputdir.c_str()) )
+ {
+ if( !gdcm::System::MakeDirectory( outputdir.c_str() ) )
+ {
+ std::cerr << "Sorry: " << outputdir << " is not a valid directory.";
+ std::cerr << std::endl;
+ std::cerr << "and I could not create it.";
+ std::cerr << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ const char *hostname = shostname.c_str();
+ std::string mode = "echo";
+ if ( echomode )
+ {
+ mode = "echo";
+ }
+ else if ( storemode )
+ {
+ mode = "store";
+ }
+ else if ( findmode )
+ {
+ mode = "find";
+ }
+ else if ( movemode )
+ {
+ mode = "move";
+ }
+ else if ( getmode )
+ {
+ mode = "get";
+ }
+
+ //this class contains the networking calls
+
+ if ( mode == "server" ) // C-STORE SCP
+ {
+ // MM: Do not expose that to user for now (2010/10/11).
+ //CStoreServer( port );
+ return 1;
+ }
+ else if ( mode == "echo" ) // C-ECHO SCU
+ {
+ // ./bin/gdcmscu mi2b2.slicer.org 11112 --aetitle ACME1 --call MI2B2
+ // ./bin/gdcmscu --echo mi2b2.slicer.org 11112 --aetitle ACME1 --call MI2B2
+ bool didItWork = gdcm::CompositeNetworkFunctions::CEcho( hostname, (uint16_t)port,
+ callingaetitle.c_str(), callaetitle.c_str() );
+ gdcmDebugMacro( (didItWork ? "Echo succeeded." : "Echo failed.") );
+ return didItWork ? 0 : 1;
+ }
+ else if ( mode == "move" ) // C-FIND SCU
+ {
+ // ./bin/gdcmscu --move --patient dhcp-67-183 5678 move
+ // ./bin/gdcmscu --move --patient mi2b2.slicer.org 11112 move
+ gdcm::ERootType theRoot = gdcm::eStudyRootType;
+ if (findpatientroot)
+ theRoot = gdcm::ePatientRootType;
+ gdcm::EQueryLevel theLevel = gdcm::eStudy;
+ if (patientquery)
+ theLevel = gdcm::ePatient;
+ if (seriesquery)
+ theLevel = gdcm::eSeries;
+ if (imagequery)
+ theLevel = gdcm::eImage;
+
+ gdcm::SmartPointer theQuery =
+ gdcm::CompositeNetworkFunctions::ConstructQuery(theRoot, theLevel ,keys, true);
+
+ if (findstudyroot == 0 && findpatientroot == 0)
+ {
+ if (gdcm::Trace::GetErrorFlag())
+ {
+ std::cerr << "Need to explicitly choose query retrieve level, --patientroot or --studyroot" << std::endl;
+ }
+ std::cerr << "Move failed." << std::endl;
+ return 1;
+ }
+
+ if( !portscp )
+ {
+ std::cerr << "Need to set explicitly port number for SCP association"
+ " --port-scp" << std::endl;
+ //std::cerr << "Move failed." << std::endl;
+ return 1;
+ }
+
+ if( storequery )
+ {
+ if (!theQuery->WriteQuery(queryfile))
+ {
+ std::cerr << "Could not write out query to: " << queryfile << std::endl;
+ std::cerr << "Move failed." << std::endl;
+ return 1;
+ }
+ }
+
+ if (!theQuery->ValidateQuery(false))
+ {
+ std::cerr << "You have not constructed a valid find query."
+ " Please try again." << std::endl;
+ return 1;
+ }
+
+ //!!! added the boolean to 'interleave writing', which basically writes
+ //each file out as it comes across, rather than all at once at the end.
+ //Turn off the boolean to have it written all at once at the end.
+ bool didItWork = gdcm::CompositeNetworkFunctions::CMove( hostname, (uint16_t)port,
+ theQuery, (uint16_t)portscpnum,
+ callingaetitle.c_str(), callaetitle.c_str(), outputdir.c_str() );
+ gdcmDebugMacro( (didItWork ? "Move succeeded." : "Move failed.") );
+ return didItWork ? 0 : 1;
+ }
+ else if ( mode == "find" ) // C-FIND SCU
+ {
+ // Construct C-FIND DataSet:
+ // ./bin/gdcmscu --find --patient dhcp-67-183 5678
+ // ./bin/gdcmscu --find --patient mi2b2.slicer.org 11112 --aetitle ACME1 --call MI2B2
+ // findscu -aec MI2B2 -P -k 0010,0010=F* mi2b2.slicer.org 11112 patqry.dcm
+
+ // PATIENT query:
+ // ./bin/gdcmscu --find --patient mi2b2.slicer.org 11112 --aetitle ACME1 --call MI2B2 --key 10,10="F*" -V
+ gdcm::ERootType theRoot = gdcm::eStudyRootType;
+ if (findpatientroot)
+ theRoot = gdcm::ePatientRootType;
+ gdcm::EQueryLevel theLevel = gdcm::eStudy;
+ if (patientquery)
+ theLevel = gdcm::ePatient;
+ if (seriesquery)
+ theLevel = gdcm::eSeries;
+ if (imagequery)
+ theLevel = gdcm::eImage;
+
+ gdcm::SmartPointer theQuery =
+ gdcm::CompositeNetworkFunctions::ConstructQuery(theRoot, theLevel ,keys);
+
+ if (findstudyroot == 0 && findpatientroot == 0)
+ {
+ if (gdcm::Trace::GetErrorFlag())
+ {
+ std::cerr << "Need to explicitly choose query retrieve level, --patientroot or --studyroot" << std::endl;
+ }
+ std::cerr << "Find failed." << std::endl;
+ return 1;
+ }
+ if (!theQuery)
+ {
+ std::cerr << "Query construction failed." <WriteQuery(queryfile))
+ {
+ std::cerr << "Could not write out query to: " << queryfile << std::endl;
+ return 1;
+ }
+ }
+
+ //doing a non-strict query, the second parameter there.
+ //look at the base query comments
+ if (!theQuery->ValidateQuery(false))
+ {
+ std::cerr << "You have not constructed a valid find query."
+ " Please try again." << std::endl;
+ return 1;
+ }
+ //the value in that tag corresponds to the query type
+ std::vector theDataSet;
+ if( !gdcm::CompositeNetworkFunctions::CFind(hostname, (uint16_t)port, theQuery, theDataSet,
+ callingaetitle.c_str(), callaetitle.c_str()) )
+ {
+ gdcmDebugMacro( "Problem in CFind." );
+ return 1;
+ }
+
+ gdcm::Printer p;
+ std::ostream &os = gdcm::Trace::GetStream();
+ for( std::vector::iterator itor
+ = theDataSet.begin(); itor != theDataSet.end(); itor++)
+ {
+ os << "Find Response: " << (itor - theDataSet.begin() + 1) << std::endl;
+ p.PrintDataSet( *itor, os );
+ os << std::endl;
+ }
+
+ if( gdcm::Trace::GetWarningFlag() ) // == verbose flag
+ {
+ os << "Find was successful." << std::endl;
+ }
+ return 0;
+ }
+ else if ( mode == "store" ) // C-STORE SCU
+ {
+ // mode == directory
+ gdcm::Directory::FilenamesType thefiles;
+ for( gdcm::Directory::FilenamesType::const_iterator file = filenames.begin();
+ file != filenames.end(); ++file )
+ {
+ if( gdcm::System::FileIsDirectory(file->c_str()) )
+ {
+ gdcm::Directory::FilenamesType files;
+ gdcm::Directory dir;
+ dir.Load(*file, theRecursive);
+ files = dir.GetFilenames();
+ thefiles.insert(thefiles.end(), files.begin(), files.end());
+ }
+ else
+ {
+ // This is a file simply add it
+ thefiles.push_back(*file);
+ }
+ }
+ bool didItWork =
+ gdcm::CompositeNetworkFunctions::CStore(hostname, (uint16_t)port, thefiles,
+ callingaetitle.c_str(), callaetitle.c_str());
+
+ gdcmDebugMacro( (didItWork ? "Store was successful." : "Store failed.") );
+ return didItWork ? 0 : 1;
+ }
+ else if ( mode == "get" ) // C-GET SCU
+ {
+ return 1;
+ }
+ else
+ {
+ assert( 0 );
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmstream.cxx b/gdcm/Applications/Cxx/gdcmstream.cxx
new file mode 100644
index 0000000..8cc5baf
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmstream.cxx
@@ -0,0 +1,1123 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+#include "gdcmAttribute.h"
+#include "gdcmFile.h"
+#include "gdcmFilename.h"
+#include "gdcmImageHelper.h"
+#include "gdcmItem.h"
+#include "gdcmMediaStorage.h"
+#include "gdcmSequenceOfItems.h"
+#include "gdcmStreamImageReader.h"
+#include "gdcmStreamImageWriter.h"
+#include "gdcmSystem.h"
+#include "gdcmTag.h"
+#include "gdcmTrace.h"
+#include "gdcmTransferSyntax.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmVersion.h"
+
+#ifdef OPENJPEG_MAJOR_VERSION
+#if OPENJPEG_MAJOR_VERSION == 1
+#include "gdcm_openjpeg.h"
+#elif OPENJPEG_MAJOR_VERSION == 2
+#define USE_OPJ_DEPRECATED // opj_setup_decoder
+#include "gdcm_openjpeg2.h"
+#else
+#error should not happen
+#endif
+#else
+#error should not happen
+#endif
+
+#include
+
+static void error_callback(const char *msg, void *) {
+ (void)msg;
+}
+static void warning_callback(const char *msg, void *) {
+ (void)msg;
+}
+static void info_callback(const char *msg, void *) {
+ (void)msg;
+}
+
+template
+static unsigned int readvector(std::vector &v, const char *str)
+{
+ if( !str ) return 0;
+ std::istringstream os( str );
+ T f;
+ while( os >> f )
+ {
+ v.push_back( f );
+ os.get(); // == ","
+ }
+
+ return (unsigned int)v.size();
+}
+
+static int No_Of_Resolutions(const char *filename)
+{
+ std::ifstream is;
+ is.open( filename, std::ios::binary );
+ opj_dparameters_t parameters; /* decompression parameters */
+ opj_event_mgr_t event_mgr; /* event manager */
+ opj_dinfo_t* dinfo; /* handle to a decompressor */
+ opj_cio_t *cio;
+ // FIXME: Do some stupid work:
+ is.seekg( 0, std::ios::end);
+ size_t buf_size = (size_t)is.tellg();
+ char *dummy_buffer = new char[(unsigned int)buf_size];
+ is.seekg(0, std::ios::beg);
+ is.read( dummy_buffer, buf_size);
+ unsigned char *src = (unsigned char*)dummy_buffer;
+ uint32_t file_length = (uint32_t)buf_size; // 32bits truncation should be ok since DICOM cannot have larger than 2Gb image
+
+
+ /* configure the event callbacks (not required) */
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = error_callback;
+ event_mgr.warning_handler = warning_callback;
+ event_mgr.info_handler = info_callback;
+
+ /* set decoding parameters to default values */
+ opj_set_default_decoder_parameters(¶meters);
+
+ // default blindly copied
+ parameters.cp_layer=0;
+ parameters.cp_reduce= 0;
+ // parameters.decod_format=-1;
+ // parameters.cod_format=-1;
+
+ const char jp2magic[] = "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A";
+ if( memcmp( src, jp2magic, sizeof(jp2magic) ) == 0 )
+ {
+ /* JPEG-2000 compressed image data ... sigh */
+ // gdcmData/ELSCINT1_JP2vsJ2K.dcm
+ // gdcmData/MAROTECH_CT_JP2Lossy.dcm
+ //gdcmWarningMacro( "J2K start like JPEG-2000 compressed image data instead of codestream" );
+ parameters.decod_format = 1; //JP2_CFMT;
+ //assert(parameters.decod_format == JP2_CFMT);
+ }
+ else
+ {
+ /* JPEG-2000 codestream */
+ //parameters.decod_format = J2K_CFMT;
+ //assert(parameters.decod_format == J2K_CFMT);
+ assert( 0 );
+ }
+ parameters.cod_format = 11; // PGX_DFMT;
+ //assert(parameters.cod_format == PGX_DFMT);
+
+ /* get a decoder handle */
+ dinfo = opj_create_decompress(CODEC_JP2);
+
+ /* catch events using our callbacks and give a local context */
+ opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL);
+
+ /* setup the decoder decoding parameters using user parameters */
+ opj_setup_decoder(dinfo, ¶meters);
+
+ /* open a byte stream */
+ cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
+
+ /* decode the stream and fill the image structure */
+ if(! opj_decode(dinfo, cio)) {
+ opj_destroy_decompress(dinfo);
+ opj_cio_close(cio);
+ //gdcmErrorMacro( "opj_decode failed" );
+ return 1;
+ }
+
+ opj_cp_t * cp = ((opj_jp2_t*)dinfo->jp2_handle)->j2k->cp;
+ opj_tcp_t *tcp = &cp->tcps[0];
+ opj_tccp_t *tccp = &tcp->tccps[0];
+
+ return tccp->numresolutions;
+ /* std::cout << "\n No of Cols In Image" << image->x1;
+ std::cout << "\n No of Rows In Image" << image->y1;
+ std::cout << "\n No of Components in Image" << image->numcomps;
+ std::cout << "\n No of Resolutions"<< tccp->numresolutions << "\n";
+*/
+
+}
+
+static bool Write_Resolution(gdcm::StreamImageWriter & theStreamWriter, const char *filename, int res, std::ostream& of, int flag, gdcm::SequenceOfItems *sq)
+{
+ (void)of;
+ std::ifstream is;
+ is.open( filename, std::ios::binary );
+ opj_dparameters_t parameters; /* decompression parameters */
+ opj_event_mgr_t event_mgr; /* event manager */
+ opj_dinfo_t* dinfo; /* handle to a decompressor */
+ opj_cio_t *cio;
+ opj_image_t *image = NULL;
+ // FIXME: Do some stupid work:
+ is.seekg( 0, std::ios::end);
+ size_t buf_size = (size_t)is.tellg();
+ char *dummy_buffer = new char[buf_size];
+ is.seekg(0, std::ios::beg);
+ is.read( dummy_buffer, buf_size);
+ unsigned char *src = (unsigned char*)dummy_buffer;
+ uint32_t file_length = (uint32_t)buf_size; // 32bits truncation should be ok since DICOM cannot have larger than 2Gb image
+
+
+ /* configure the event callbacks (not required) */
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = error_callback;
+ event_mgr.warning_handler = warning_callback;
+ event_mgr.info_handler = info_callback;
+
+ /* set decoding parameters to default values */
+ opj_set_default_decoder_parameters(¶meters);
+
+ // default blindly copied
+ parameters.cp_layer=0;
+ parameters.cp_reduce= res;
+ // parameters.decod_format=-1;
+ // parameters.cod_format=-1;
+
+ const char jp2magic[] = "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A";
+ if( memcmp( src, jp2magic, sizeof(jp2magic) ) == 0 )
+ {
+ /* JPEG-2000 compressed image data ... sigh */
+ // gdcmData/ELSCINT1_JP2vsJ2K.dcm
+ // gdcmData/MAROTECH_CT_JP2Lossy.dcm
+ //gdcmWarningMacro( "J2K start like JPEG-2000 compressed image data instead of codestream" );
+ parameters.decod_format = 1; //JP2_CFMT;
+ //assert(parameters.decod_format == JP2_CFMT);
+ }
+ else
+ {
+ /* JPEG-2000 codestream */
+ //parameters.decod_format = J2K_CFMT;
+ //assert(parameters.decod_format == J2K_CFMT);
+ assert( 0 );
+ }
+ parameters.cod_format = 11; // PGX_DFMT;
+ //assert(parameters.cod_format == PGX_DFMT);
+
+ /* get a decoder handle */
+ dinfo = opj_create_decompress(CODEC_JP2);
+
+ /* catch events using our callbacks and give a local context */
+ opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL);
+
+ /* setup the decoder decoding parameters using user parameters */
+ opj_setup_decoder(dinfo, ¶meters);
+
+ /* open a byte stream */
+ cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
+
+ /* decode the stream and fill the image structure */
+ image = opj_decode(dinfo, cio);
+ if(!image)
+ {
+ opj_destroy_decompress(dinfo);
+ opj_cio_close(cio);
+ //gdcmErrorMacro( "opj_decode failed" );
+ return 1;
+ }
+
+ //opj_cp_t * cp = ((opj_jp2_t*)dinfo->jp2_handle)->j2k->cp;
+ //opj_tcp_t *tcp = &cp->tcps[0];
+ //opj_tccp_t *tccp = &tcp->tccps[0];
+ /* std::cout << "\n No of Cols In Image" << image->x1;
+ std::cout << "\n No of Rows In Image" << image->y1;
+ std::cout << "\n No of Components in Image" << image->numcomps;
+ std::cout << "\n No of Resolutions"<< tccp->numresolutions << "\n";
+ */
+ //opj_j2k_t* j2k = NULL;
+ //opj_jp2_t* jp2 = NULL;
+ //jp2 = (opj_jp2_t*)dinfo->jp2_handle;
+ //int reversible = jp2->j2k->cp->tcps->tccps->qmfbid;
+ //std:: cout << reversible;
+ int Dimensions[2];
+{
+ int compno = 0;
+ opj_image_comp_t *comp = &image->comps[compno];
+ Dimensions[0]= comp->w;
+ Dimensions[1] = comp->h;
+ opj_cio_close(cio);
+}
+ unsigned long rawlen = Dimensions[0]*Dimensions[1] * image->numcomps;
+ //std::cout << "\nTest" <comps[0].factor;
+ char *raw = new char[rawlen];
+
+ for (unsigned int compno = 0; compno < (unsigned int)image->numcomps; compno++)
+ {
+ const opj_image_comp_t *comp = &image->comps[compno];
+
+ int w = comp->w;
+ int h = comp->h;
+ uint8_t *data8 = (uint8_t*)raw + compno;
+ for (int i = 0; i < w * h ; i++)
+ {
+ int v = image->comps[compno].data[i];
+ *data8 = (uint8_t)v;
+ data8 += image->numcomps;
+ }
+ }
+
+
+ gdcm::Writer w;
+ gdcm::File &file = w.GetFile();
+ gdcm::DataSet &ds = file.GetDataSet();
+
+ file.GetHeader().SetDataSetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
+
+ gdcm::UIDGenerator uid;
+ gdcm::DataElement de( gdcm::Tag(0x8,0x18) ); // SOP Instance UID
+ de.SetVR( gdcm::VR::UI );
+ const char *u = uid.Generate();
+ de.SetByteValue( u, (uint32_t)strlen(u) );
+ ds.Insert( de );
+
+ gdcm::DataElement de1( gdcm::Tag(0x8,0x16) );
+ de1.SetVR( gdcm::VR::UI );
+ gdcm::MediaStorage ms( gdcm::MediaStorage::VLWholeSlideMicroscopyImageStorage );
+ de1.SetByteValue( ms.GetString(), (uint32_t)strlen(ms.GetString()));
+ ds.Insert( de1 );
+
+ gdcm::DataElement de2( gdcm::Tag(0x28,0x04) );
+ //de.SetTag(gdcm::Tag(0x28,0x04));
+ de2.SetVR( gdcm::VR::CS );
+
+ if(image->numcomps == 1)
+ {
+ const char mystr[] = "MONOCHROME2";
+ de2.SetByteValue(mystr, (uint32_t)strlen(mystr));
+ }
+ else
+ {
+ const char mystr1[] = "RGB";
+ de2.SetByteValue(mystr1, (uint32_t)strlen(mystr1));
+ }
+
+ ds.Insert( de2 );
+
+
+ gdcm::Attribute<0x0028,0x0100> at = {8};
+ ds.Insert( at.GetAsDataElement() );
+
+ gdcm::Attribute<0x0028,0x0002> at1 = { (uint16_t)image->numcomps};
+ ds.Insert( at1.GetAsDataElement() );
+
+ gdcm::Attribute<0x0028,0x0101> at2 = {8};
+ ds.Insert( at2.GetAsDataElement() );
+
+ gdcm::Attribute<0x0028,0x0102> at3 = {7};
+ ds.Insert( at3.GetAsDataElement() );
+
+
+ if (flag == 1) //This flag is to write Image Information
+ {
+ for (int i=0; i <= res; i++) // Loop to set different dimensions of all resolution
+ {
+ int a = 1;
+ int b =1;
+ while(a!=((res+1)-i))
+ {
+ b = b*2;
+ a = a+1;
+ }
+
+ uint16_t row = (uint16_t)((image->y1)/b);
+ uint16_t col = (uint16_t)((image->x1)/b);
+
+ gdcm::Element el2;
+ el2.SetValue(i+1);
+ gdcm::DataElement rfn = el2.GetAsDataElement(); //rfn ---> reference frame number
+ rfn.SetTag( gdcm::Tag(0x0008,0x1160) );
+
+ gdcm::Element el;
+ el.SetValue(1,0);
+ el.SetValue(1,1);
+ gdcm::DataElement ulr = el.GetAsDataElement(); //ulr --> upper left col/row
+ ulr.SetTag( gdcm::Tag(0x0048,0x0201) );
+
+ gdcm::Element el1;
+ el1.SetValue(col,0);
+ el1.SetValue(row,1);
+ gdcm::DataElement brr = el1.GetAsDataElement();
+ brr.SetTag( gdcm::Tag(0x0048,0x0202) ); //brr --> bottom right col/row
+
+ gdcm::Item it;
+ gdcm::DataSet &nds = it.GetNestedDataSet();
+ nds.Insert( rfn );
+ nds.Insert(ulr);
+ nds.Insert(brr);
+
+ sq->AddItem(it);
+ }//For loop
+
+ gdcm::File &file2 = w.GetFile();
+ gdcm::DataSet &ds1 = file2.GetDataSet();
+
+ gdcm::Attribute<0x0048,0x0006> row1 = {(unsigned short)image->y1};
+ ds1.Insert( row1.GetAsDataElement() );
+
+ gdcm::Attribute<0x0048,0x0007> col1 = {(unsigned short)image->x1};
+ ds1.Insert( col1.GetAsDataElement() );
+ gdcm::Attribute<0x0028,0x0008> Number_Of_Frames = {res+1};
+ ds1.Insert( Number_Of_Frames.GetAsDataElement() );
+
+ gdcm::DataElement des( gdcm::Tag(0x0048,0x0200) );
+ des.SetVR(gdcm::VR::SQ);
+ des.SetValue(*sq);
+ des.SetVLToUndefined();
+
+ ds1.Insert(des);
+
+ theStreamWriter.SetFile(file2);
+
+ if (!theStreamWriter.WriteImageInformation())
+ {
+ std::cerr << "unable to write image information" << std::endl;
+ return 1; //the CanWrite function should prevent getting here, else,
+ //that's a test failure
+ }
+
+ ds1.Remove( gdcm::Tag(0x0048,0x0006) );
+ ds1.Remove( gdcm::Tag(0x0048,0x0007) );
+ ds1.Remove( gdcm::Tag(0x0028,0x0008) );
+ }//if (flag == 1) //This flag is to write Image Information
+
+ gdcm::Attribute<0x0048,0x0006> row = {(unsigned short)image->comps[0].w};
+ ds.Insert( row.GetAsDataElement() );
+
+ gdcm::Attribute<0x0048,0x0007> col = {(unsigned short)image->comps[0].h};
+ ds.Insert( col.GetAsDataElement() );
+
+ gdcm::Attribute<0x0028,0x0008> Number_Of_Frames = {1};
+ ds.Insert( Number_Of_Frames.GetAsDataElement() );
+
+ theStreamWriter.SetFile(file);
+
+ if (!theStreamWriter.CanWriteFile())
+ {
+ delete [] raw;
+ std::cerr << "Not able to write" << std::endl;
+ return 0;//this means that the file was unwritable, period.
+ //very similar to a ReadImageInformation failure
+ }
+
+ // Important to write here
+ std::vector extent = gdcm::ImageHelper::GetDimensionsValue(file);
+
+ unsigned short xmax = (uint16_t)extent[0];
+ unsigned short ymax = (uint16_t)extent[1];
+ unsigned short theChunkSize = 4;
+ unsigned short ychunk = (unsigned short)(extent[1]/theChunkSize); //go in chunk sizes of theChunkSize
+ unsigned short zmax = (uint16_t)extent[2];
+ //std::cout << "\n"<numcomps<<"\n";
+
+ if (xmax == 0 || ymax == 0)
+ {
+ std::cerr << "Image has no size, unable to write zero-sized image." << std::endl;
+ return 0;
+ }
+
+ int z, y, nexty;
+ unsigned long prevLen = 0; //when going through the char buffer, make sure to grab
+ //the bytes sequentially. So, store how far you got in the buffer with each iteration.
+ for (z = 0; z < zmax; ++z)
+ {
+ for (y = 0; y < ymax; y += ychunk)
+ {
+ nexty = y + ychunk;
+ if (nexty > ymax) nexty = ymax;
+ theStreamWriter.DefinePixelExtent(0, xmax, (uint16_t)y, (uint16_t)nexty, (uint16_t)z, (uint16_t)(z+1));
+ unsigned long len = theStreamWriter.DefineProperBufferLength();
+ //std::cout << "\n" < ymax) nexty = ymax;
+ reader.DefinePixelExtent(xmin, xmax, (uint16_t)y, (uint16_t)nexty, (uint16_t)z, (uint16_t)(z+1));
+ unsigned long len = reader.DefineProperBufferLength();
+
+ char* finalBuffer = new char[len];
+ if (reader.CanReadImage())
+ {
+ bool result = reader.Read(finalBuffer, len);
+ if( !result )
+ {
+ std::cerr << "res2 failure:" << std::endl;
+ delete [] finalBuffer;
+ return 1;
+ }
+ else
+ {
+ // std::cout<< "Able to read";
+ //delete [] finalBuffer;
+ // return 0; //essentially, we're going to skip this file since it can't be read by the streamer
+ }
+ }
+ else
+ {
+ std::cerr<< "Not able to put in read data buffer"<< std::endl;
+ }
+ theStreamWriter.DefinePixelExtent(xmin, xmax, (uint16_t)y, (uint16_t)nexty, (uint16_t)z, (uint16_t)(z+1));
+ // unsigned long len = theStreamWriter.DefineProperBufferLength();
+ //std::cout << "\n" < start, std::vector end)
+{
+ //std::vector::const_iterator it = filenames.begin();
+ gdcm::StreamImageReader reader;
+ reader.SetFileName( filename );
+
+
+ if (!reader.ReadImageInformation())
+ {
+ std::cerr << "unable to read image information" << std::endl;
+ return 1; //unable to read tags as expected.
+ }
+
+ gdcm::File file1 = reader.GetFile();
+ gdcm::DataSet ds1 = file1.GetDataSet();
+
+
+ gdcm::Writer w;
+ gdcm::File &file = w.GetFile();
+ gdcm::DataSet &ds = file.GetDataSet();
+
+
+
+ file.GetHeader().SetDataSetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian );
+ gdcm::DataElement uid = ds1.GetDataElement( gdcm::Tag(0x0008,0x0018) );
+ ds.Insert( uid );
+
+ gdcm::DataElement ms = ds1.GetDataElement( gdcm::Tag(0x0008,0x0016) );
+ ds.Insert( ms );
+
+ gdcm::DataElement mystr = ds1.GetDataElement( gdcm::Tag(0x0028,0x0004) );
+ ds.Insert( mystr );
+
+ if(res == 0)
+ {
+ gdcm::DataElement seq = ds1.GetDataElement( gdcm::Tag(0x0048,0x0200) );
+ ds.Insert(seq);
+ }
+ else
+ {
+ std::vector extent = reader.GetDimensionsValueForResolution(res);
+
+
+ gdcm::SmartPointer sq = new gdcm::SequenceOfItems();
+ sq->SetLengthToUndefined();
+ gdcm::Element el1;
+ el1.SetValue(res);
+ gdcm::DataElement rfn = el1.GetAsDataElement(); //rfn ---> reference frame number
+ rfn.SetTag( gdcm::Tag(0x0008,0x1160) );
+
+ gdcm::Element el2;
+ if(tile == 1)
+ {
+ el2.SetValue((unsigned short)start[0],0);
+ el2.SetValue((unsigned short)start[1],1);
+ }
+ else
+ {
+ el2.SetValue(1,0);
+ el2.SetValue(1,1);
+ }
+ gdcm::DataElement ulr = el2.GetAsDataElement(); //ulr --> upper left col/row
+ ulr.SetTag( gdcm::Tag(0x0048,0x0201) );
+
+
+ gdcm::Element el3;
+ if(tile == 1)
+ {
+ el3.SetValue((unsigned short)end[0],0);
+ el3.SetValue((unsigned short)end[1],1);
+ }
+ else
+ {
+ el3.SetValue((unsigned short)extent[0],0);
+ el3.SetValue((unsigned short)extent[1],1);
+ }
+ gdcm::DataElement brr = el3.GetAsDataElement();
+ brr.SetTag( gdcm::Tag(0x0048,0x0202) ); //brr --> bottom right col/row
+
+ gdcm::Item it;
+ gdcm::DataSet &nds = it.GetNestedDataSet();
+ nds.Insert( rfn );
+ nds.Insert(ulr);
+ nds.Insert(brr);
+
+ sq->AddItem(it);
+
+ gdcm::DataElement des( gdcm::Tag(0x0048,0x0200) );
+ des.SetVR(gdcm::VR::SQ);
+ des.SetValue(*sq);
+ des.SetVLToUndefined();
+
+ ds.Insert(des);
+
+ }
+
+
+
+
+ gdcm::DataElement row = ds1.GetDataElement( gdcm::Tag(0x0048,0x0006) );
+ assert( row.GetVR() == gdcm::VR::UL );
+ ds.Insert(row);
+
+ gdcm::DataElement col = ds1.GetDataElement( gdcm::Tag(0x0048,0x0007) );
+ ds.Insert(col);
+
+ gdcm::DataElement Number_Of_Frames = ds1.GetDataElement( gdcm::Tag(0x0028,0x0008) );
+ ds.Insert(Number_Of_Frames);
+
+ gdcm::Element el;
+ el.SetFromDataElement( Number_Of_Frames );
+
+
+ uint16_t No_Of_Resolutions = (uint16_t)el.GetValue(0);
+ //std::cout << "HERE NO. "<< No_Of_Resolutions;
+
+ gdcm::DataElement BA = ds1.GetDataElement( gdcm::Tag(0x0028,0x0100) );
+ ds.Insert( BA );
+
+ gdcm::DataElement SPP = ds1.GetDataElement( gdcm::Tag(0x0028,0x0002) );
+ ds.Insert( SPP );
+
+ gdcm::DataElement BS = ds1.GetDataElement( gdcm::Tag(0x0028,0x0101) );
+ ds.Insert( BS );
+
+ gdcm::DataElement HB = ds1.GetDataElement( gdcm::Tag(0x0028,0x0102) );
+ ds.Insert( HB );
+
+ theStreamWriter.SetFile(file);
+
+ if (!theStreamWriter.WriteImageInformation())
+ {
+ std::cerr << "unable to write image information" << std::endl;
+ return 1; //the CanWrite function should prevent getting here, else,
+ //that's a test failure
+ }
+
+
+ bool b = true;
+
+ if(res == 0)
+ {
+ for(int i = 1 ; i <= No_Of_Resolutions; ++i)
+ {
+ b = b && StreamImageRead_Write( theStreamWriter, reader, i, of, tile , start , end );
+ }
+ }
+ else
+ b = b && StreamImageRead_Write( theStreamWriter, reader, res, of ,tile, start, end );
+
+ return b;
+}
+
+
+
+static bool Different_Resolution_From_jp2( gdcm::StreamImageWriter & theStreamWriter, const char *filename, std::ostream& of, int nres)
+{
+ //std::vector::const_iterator it = filenames.begin();
+ bool b = true;
+ int flag = 1;
+
+ gdcm::SmartPointer sq = new gdcm::SequenceOfItems();
+ sq->SetLengthToUndefined();
+
+ int resolutions;
+
+ if(nres==0)
+ resolutions = No_Of_Resolutions(filename);
+ else
+ resolutions = nres;
+
+ for(int i = resolutions-1 ; i>=0; --i)
+ {
+ b = b && Write_Resolution( theStreamWriter, filename, i, of ,flag,sq);
+ flag = 0;
+ }
+ return b;
+}
+
+
+
+static void PrintVersion()
+{
+ std::cout << "gdcmstream: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmstream [OPTION] input.dcm output.dcm" << std::endl;
+}
+
+static void end_of_WSIFile(std::ostream& of)
+{
+ uint16_t firstTag1 = 0xfffe;
+ uint16_t secondTag1 = 0xe0dd;
+ uint32_t thirdTag1 = 0x00000000;
+ //uint16_t fourthTag1 = 0xffff;
+ const int theBufferSize1 = 2*sizeof(uint16_t)+sizeof(uint32_t);
+ char* tmpBuffer2 = new char[theBufferSize1];
+ memcpy(&(tmpBuffer2[0]), &firstTag1, sizeof(uint16_t));
+ memcpy(&(tmpBuffer2[sizeof(uint16_t)]), &secondTag1, sizeof(uint16_t));
+ memcpy(&(tmpBuffer2[2*sizeof(uint16_t)]), &thirdTag1, sizeof(uint32_t));
+ //memcpy(&(tmpBuffer2[3*sizeof(uint16_t)]), &fourthTag1, sizeof(uint16_t));
+ assert( of && !of.eof() && of.good() );
+ of.write(tmpBuffer2, theBufferSize1);
+ of.flush();
+ assert( of );
+}
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+ //std::string filename;
+ //std::string outfilename;
+ gdcm::Filename filename;
+ gdcm::Filename outfilename;
+
+ std::string root;
+ int rootuid = 0;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ int nres = 0;
+ int res = 0;
+ int tile = 0;
+ std::vector start;
+ std::vector end;
+
+ while (1) {
+
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+
+ // General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {"resolution", 1, &nres, 1},
+ {"resolution-only", 1, &res, 1},
+ {"roi-start", 1, &tile, 1},
+ {"roi-end", 1, &tile, 1},
+ {0, 0, 0, 0}
+ };
+ c = getopt_long (argc, argv, "i:o:VWDEhv:r:n:",long_options, &option_index);
+
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.IsEmpty() );
+ filename = optarg;
+ }
+ //printf (" with arg %s, index = %d", optarg, option_index)
+ else if( option_index == 1 ) /* input */
+ {
+ assert( strcmp(s, "output") == 0 );
+ assert( outfilename.IsEmpty() );
+ outfilename = optarg;
+ }
+ //printf (" with arg %s, index = %d", optarg, option_index);
+
+ else if( option_index == 8 ) /* number of resolution */
+ {
+ assert( strcmp(s, "resolution") == 0 );
+ nres = atoi(optarg);
+ }
+
+ else if( option_index == 9 ) /* number of resolution */
+ {
+ assert( strcmp(s, "resolution-only") == 0 );
+ res = atoi(optarg);
+ }
+
+ else if( option_index == 10 ) /* tile */
+ {
+ assert( strcmp(s, "roi-start") == 0 );
+ tile = 1;
+ unsigned int n = readvector(start, optarg);
+ assert( n == 2 ); (void)n;
+ }
+ else if( option_index == 11 ) /* tile */
+ {
+ assert( strcmp(s, "roi-end") == 0 );
+ tile = 1;
+ unsigned int n = readvector(end, optarg);
+ assert( n == 2 ); (void)n;
+ }
+
+ //printf ("\n");
+ }
+
+ }
+ break;
+
+ case 'i':
+ assert( filename.IsEmpty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ assert( outfilename.IsEmpty() );
+ outfilename = optarg;
+ break;
+
+ // General option
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case 'r':
+ nres = atoi(optarg);
+ break;
+
+ case 'n':
+ res = atoi(optarg);
+ break;
+
+
+ case '?':
+ break;
+
+ default:
+
+ printf ("?? getopt returned character code 0%o ??\n", c);
+
+ }
+ }
+
+ // For now only support one input / one output
+ if (optind < argc)
+ {
+ //printf ("non-option ARGV-elements: ");
+ //std::cout << "HERE";
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind]);
+ files.push_back( argv[optind] );
+ }
+
+ //printf ("\n");
+ if( files.size() == 2 && filename.IsEmpty() && outfilename.IsEmpty() )
+ {
+ filename = files[0].c_str();
+ outfilename = files[ files.size() - 1 ].c_str();
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }//if (optind < argc)
+
+ if( version )
+ {
+ std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ std::cout << "help here" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.IsEmpty())
+ {
+ std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ if( outfilename.IsEmpty() )
+ {
+ std::cerr << "Need output file (-o)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warningerror are turned on:
+
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmstream" );
+
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+
+ }
+
+ if( rootuid )
+ {
+ // root is set either by the cmd line option or the env var
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ const char *inputextension = filename.GetExtension();
+ //const char *outputextension = outfilename.GetExtension();
+
+ gdcm::StreamImageWriter theStreamWriter;
+ std::ofstream of;
+ of.open( outfilename, std::ios::out | std::ios::binary );
+ theStreamWriter.SetStream(of);
+
+ if( inputextension )
+ {
+ if( gdcm::System::StrCaseCmp(inputextension,".jp2") == 0 )
+ {
+ if( !Different_Resolution_From_jp2( theStreamWriter, filename,of,nres ) ) return 1;
+ end_of_WSIFile(of);
+ }
+
+ if( gdcm::System::StrCaseCmp(inputextension,".dcm") == 0 )
+ {
+ Different_Resolution_From_DICOM( theStreamWriter, filename, of, res , tile , start , end);
+ //if(!StreamImageRead_Write( theStreamWriter, filename, 0)) return 1;
+ end_of_WSIFile(of);
+ }
+ }
+
+ // gdcm::StreamImageReader ...
+ return 0;
+}
diff --git a/gdcm/Applications/Cxx/gdcmtar.cxx b/gdcm/Applications/Cxx/gdcmtar.cxx
new file mode 100644
index 0000000..b27b2ed
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmtar.cxx
@@ -0,0 +1,1300 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * tar is a UNIX program for archiving.
+ * Two types of operations are possible: concatenate / extract
+ * Thus the name of 'gdcmtar' to concatenate a list of 2D slices into a multi frames
+ * and the other way around: extract 2D slices from a multi frames image
+ * It also support the fake multi frame image (CSA MOSAIC)
+ */
+
+#include "gdcmReader.h"
+#include "gdcmVersion.h"
+#include "gdcmImageReader.h"
+#include "gdcmDataElement.h"
+#include "gdcmImageWriter.h"
+#include "gdcmSplitMosaicFilter.h"
+#include "gdcmFilename.h"
+#include "gdcmFilenameGenerator.h"
+#include "gdcmDirectionCosines.h"
+#include "gdcmImageHelper.h"
+#include "gdcmUIDGenerator.h"
+#include "gdcmUIDs.h"
+#include "gdcmGlobal.h"
+#include "gdcmDirectory.h"
+#include "gdcmScanner.h"
+#include "gdcmIPPSorter.h"
+#include "gdcmAttribute.h"
+
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+static void PrintVersion()
+{
+ std::cout << "gdcmtar: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmtar [OPTION] [FILE]" << std::endl;
+ std::cout << "Concatenate/Extract DICOM files.\n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input DICOM filename" << std::endl;
+ std::cout << " -o --output DICOM filename" << std::endl;
+ std::cout << "Options:" << std::endl;
+ std::cout << " --enhance enhance (default)" << std::endl;
+ std::cout << " -U --unenhance unenhance" << std::endl;
+ std::cout << " -M --mosaic Split SIEMENS Mosaic image into multiple frames." << std::endl;
+ std::cout << " -p --pattern Specify trailing file pattern." << std::endl;
+ std::cout << " --root-uid Root UID." << std::endl;
+ //std::cout << " --resources-path Resources path." << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Env var:" << std::endl;
+ std::cout << " GDCM_ROOT_UID Root UID" << std::endl;
+ //std::cout << " GDCM_RESOURCES_PATH path pointing to resources files (Part3.xml, ...)" << std::endl;
+}
+
+/*
+ * The following example is a basic sorted which should work in generic cases.
+ * It sort files based on:
+ * Study Instance UID
+ * Series Instance UID
+ * Frame of Reference UID
+ * Image Orientation (Patient)
+ * Image Position (Patient) (Sorting based on IPP + IOP)
+ */
+
+namespace gdcm {
+ const Tag T0(0x0008,0x0016); // SOP Class UID
+ const Tag T1(0x0020,0x000d); // Study Instance UID
+ const Tag T2(0x0020,0x000e); // Series Instance UID
+ const Tag T3(0x0020,0x0052); // Frame of Reference UID
+ const Tag T4(0x0020,0x0037); // Image Orientation (Patient)
+
+class DiscriminateVolume
+{
+private:
+ static const bool debuggdcmtar = false;
+ std::vector< Directory::FilenamesType > SortedFiles;
+ std::vector< Directory::FilenamesType > UnsortedFiles;
+
+ Directory::FilenamesType GetAllFilenamesFromTagToValue(
+ Scanner const & s, Directory::FilenamesType const &filesubset, Tag const &t, const char *valueref)
+{
+ Directory::FilenamesType theReturn;
+ if( valueref )
+ {
+ size_t len = strlen( valueref );
+ Directory::FilenamesType::const_iterator file = filesubset.begin();
+ for(; file != filesubset.end(); ++file)
+ {
+ const char *filename = file->c_str();
+ const char * value = s.GetValue(filename, t);
+ if( value && strncmp(value, valueref, len ) == 0 )
+ {
+ theReturn.push_back( filename );
+ }
+ }
+ }
+ return theReturn;
+}
+
+void ProcessAIOP(Scanner const & , Directory::FilenamesType const & subset, const char *iopval)
+{
+ if( debuggdcmtar )
+ std::cout << "IOP: " << iopval << std::endl;
+ IPPSorter ipp;
+ ipp.SetComputeZSpacing( true );
+ ipp.SetZSpacingTolerance( 1e-3 ); // ??
+ bool b = ipp.Sort( subset );
+ if( !b )
+ {
+ // If you reach here this means you need one more parameter to discriminiat this
+ // series. Eg. T1 / T2 intertwinted. Multiple Echo (0018,0081)
+ if( debuggdcmtar )
+ {
+ std::cerr << "Failed to sort: " << subset.begin()->c_str() << std::endl;
+ for(
+ Directory::FilenamesType::const_iterator file = subset.begin();
+ file != subset.end(); ++file)
+ {
+ std::cerr << *file << std::endl;
+ }
+ }
+ UnsortedFiles.push_back( subset );
+ return ;
+ }
+ if( debuggdcmtar )
+ ipp.Print( std::cout );
+ SortedFiles.push_back( ipp.GetFilenames() );
+}
+
+void ProcessAFrameOfRef(Scanner const & s, Directory::FilenamesType const & subset, const char * frameuid)
+{
+ // In this subset of files (belonging to same series), let's find those
+ // belonging to the same Frame ref UID:
+ Directory::FilenamesType files = GetAllFilenamesFromTagToValue(
+ s, subset, T3, frameuid);
+
+ std::set< std::string > iopset;
+
+ for(
+ Directory::FilenamesType::const_iterator file = files.begin();
+ file != files.end(); ++file)
+ {
+ //std::cout << *file << std::endl;
+ const char * value = s.GetValue(file->c_str(), gdcm::T4 );
+ assert( value );
+ iopset.insert( value );
+ }
+ size_t n = iopset.size();
+ if ( n == 0 )
+ {
+ assert( files.empty() );
+ return;
+ }
+
+ if( debuggdcmtar )
+ std::cout << "Frame of Ref: " << frameuid << std::endl;
+ if ( n == 1 )
+ {
+ ProcessAIOP(s, files, iopset.begin()->c_str() );
+ }
+ else
+ {
+ const char *f = files.begin()->c_str();
+ if( debuggdcmtar )
+ std::cerr << "More than one IOP: " << f << std::endl;
+ // Make sure that there is actually 'n' different IOP
+ gdcm::DirectionCosines ref;
+ gdcm::DirectionCosines dc;
+ for(
+ std::set< std::string >::const_iterator it = iopset.begin();
+ it != iopset.end(); ++it )
+ {
+ ref.SetFromString( it->c_str() );
+ for(
+ Directory::FilenamesType::const_iterator file = files.begin();
+ file != files.end(); ++file)
+ {
+ std::string value = s.GetValue(file->c_str(), gdcm::T4 );
+ if( value != it->c_str() )
+ {
+ dc.SetFromString( value.c_str() );
+ const double crossdot = ref.CrossDot(dc);
+ const double eps = std::fabs( 1. - crossdot );
+ if( eps < 1e-6 )
+ {
+ std::cerr << "Problem with IOP discrimination: " << file->c_str()
+ << " " << it->c_str() << std::endl;
+ return;
+ }
+ }
+ }
+ }
+ // If we reach here this means there is actually 'n' different IOP
+ for(
+ std::set< std::string >::const_iterator it = iopset.begin();
+ it != iopset.end(); ++it )
+ {
+ const char *iopvalue = it->c_str();
+ Directory::FilenamesType iopfiles = GetAllFilenamesFromTagToValue(
+ s, files, T4, iopvalue );
+ ProcessAIOP(s, iopfiles, iopvalue );
+ }
+ }
+}
+
+void ProcessASeries(Scanner const & s, const char * seriesuid)
+{
+ if( debuggdcmtar )
+ std::cout << "Series: " << seriesuid << std::endl;
+ // let's find all files belonging to this series:
+ Directory::FilenamesType seriesfiles = GetAllFilenamesFromTagToValue(
+ s, s.GetFilenames(), T2, seriesuid);
+
+ gdcm::Scanner::ValuesType vt3 = s.GetValues(T3);
+ for(
+ gdcm::Scanner::ValuesType::const_iterator it = vt3.begin()
+ ; it != vt3.end(); ++it )
+ {
+ ProcessAFrameOfRef(s, seriesfiles, it->c_str());
+ }
+}
+
+void ProcessAStudy(Scanner const & s, const char * studyuid)
+{
+ if( debuggdcmtar )
+ std::cout << "Study: " << studyuid << std::endl;
+ gdcm::Scanner::ValuesType vt2 = s.GetValues(T2);
+ if( vt2.empty() )
+ std::cerr << "No Series Found" << std::endl;
+ for(
+ gdcm::Scanner::ValuesType::const_iterator it = vt2.begin()
+ ; it != vt2.end(); ++it )
+ {
+ ProcessASeries(s, it->c_str());
+ }
+}
+public:
+
+void Print( std::ostream & os )
+{
+ os << "Sorted Files: " << std::endl;
+ for(
+ std::vector< Directory::FilenamesType >::const_iterator it = SortedFiles.begin();
+ it != SortedFiles.end(); ++it )
+ {
+ os << "Group: " << std::endl;
+ for(
+ Directory::FilenamesType::const_iterator file = it->begin();
+ file != it->end(); ++file)
+ {
+ os << *file << std::endl;
+ }
+ }
+ os << "Unsorted Files: " << std::endl;
+ for(
+ std::vector< Directory::FilenamesType >::const_iterator it = UnsortedFiles.begin();
+ it != UnsortedFiles.end(); ++it )
+ {
+ os << "Group: " << std::endl;
+ for(
+ Directory::FilenamesType::const_iterator file = it->begin();
+ file != it->end(); ++file)
+ {
+ os << *file << std::endl;
+ }
+ }
+
+}
+
+ std::vector< Directory::FilenamesType > const & GetSortedFiles() const { return SortedFiles; }
+ std::vector< Directory::FilenamesType > const & GetUnsortedFiles() const { return UnsortedFiles; }
+
+void ProcessIntoVolume( Scanner const & s )
+{
+ gdcm::Scanner::ValuesType vt1 = s.GetValues( gdcm::T1 );
+ for(
+ gdcm::Scanner::ValuesType::const_iterator it = vt1.begin()
+ ; it != vt1.end(); ++it )
+ {
+ ProcessAStudy( s, it->c_str() );
+ }
+
+}
+
+};
+
+static bool ConcatenateImages(Image &im1, Image const &im2)
+{
+ DataElement& de1 = im1.GetDataElement();
+ if( de1.GetByteValue() )
+ {
+ const ByteValue *bv1 = de1.GetByteValue();
+ std::vector v1 = *bv1;
+ const DataElement& de2 = im2.GetDataElement();
+ const ByteValue *bv2 = de2.GetByteValue();
+ const std::vector & v2 = *bv2;
+ v1.insert( v1.end(), v2.begin(), v2.end() );
+
+ de1.SetByteValue(&v1[0], (uint32_t)v1.size());
+ }
+ else if( de1.GetSequenceOfFragments() )
+ {
+ SequenceOfFragments *sqf1 = de1.GetSequenceOfFragments();
+ assert( sqf1 );
+ const DataElement& de2 = im2.GetDataElement();
+ const SequenceOfFragments *sqf2 = de2.GetSequenceOfFragments();
+ assert( sqf2 );
+ assert( sqf2->GetNumberOfFragments() == 1 );
+ const Fragment& frag = sqf2->GetFragment(0);
+ sqf1->AddFragment(frag);
+ }
+ else
+ {
+ return false;
+ }
+
+ // update meta info
+ unsigned int z = im1.GetDimension(2);
+ im1.SetDimension(2, z + 1 );
+ return true;
+}
+
+} // namespace gdcm
+
+
+static int MakeImageEnhanced( std::string const & filename, std::string const &outfilename )
+{
+ if( !gdcm::System::FileIsDirectory(filename.c_str()) )
+ {
+ std::cerr << "Input needs to be directory" << std::endl;
+ return 1;
+ }
+
+ gdcm::Directory d;
+ d.Load( filename.c_str(), true ); // recursive !
+
+ gdcm::Scanner s;
+ s.AddTag( gdcm::T0 );
+ s.AddTag( gdcm::T1 );
+ s.AddTag( gdcm::T2 );
+ s.AddTag( gdcm::T3 );
+ s.AddTag( gdcm::T4 );
+ bool b = s.Scan( d.GetFilenames() );
+ if( !b )
+ {
+ std::cerr << "Scanner failed" << std::endl;
+ return 1;
+ }
+
+ // For now only accept MR Image Storage
+ gdcm::Scanner::ValuesType vt = s.GetValues(gdcm::T0);
+ if( vt.size() != 1 ) return 1;
+
+ const char *sop = vt.begin()->c_str();
+ gdcm::MediaStorage msorig = gdcm::MediaStorage::GetMSType( sop );
+ if( msorig != gdcm::MediaStorage::MRImageStorage
+ && msorig != gdcm::MediaStorage::CTImageStorage )
+ {
+ std::cerr << "Sorry MediaStorage not supported: [" << sop << "]" << std::endl;
+ return 1;
+ }
+
+ gdcm::DiscriminateVolume dv;
+ dv.ProcessIntoVolume( s );
+// dv.Print( std::cout );
+
+ // gdcm::DataElement &de = im.GetImage().GetDataElement();
+ std::vector< gdcm::Directory::FilenamesType > const &sorted = dv.GetSortedFiles();
+ if( !gdcm::System::MakeDirectory( outfilename.c_str() ) )
+ {
+ std::cerr << "Could not create dir: " << outfilename << std::endl;
+ return 1;
+ }
+ for(
+ std::vector< gdcm::Directory::FilenamesType >::const_iterator it = sorted.begin();
+ it != sorted.end(); ++it )
+ {
+ gdcm::ImageWriter im0;
+
+ gdcm::Directory::FilenamesType const & files = *it;
+ gdcm::Directory::FilenamesType::const_iterator file = files.begin();
+
+ const char *reffile = file->c_str();
+ // construct the target dir:
+ const char* studyuid = s.GetValue(reffile, gdcm::T1);
+ const char* seriesuid = s.GetValue(reffile, gdcm::T2);
+ const char* frameuid = s.GetValue(reffile, gdcm::T3);
+ std::string targetdir = outfilename;
+ targetdir += '/';
+ targetdir += studyuid;
+ targetdir += '/';
+ targetdir += seriesuid;
+ targetdir += '/';
+ targetdir += frameuid;
+ // construct the target name:
+ std::string targetname = targetdir;
+
+ targetdir += "/old/";
+
+ // make sure the dir exist first:
+ if( !gdcm::System::MakeDirectory( targetdir.c_str() ) )
+ {
+ std::cerr << "Could not create dir: " << targetdir << std::endl;
+ return 1;
+ }
+
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( files.size() );
+ fg.SetPrefix( targetdir.c_str() );
+ fg.SetPattern( "%04d.dcm" );
+ if( !fg.Generate() )
+ {
+ assert( 0 );
+ }
+
+ gdcm::ImageReader reader0;
+ reader0.SetFileName( reffile );
+ if( !reader0.Read() )
+ {
+ assert( 0 );
+ }
+ gdcm::Image ¤tim = reader0.GetImage();
+ assert( currentim.GetNumberOfDimensions( ) == 2 );
+ currentim.SetNumberOfDimensions( 3 );
+ size_t count = 0;
+
+ //gdcm::ImageWriter writer;
+ gdcm::Writer writer0;
+ writer0.SetFileName( fg.GetFilename( count ) );
+ writer0.SetFile( reader0.GetFile() );
+ writer0.GetFile().GetHeader().Clear();
+ if( !writer0.Write() )
+ {
+ assert( 0 );
+ }
+ ++file;
+ ++count;
+
+ for( ; file != files.end(); ++file, ++count )
+ {
+ gdcm::ImageReader reader;
+ reader.SetFileName( file->c_str() );
+ if( !reader.Read() )
+ {
+ assert( 0 );
+ }
+ const gdcm::Image &im = reader.GetImage();
+
+ //gdcm::ImageWriter writer;
+ gdcm::Writer writer;
+ writer.SetFileName( fg.GetFilename( count ) );
+ writer.SetFile( reader.GetFile() );
+ writer.GetFile().GetHeader().Clear();
+ if( !writer.Write() )
+ {
+ assert( 0 );
+ }
+
+ if( !ConcatenateImages(currentim, im) )
+ {
+ assert( 0 );
+ }
+ }
+
+ im0.SetFileName( (targetname + "/new.dcm").c_str() );
+ // im.SetFile( reader.GetFile() );
+
+ gdcm::DataSet &ds = im0.GetFile().GetDataSet();
+
+ gdcm::MediaStorage ms;
+ switch( msorig )
+ {
+ case gdcm::MediaStorage::CTImageStorage:
+ ms = gdcm::MediaStorage::EnhancedCTImageStorage;
+ break;
+ case gdcm::MediaStorage::MRImageStorage:
+ ms = gdcm::MediaStorage::EnhancedMRImageStorage;
+ break;
+ default:
+ return 1;
+ }
+
+ gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016) );
+ const char* msstr = gdcm::MediaStorage::GetMSString(ms);
+ de.SetByteValue( msstr, (uint32_t)strlen(msstr) );
+ de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() );
+ ds.Insert( de );
+
+ im0.SetImage( currentim );
+ if( !im0.Write() )
+ {
+ std::cerr << "Could not write: " << std::endl;
+ return 1;
+ }
+
+ }
+
+ std::vector< gdcm::Directory::FilenamesType > const &unsorted = dv.GetUnsortedFiles();
+ if( !unsorted.empty() )
+ {
+ std::string targetdir3 = outfilename;
+ targetdir3 += "/unhandled/";
+ if( !gdcm::System::MakeDirectory( targetdir3.c_str() ) )
+ {
+ std::cerr << "Could not create dir: " << outfilename << std::endl;
+ return 1;
+ }
+ std::cerr << "Could not process the following files (please report): " << std::endl;
+ std::vector< gdcm::Directory::FilenamesType >::const_iterator it = unsorted.begin();
+ for( ; it != unsorted.end(); ++it )
+ {
+ gdcm::Directory::FilenamesType const & files = *it;
+ gdcm::Directory::FilenamesType::const_iterator file = files.begin();
+ for( ; file != files.end(); ++file )
+ {
+ const char *f = file->c_str();
+ std::string targetdir2 = outfilename;
+ targetdir2 += "/unhandled/";
+ gdcm::Filename fn2( f );
+ const char *outfn2 = fn2.GetName();
+ targetdir2 += outfn2;
+ //std::cerr << f << " -> " << targetdir2 << std::endl;
+ std::ifstream in( f, std::ios::binary );
+ std::ofstream out( targetdir2.c_str() , std::ios::binary );
+ out << in.rdbuf();
+ }
+ }
+ }
+
+ return 0;
+}
+
+namespace gdcm
+{
+
+static const DataElement &GetNestedDataElement( const DataSet &ds, const Tag & t1, const Tag & t2 )
+{
+ assert( ds.FindDataElement( t1 ) );
+ SmartPointer sqi1 = ds.GetDataElement( t1 ).GetValueAsSQ();
+ assert( sqi1 );
+ const Item &item1 = sqi1->GetItem(1);
+ const DataSet & ds1 = item1.GetNestedDataSet();
+ assert( ds1.FindDataElement( t2 ) );
+ return ds1.GetDataElement( t2 );
+}
+
+static bool RemapSharedIntoOld( gdcm::DataSet & ds,
+ SequenceOfItems *sfgs,
+ SequenceOfItems *pffgs,
+ unsigned int index )
+{
+ assert( sfgs );
+ assert( pffgs );
+
+ assert( sfgs->GetNumberOfItems() == 1 );
+ Item const &item1 = sfgs->GetItem( 1 );
+ const DataSet & sfgs_ds = item1.GetNestedDataSet();
+#if 1
+ // Repetition Time
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9112), Tag(0x0018,0x0080) ) );
+ // Echo Train Length
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9112), Tag(0x0018,0x0091) ) );
+ // Flip Angle
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9112), Tag(0x0018,0x1314) ) );
+ // Number of Averages
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9119), Tag(0x0018,0x0083) ) );
+
+ // Percent Sampling
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9125), Tag(0x0018,0x0093) ) );
+ // Percent Phase Field of View
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9125), Tag(0x0018,0x0094) ) );
+ // Receive Coil Name
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9042), Tag(0x0018,0x1250) ) );
+ // Transmit Coil Name
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9049), Tag(0x0018,0x1251) ) );
+ // InPlanePhaseEncodingDirection
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9125), Tag(0x0018,0x1312) ) );
+ // TransmitterFrequency
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9006), Tag(0x0018,0x9098) ) );
+ // InversionRecovery
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9115), Tag(0x0018,0x9009) ) );
+ // FlowCompensation
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9115), Tag(0x0018,0x9010) ) );
+ // ReceiveCoilType
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9042), Tag(0x0018,0x9043) ) );
+ // QuadratureReceiveCoil
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9042), Tag(0x0018,0x9044) ) );
+ // SlabThickness
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9107), Tag(0x0018,0x9104) ) );
+ // MultiCoilDefinitionSequence
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9042), Tag(0x0018,0x9045) ) );
+ // SlabOrientation
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9107), Tag(0x0018,0x9105) ) );
+ // MidSlabPosition
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9107), Tag(0x0018,0x9106) ) );
+ // OperatingModeSequence
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9112), Tag(0x0018,0x9176) ) );
+ // MRAcquisitionPhaseEncodingStepsOutOf
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9125), Tag(0x0018,0x9232) ) );
+ // SpecificAbsorptionRateSequence
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0018,0x9112), Tag(0x0018,0x9239) ) );
+ // AnatomicRegionSequence
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0020,0x9071), Tag(0x0008,0x2218) ) );
+ // Purpose of Reference Code Sequence
+ // FIXME what if there is multiple purpose of rcs ?
+ ds.Replace( GetNestedDataElement(sfgs_ds, Tag(0x0008,0x1140), Tag(0x0040,0xa170) ) );
+#else
+ for(
+ DataSet::ConstIterator it = sfgs_ds.Begin();
+ it != sfgs_ds.End(); ++it )
+ {
+ ds.Replace( *it );
+ }
+#endif
+
+ Item const &item2 = pffgs->GetItem( index + 1 );
+ const DataSet & pffgs_ds = item2.GetNestedDataSet();
+
+#if 1
+ // Effective Echo Time
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0018,0x9114), Tag(0x0018,0x9082) ) );
+ // -> should also be Echo Time
+ // Nominal Cardiac Trigger Delay Time
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0018,0x9118), Tag(0x0020,0x9153) ) );
+ // Metabolite Map Description
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0018,0x9152), Tag(0x0018,0x9080) ) );
+ // IPP
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9113), Tag(0x0020,0x0032) ) );
+ // IOP
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9116), Tag(0x0020,0x0037) ) );
+ // Slice Thickness
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9110), Tag(0x0018,0x0050) ) );
+ // Pixel Spacing
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9110), Tag(0x0028,0x0030) ) );
+
+ // window level
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9132), Tag(0x0028,0x1050) ) );
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9132), Tag(0x0028,0x1051) ) );
+
+ // rescale slope/intercept
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9145), Tag(0x0028,0x1052) ) );
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9145), Tag(0x0028,0x1053) ) );
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0028,0x9145), Tag(0x0028,0x1054) ) );
+
+ // FrameReferenceDateTime
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0018,0x9151) ) );
+ // FrameAcquisitionDuration
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0018,0x9220) ) );
+ // TemporalPositionIndex
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0020,0x9128) ) );
+ // InStackPositionNumber
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0020,0x9057) ) );
+ // FrameType
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0018,0x9226), Tag(0x0008,0x9007) ) );
+ // DimensionIndexValues
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0020,0x9157) ) );
+ // FrameAcquisitionDateTime
+ ds.Replace( GetNestedDataElement(pffgs_ds, Tag(0x0020,0x9111), Tag(0x0018,0x9074) ) );
+ // Nominal Cardiac Trigger Delay Time -> Trigger Time
+ //const DataElement &NominalCardiacTriggerDelayTime =
+ // GetNestedDataElement(pffgs_ds, Tag(0x0018,0x9226), Tag(0x0008,0x9007) );
+#endif
+
+ // (0020,9228) UL 158 # 4, 1 ConcatenationFrameOffsetNumber
+ gdcm::Attribute<0x0020,0x9228> at = { index };
+ ds.Replace( at.GetAsDataElement() );
+
+ return true;
+}
+
+} // namespace gdcm
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+
+ int rootuid = 0;
+ std::string filename;
+ std::string outfilename;
+ std::string root;
+ int resourcespath = 0;
+ int mosaic = 0;
+ int enhance = 1;
+ int unenhance = 0;
+ std::string xmlpath;
+
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+
+ std::string pattern;
+ while (1) {
+ //int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"mosaic", 0, &mosaic, 1}, // split siemens mosaic into multiple frames
+ {"pattern", 1, 0, 0}, // p
+ {"enhance", 0, &enhance, 1}, // unenhance
+ {"unenhance", 0, &unenhance, 1}, // unenhance
+ {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
+ //{"resources-path", 0, &resourcespath, 1},
+
+// General options !
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "i:o:MUp:VWDEhv",
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ //printf ("option %s", s);
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( filename.empty() );
+ filename = optarg;
+ }
+ else if( option_index == 3 ) /* pattern */
+ {
+ assert( strcmp(s, "pattern") == 0 );
+ assert( pattern.empty() );
+ pattern = optarg;
+ }
+ else if( option_index == 6 ) /* root uid */
+ {
+ assert( strcmp(s, "root-uid") == 0 );
+ root = optarg;
+ }
+ else if( option_index == 7 ) /* resourcespath */
+ {
+ assert( strcmp(s, "resources-path") == 0 );
+ assert( xmlpath.empty() );
+ xmlpath = optarg;
+ }
+ else
+ {
+ printf (" with arg %s, index = %d", optarg, option_index);
+ assert(0);
+ }
+ //printf (" with arg %s, index = %d", optarg, option_index);
+ }
+ //printf ("\n");
+ }
+ break;
+
+ case 'i':
+ assert( filename.empty() );
+ filename = optarg;
+ break;
+
+ case 'o':
+ assert( outfilename.empty() );
+ outfilename = optarg;
+ break;
+
+ case 'U':
+ //assert( outfilename.empty() );
+ //outfilename = optarg;
+ //printf ("option unenhance \n");
+ unenhance = 1;
+ break;
+
+ case 'M':
+ //assert( outfilename.empty() );
+ //outfilename = optarg;
+ mosaic = 1;
+ break;
+
+ case 'p':
+ assert( pattern.empty() );
+ pattern = optarg;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ // For now only support one input / one output
+ if (optind < argc)
+ {
+ std::vector files;
+ while (optind < argc)
+ {
+ //printf ("%s\n", argv[optind++]);
+ files.push_back( argv[optind++] );
+ }
+ //printf ("\n");
+ if( files.size() == 2
+ && filename.empty()
+ && outfilename.empty()
+ )
+ {
+ filename = files[0];
+ outfilename = files[1];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( version )
+ {
+ //std::cout << "version" << std::endl;
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ //std::cout << "help" << std::endl;
+ PrintHelp();
+ return 0;
+ }
+
+ if( filename.empty() )
+ {
+ //std::cerr << "Need input file (-i)\n";
+ PrintHelp();
+ return 1;
+ }
+ if( outfilename.empty() )
+ {
+ //std::cerr << "Need output file (-o)\n";
+ PrintHelp();
+ return 1;
+ }
+
+ // Debug is a little too verbose
+ gdcm::Trace::SetDebug( (debug > 0 ? true : false));
+ gdcm::Trace::SetWarning( (warning > 0 ? true : false));
+ gdcm::Trace::SetError( (error > 0 ? true : false));
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( (verbose > 0 ? true : false) );
+ gdcm::Trace::SetError( (verbose > 0 ? true : false) );
+ }
+
+ gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcmtar" );
+ if( !rootuid )
+ {
+ // only read the env var is no explicit cmd line option
+ // maybe there is an env var defined... let's check
+ const char *rootuid_env = getenv("GDCM_ROOT_UID");
+ if( rootuid_env )
+ {
+ rootuid = 1;
+ root = rootuid_env;
+ }
+ }
+ if( rootuid )
+ {
+ if( !gdcm::UIDGenerator::IsValid( root.c_str() ) )
+ {
+ std::cerr << "specified Root UID is not valid: " << root << std::endl;
+ return 1;
+ }
+ gdcm::UIDGenerator::SetRoot( root.c_str() );
+ }
+
+ if( unenhance && false )
+ {
+ gdcm::Global& g = gdcm::Global::GetInstance();
+ // First thing we need to locate the XML dict
+ // did the user requested to look XML file in a particular directory ?
+ if( !resourcespath )
+ {
+ const char *xmlpathenv = getenv("GDCM_RESOURCES_PATH");
+ if( xmlpathenv )
+ {
+ // Make sure to look for XML dict in user explicitly specified dir first:
+ xmlpath = xmlpathenv;
+ resourcespath = 1;
+ }
+ }
+ if( resourcespath )
+ {
+ // xmlpath is set either by the cmd line option or the env var
+ if( !g.Prepend( xmlpath.c_str() ) )
+ {
+ std::cerr << "specified Resources Path is not valid: " << xmlpath << std::endl;
+ return 1;
+ }
+ }
+
+ // All set, then load the XML files:
+ if( !g.LoadResourcesFiles() )
+ {
+ return 1;
+ }
+
+ //const gdcm::Defs &defs = g.GetDefs();
+ }
+
+
+ if( mosaic )
+ {
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "could not read: " << filename << std::endl;
+ return 1;
+ }
+
+ gdcm::SplitMosaicFilter filter;
+ filter.SetImage( reader.GetImage() );
+ filter.SetFile( reader.GetFile() );
+ bool b = filter.Split();
+ if( !b )
+ {
+ std::cerr << "Could not split : " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::Image &image = filter.GetImage();
+ const unsigned int *dims = image.GetDimensions();
+ const gdcm::DataElement &pixeldata = image.GetDataElement();
+ const gdcm::ByteValue *bv = pixeldata.GetByteValue();
+ unsigned long slice_len = image.GetBufferLength() / dims[2];
+ //assert( image.GetBufferLength() == bv->GetLength() );
+
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( dims[2] );
+ fg.SetPrefix( outfilename.c_str() );
+ fg.SetPattern( pattern.c_str() );
+ if( !fg.Generate() )
+ {
+ std::cerr << "could not generate filenames" << std::endl;
+ return 1;
+ }
+ const double *cosines = image.GetDirectionCosines();
+ gdcm::DirectionCosines dc( cosines );
+ double normal[3];
+ dc.Cross( normal );
+ const double *origin = image.GetOrigin();
+ double zspacing = image.GetSpacing(2);
+
+ for(unsigned int i = 0; i < dims[2]; ++i)
+ {
+ double new_origin[3];
+ for (int j = 0; j < 3; j++)
+ {
+ // the n'th slice is n * z-spacing aloung the IOP-derived
+ // z-axis
+ new_origin[j] = origin[j] + normal[j] * i * zspacing;
+ }
+
+ const char *outfilenamei = fg.GetFilename(i);
+ gdcm::ImageWriter writer;
+ writer.SetFileName( outfilenamei );
+ //writer.SetFile( filter.GetFile() );
+ writer.SetFile( reader.GetFile() );
+
+ //
+ //writer.SetImage( filter.GetImage() );
+ gdcm::Image &slice = writer.GetImage();
+ slice = filter.GetImage();
+ slice.SetOrigin( new_origin );
+ slice.SetNumberOfDimensions( 2 );
+ assert( slice.GetPixelFormat() == filter.GetImage().GetPixelFormat() );
+ slice.SetSpacing(2, filter.GetImage().GetSpacing(2) );
+ //slice.Print( std::cout );
+ gdcm::DataElement &pd = slice.GetDataElement();
+ const char *sliceptr = bv->GetPointer() + i * slice_len;
+ pd.SetByteValue( sliceptr, (uint32_t)slice_len);
+
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilename << std::endl;
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+ else if ( unenhance )
+ {
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "could not read: " << filename << std::endl;
+ return 1;
+ }
+
+ gdcm::File &file = reader.GetFile();
+ gdcm::DataSet &ds = file.GetDataSet();
+ gdcm::MediaStorage ms;
+ ms.SetFromFile(file);
+ if( ms.IsUndefined() )
+ {
+ std::cerr << "Unknown MediaStorage" << std::endl;
+ return 1;
+ }
+
+ gdcm::UIDs uid;
+ uid.SetFromUID( ms.GetString() );
+
+ if( uid != gdcm::UIDs::EnhancedMRImageStorage )
+ {
+ std::cerr << "MediaStorage is not handled " << ms << " [" << uid.GetName() << "]" << std::endl;
+ return 1;
+ }
+
+ // Preserve info:
+ gdcm::DataElement oldsopclassuid = ds.GetDataElement( gdcm::Tag(0x8,0x16) );
+ gdcm::DataElement oldinstanceuid = ds.GetDataElement( gdcm::Tag(0x8,0x18) );
+
+ // Ok then change it old Old MR Image Storage
+ gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016) );
+ ms = gdcm::MediaStorage::MRImageStorage;
+ const char* msstr = gdcm::MediaStorage::GetMSString(ms);
+ de.SetByteValue( msstr, (uint32_t)strlen(msstr) );
+ de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() );
+ ds.Replace( de );
+
+ const gdcm::Image &image = reader.GetImage();
+ const unsigned int *dims = image.GetDimensions();
+ //std::cout << image << std::endl;
+ const gdcm::DataElement &pixeldata = image.GetDataElement();
+ //const gdcm::ByteValue *bv = pixeldata.GetByteValue();
+ gdcm::SmartPointer bv = (gdcm::ByteValue*)pixeldata.GetByteValue();
+ unsigned long slice_len = image.GetBufferLength() / dims[2];
+ assert( slice_len * dims[2] == image.GetBufferLength() );
+ //assert( image.GetBufferLength() == bv->GetLength() );
+
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( dims[2] );
+ fg.SetPrefix( outfilename.c_str() );
+ fg.SetPattern( pattern.c_str() );
+ if( !fg.Generate() )
+ {
+ std::cerr << "could not generate" << std::endl;
+ return 1;
+ }
+ const double *cosines = image.GetDirectionCosines();
+ gdcm::DirectionCosines dc( cosines );
+ double normal[3];
+ dc.Cross( normal );
+ //const double *origin = image.GetOrigin();
+ //double zspacing = image.GetSpacing(2);
+
+ // Remove SharedFunctionalGroupsSequence
+ gdcm::SmartPointer sfgs =
+ ds.GetDataElement( gdcm::Tag( 0x5200,0x9229 ) ).GetValueAsSQ();
+ ds.Remove( gdcm::Tag( 0x5200,0x9229 ) );
+ assert( ds.FindDataElement( gdcm::Tag( 0x5200,0x9229 ) ) == false );
+ // Remove PerFrameFunctionalGroupsSequence
+ gdcm::SmartPointer pffgs =
+ ds.GetDataElement( gdcm::Tag( 0x5200,0x9230 ) ).GetValueAsSQ();
+ ds.Remove( gdcm::Tag( 0x5200,0x9230 ) );
+ assert( ds.FindDataElement( gdcm::Tag( 0x5200,0x9230 ) ) == false );
+ ds.Remove( gdcm::Tag( 0x28,0x8) );
+ ds.Remove( gdcm::Tag( 0x7fe0,0x0010) );
+ assert( ds.FindDataElement( gdcm::Tag( 0x7fe0,0x0010) ) == false );
+ //ds.Remove( gdcm::Tag( 0x0008,0x0012) );
+ //ds.Remove( gdcm::Tag( 0x0008,0x0013) );
+
+ // reference the old instance:
+ // PS 3.3-2009 C.7.6.16.1.3
+#if 0
+ assert( ds.FindDataElement( gdcm::Tag(0x0008,0x1150) ) == false );
+ assert( ds.FindDataElement( gdcm::Tag(0x0008,0x1155) ) == false );
+ assert( ds.FindDataElement( gdcm::Tag(0x0008,0x1160) ) == false );
+ oldsopclassuid.SetTag( gdcm::Tag(0x8,0x1150) );
+ oldinstanceuid.SetTag( gdcm::Tag(0x8,0x1155) );
+ ds.Insert( oldsopclassuid );
+ ds.Insert( oldinstanceuid );
+#endif
+
+ char date[22];
+ const size_t datelen = 8;
+ //int res = gdcm::System::GetCurrentDateTime(date);
+ gdcm::Attribute<0x8,0x12> instcreationdate;
+ instcreationdate.SetValue( gdcm::DTComp( date, datelen ) );
+ ds.Replace( instcreationdate.GetAsDataElement() );
+ gdcm::Attribute<0x8,0x13> instcreationtime;
+ instcreationtime.SetValue( gdcm::DTComp( date + datelen, 13 ) );
+ ds.Replace( instcreationtime.GetAsDataElement() );
+ const char *offset = gdcm::System::GetTimezoneOffsetFromUTC();
+ gdcm::Attribute<0x8,0x201> timezoneoffsetfromutc;
+ timezoneoffsetfromutc.SetValue( offset );
+ ds.Replace( timezoneoffsetfromutc.GetAsDataElement() );
+
+ for(unsigned int i = 0; i < dims[2]; ++i)
+ {
+#if 0
+ double new_origin[3];
+ for (int j = 0; j < 3; j++)
+ {
+ // the n'th slice is n * z-spacing aloung the IOP-derived
+ // z-axis
+ new_origin[j] = origin[j] + normal[j] * i * zspacing;
+ }
+#endif
+
+ const char *outfilenamei = fg.GetFilename(i);
+ //gdcm::ImageWriter writer;
+ gdcm::Writer writer;
+ writer.SetFileName( outfilenamei );
+ //writer.SetFile( filter.GetFile() );
+ writer.SetFile( reader.GetFile() );
+
+ if ( !gdcm::RemapSharedIntoOld( ds, sfgs, pffgs, i ) )
+ {
+ return 1;
+ }
+
+ //
+ //writer.SetImage( filter.GetImage() );
+ //gdcm::Image & //slice = writer.GetImage();
+ //slice = reader.GetImage();
+// slice.SetOrigin( new_origin );
+// slice.SetNumberOfDimensions( 2 );
+// assert( slice.GetPixelFormat() == reader.GetImage().GetPixelFormat() );
+// slice.SetSpacing(2, reader.GetImage().GetSpacing(2) );
+ //slice.Print( std::cout );
+// gdcm::DataElement &pd = slice.GetDataElement();
+ const char *sliceptr = bv->GetPointer() + i * slice_len;
+ gdcm::DataElement newpixeldata( gdcm::Tag(0x7fe0,0x0010) );
+ newpixeldata.SetByteValue( sliceptr, (uint32_t)slice_len); // slow !
+ ds.Replace( newpixeldata );
+
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilenamei << std::endl;
+ return 1;
+ }
+ //else
+ // {
+ // std::cout << "Success to write: " << outfilenamei << std::endl;
+ // }
+ }
+
+ return 0;
+ }
+ else
+ {
+ assert( enhance );
+ return MakeImageEnhanced( filename, outfilename );
+#if 0
+ std::cerr << "Not implemented" << std::endl;
+ return 1;
+ gdcm::ImageReader reader;
+ reader.SetFileName( filename.c_str() );
+ if( !reader.Read() )
+ {
+ std::cerr << "could not read: " << filename << std::endl;
+ return 1;
+ }
+
+ const gdcm::Image &image = reader.GetImage();
+ const unsigned int *dims = image.GetDimensions();
+ std::cout << image << std::endl;
+ const gdcm::DataElement &pixeldata = image.GetDataElement();
+ const gdcm::ByteValue *bv = pixeldata.GetByteValue();
+ unsigned long slice_len = image.GetBufferLength() / dims[2];
+ //assert( image.GetBufferLength() == bv->GetLength() );
+
+ gdcm::FilenameGenerator fg;
+ fg.SetNumberOfFilenames( dims[2] );
+ fg.SetPrefix( outfilename.c_str() );
+ fg.SetPattern( pattern.c_str() );
+ if( !fg.Generate() )
+ {
+ std::cerr << "could not generate" << std::endl;
+ return 1;
+ }
+ const double *cosines = image.GetDirectionCosines();
+ gdcm::DirectionCosines dc( cosines );
+ double normal[3];
+ dc.Cross( normal );
+ const double *origin = image.GetOrigin();
+ double zspacing = image.GetSpacing(2);
+
+ for(unsigned int i = 0; i < dims[2]; ++i)
+ {
+ double new_origin[3];
+ for (int j = 0; j < 3; j++)
+ {
+ // the n'th slice is n * z-spacing aloung the IOP-derived
+ // z-axis
+ new_origin[j] = origin[j] + normal[j] * i * zspacing;
+ }
+
+ const char *outfilenamei = fg.GetFilename(i);
+ gdcm::ImageWriter writer;
+ writer.SetFileName( outfilenamei );
+ //writer.SetFile( filter.GetFile() );
+ writer.SetFile( reader.GetFile() );
+
+ //
+ //writer.SetImage( filter.GetImage() );
+ gdcm::Image &slice = writer.GetImage();
+ slice = reader.GetImage();
+ slice.SetOrigin( new_origin );
+ slice.SetNumberOfDimensions( 2 );
+ assert( slice.GetPixelFormat() == reader.GetImage().GetPixelFormat() );
+ slice.SetSpacing(2, reader.GetImage().GetSpacing(2) );
+ //slice.Print( std::cout );
+ gdcm::DataElement &pd = slice.GetDataElement();
+ const char *sliceptr = bv->GetPointer() + i * slice_len;
+ pd.SetByteValue( sliceptr, slice_len); // slow !
+
+ if( !writer.Write() )
+ {
+ std::cerr << "Failed to write: " << outfilenamei << std::endl;
+ return 1;
+ }
+ else
+ {
+ std::cout << "Success to write: " << outfilenamei << std::endl;
+ }
+ }
+
+ return 0;
+#endif
+ }
+}
diff --git a/gdcm/Applications/Cxx/gdcmxml.cxx b/gdcm/Applications/Cxx/gdcmxml.cxx
new file mode 100644
index 0000000..ed061aa
--- /dev/null
+++ b/gdcm/Applications/Cxx/gdcmxml.cxx
@@ -0,0 +1,918 @@
+/*=========================================================================
+
+ Program: GDCM (Grassroots DICOM). A DICOM library
+
+ Copyright (c) 2006-2011 Mathieu Malaterre
+ All rights reserved.
+ See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notice for more information.
+
+=========================================================================*/
+/*
+ * This is an implementation of Application Hosting: DICOM Native Model
+ */
+#include "gdcmFilename.h"
+#include "gdcmReader.h"
+#include "gdcmVersion.h"
+#include "gdcmFileMetaInformation.h"
+#include "gdcmDataSet.h"
+#include "gdcmDataElement.h"
+#include "gdcmAttribute.h"
+#include "gdcmPrivateTag.h"
+#include "gdcmValidate.h"
+#include "gdcmWriter.h"
+#include "gdcmSystem.h"
+#include "gdcmDirectory.h"
+#include "gdcmCSAHeader.h"
+#include "gdcmPDBHeader.h"
+#include "gdcmSequenceOfItems.h"
+#include "gdcmASN1.h"
+#include "gdcmFile.h"
+#include "gdcmXMLPrinter.h"
+#include "gdcmPrinter.h"
+
+#ifdef GDCM_USE_SYSTEM_LIBXML2
+#include
+#endif
+
+#include
+#include
+#include
+
+#include /* for printf */
+#include /* for exit */
+#include
+#include
+
+using namespace gdcm;
+
+// This is a very dumb implementation for getData handling
+// by default GDCM simply drop the BulkData so we need to store
+// the actual BulkData somewhere for proper implementation of getData
+class SimpleFileXMLPrinter : public XMLPrinter
+{
+public:
+ void HandleBulkData(const char *uuid, const TransferSyntax & ts,
+ const char *bulkdata, size_t bulklen)
+ {
+ // Store Bulk Data
+ std::ofstream out( uuid, std::ios::binary );
+ out.write( bulkdata, bulklen );
+ out.close();
+ std::string tsfn = uuid;
+ tsfn += ".ts";
+ // Need to store Transfer Syntax for later getData() implementation
+ // See Sup118 for details
+ const char *tsstring = ts.GetString();
+ assert( tsstring );
+ std::ofstream out2( tsfn.c_str(), std::ios::binary );
+ out2.write( tsstring, strlen(tsstring) );
+ out2.close();
+ }
+};
+
+//Global Variables
+int loadBulkData = 0;
+int loadTransferSyntax = 0;
+TransferSyntax ts;
+
+static void PrintVersion()
+{
+ std::cout << "gdcmxml: gdcm " << gdcm::Version::GetVersion() << " ";
+ const char date[] = "$Date$";
+ std::cout << date << std::endl;
+}
+
+static void PrintHelp()
+{
+ PrintVersion();
+ std::cout << "Usage: gdcmxml [OPTION]... FILE..." << std::endl;
+ std::cout << "Convert a DICOM file into an XML file or vice-versa \n";
+ std::cout << "Parameter (required):" << std::endl;
+ std::cout << " -i --input Filename1" << std::endl;
+ std::cout << " -o --output Filename2" << std::endl;
+ std::cout << "General Options:" << std::endl;
+ std::cout << " -V --verbose more verbose (warning+error)." << std::endl;
+ std::cout << " -W --warning print warning info." << std::endl;
+ std::cout << " -D --debug print debug info." << std::endl;
+ std::cout << " -E --error print error info." << std::endl;
+ std::cout << " -h --help print help." << std::endl;
+ std::cout << " -v --version print version." << std::endl;
+ std::cout << "Options for Dicom to XML:" << std::endl;
+ std::cout << " -B --loadBulkData Loads bulk data into a binary file named \"UUID\"(by default UUID are written)." << std::endl;
+ std::cout << "Options for XML to Dicom:" << std::endl;
+ std::cout << " -B --loadBulkData Loads bulk data from a binary file named as the \"UUID\" in XML file(by default UUID are written)."<< std::endl;
+ std::cout << " -T --TransferSyntax Loads transfer syntax from file (default is LittleEndianImplicit)" << std::endl;
+}
+
+#ifdef GDCM_USE_SYSTEM_LIBXML2
+
+#define CHECK_READER \
+ if(ret == -1) \
+ assert(0 && "unable to read");
+
+#define READ_NEXT\
+ ret = xmlTextReaderRead(reader);\
+ CHECK_READER\
+ if(xmlTextReaderNodeType(reader) == 14 || xmlTextReaderNodeType(reader) == 13)\
+ ret = xmlTextReaderRead(reader);\
+ CHECK_READER
+
+#define CHECK_NAME(value)\
+ strcmp((const char*)xmlTextReaderConstName(reader),value)
+
+static void HandleBulkData(const char *uuid, DataElement &de)
+ {
+ // Load Bulk Data
+ if(loadBulkData)
+ {
+ std::ifstream file( uuid, std::ios::in|std::ios::binary|std::ios::ate );//open file with pointer at file end
+
+ if (file.is_open())
+ {
+ std::ifstream::pos_type size = file.tellg();
+ char *bulkData = new char [size];
+ file.seekg (0, std::ios::beg);
+ file.read (bulkData, size);
+ file.close();
+
+ ByteValue *bv = new ByteValue(bulkData,(int)size);
+ de.SetValue(*bv);
+ }
+ }
+
+ if(loadTransferSyntax)
+ {
+ std::string tsfn = uuid;
+ tsfn += ".ts";
+ std::ifstream file( tsfn.c_str(), std::ios::in|std::ios::binary|std::ios::ate );//open file with pointer at file end
+ if (file.is_open())
+ {
+ std::ifstream::pos_type size = file.tellg();
+ char *tsstring = new char [size];
+ file.seekg (0, std::ios::beg);
+ file.read (tsstring, size);
+ file.close();
+ TransferSyntax::TSType tsType = TransferSyntax::GetTSType(tsstring);
+ const TransferSyntax ts_temp(tsType);
+
+ if(ts_temp.IsValid())
+ {
+ ts = tsType;
+ }
+ }
+ }
+ }
+
+static void HandlePN(xmlTextReaderPtr reader,DataElement &de)
+{
+ if(CHECK_NAME("DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)
+ return;//empty element
+ else if(!(CHECK_NAME("PersonName") == 0))
+ assert(0 && "Invalid XML");
+
+ int depth_curr = xmlTextReaderDepth(reader);
+ (void)depth_curr;
+ int ret;
+ std::string name;
+ READ_NEXT;
+ READ_NEXT;
+ while(!(CHECK_NAME("DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15))
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ if((CHECK_NAME("Ideographic") == 0 || CHECK_NAME("Phonetic") == 0) && xmlTextReaderNodeType(reader) == 1)
+ {
+ name += "=";
+ }
+
+ }
+
+ de.SetByteValue( name.c_str(), (uint32_t)name.size() );
+ return;
+
+ /*
+ READ_NEXT//at ByteValue
+
+
+
+ //char *temp_name=new char[500];
+ std::string name;
+ int count =0;
+ //while(!(CHECK_NAME("PersonName") == 0 && xmlTextReaderDepth(reader) == depth_curr && xmlTextReaderNodeType(reader) == 15))
+ //{
+ if(CHECK_NAME("SingleByte") == 0)
+ {
+
+ READ_NEXT
+
+ if(CHECK_NAME("FamilyName") == 0)
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ //append here
+ count +=strlen((char*)xmlTextReaderConstValue(reader));
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ READ_NEXT//at
+ READ_NEXT//at
+ //READ_NEXT//may be new name or at
+ //if(CHECK_NAME("ByteValue") == 0)
+ //READ_NEXT
+ }
+ if(CHECK_NAME("GivenName") == 0)
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ //append here
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ READ_NEXT
+ READ_NEXT
+ }
+ if(CHECK_NAME("MiddleName") == 0)
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ //append here
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ READ_NEXT
+ READ_NEXT
+ }
+ if(CHECK_NAME("NamePrefix") == 0)
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ //append here
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ READ_NEXT
+ READ_NEXT
+ }
+ if(CHECK_NAME("NameSuffix") == 0)
+ {
+ READ_NEXT
+ if(xmlTextReaderNodeType(reader) == 3)
+ {
+ //append here
+ name += (char*)xmlTextReaderConstValue(reader);
+ name += "^";
+ }
+ READ_NEXT
+ READ_NEXT
+ }
+ READ_NEXT//starts new or end tag PersonName
+ //name += "=";
+ }
+ if(CHECK_NAME("Ideographic") == 0)
+ {
+
+ }
+ if(CHECK_NAME("Phonetic") == 0)
+ {
+ }
+
+ //Set Value in de
+ Element el;
+ el.SetValue(name.c_str(),0);
+ de = el.GetAsDataElement();
+
+ READ_NEXT//at correct place for Populate DataSet
+ return;
+ */
+
+}
+
+static void HandleSequence(SequenceOfItems *sqi,xmlTextReaderPtr reader,int depth);
+
+static void PopulateDataSet(xmlTextReaderPtr reader,DataSet &DS, int depth, bool SetSQ )
+{
+ (void)depth;
+ int ret;
+ const char *name = (const char*)xmlTextReaderConstName(reader);
+ //printf("%s\n",name);
+
+#define LoadValueASCII(type) \
+ case type: \
+ { \
+ int count =0; \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ if(strcmp(name,"DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)\
+ break;\
+ char values[10][100] = {"","","","","","","","","",""}; \
+ Element el; \
+ while(strcmp(name,"Value") == 0) \
+ { \
+ READ_NEXT \
+ if(CHECK_NAME("Value") == 0)\
+ {READ_NEXT;}\
+ else{\
+ char *value = (char*)xmlTextReaderConstValue(reader); \
+ strcpy((char *)values[count++],value); \
+ READ_NEXT /*Value ending tag*/ \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ READ_NEXT \
+ name = (const char*)xmlTextReaderConstName(reader); }\
+ } \
+ assert(CHECK_NAME("DicomAttribute") == 0);\
+ el.SetLength( (count) * vr.GetSizeof() ); \
+ int total = 0; \
+ while(total < count) \
+ { \
+ el.SetValue(values[total],total); \
+ total++; \
+ } \
+ de = el.GetAsDataElement(); \
+ }break
+
+#define LoadValueInteger(type) \
+ case type: \
+ { \
+ int count =0; \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ if(strcmp(name,"DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)\
+ break;\
+ int values[10]; \
+ Element el; \
+ while(strcmp(name,"Value") == 0) \
+ { \
+ READ_NEXT \
+ char *value_char = (char*)xmlTextReaderConstValue(reader); \
+ int nvalue = sscanf(value_char,"%d",&(values[count++])); \
+ assert( nvalue == 1 ); \
+ READ_NEXT /*Value ending tag*/ \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ READ_NEXT \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ } \
+ el.SetLength( (count) * vr.GetSizeof() ); \
+ int total = 0; \
+ while(total < count) \
+ { \
+ el.SetValue( (VRToType::Type)(values[total]),total); \
+ total++; \
+ } \
+ de = el.GetAsDataElement(); \
+ }break
+
+#define LoadValueFloat(type) \
+ case type: \
+ { \
+ int count =0; \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ if(strcmp(name,"DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)\
+ break;\
+ float values[10]; \
+ Element el; \
+ while(strcmp(name,"Value") == 0) \
+ { \
+ READ_NEXT \
+ char *value_char = (char*)xmlTextReaderConstValue(reader); \
+ sscanf(value_char,"%f",&(values[count++])); \
+ READ_NEXT /*Value ending tag*/ \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ READ_NEXT \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ } \
+ el.SetLength( (count) * vr.GetSizeof() ); \
+ int total = 0; \
+ while(total < count) \
+ { \
+ el.SetValue(values[total],total); \
+ total++; \
+ } \
+ de = el.GetAsDataElement(); \
+ }break
+
+#define LoadValueDouble(type) \
+ case type: \
+ { \
+ int count =0; \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ if(strcmp(name,"DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)\
+ break;\
+ double values[10]; \
+ Element el; \
+ while(strcmp(name,"Value") == 0) \
+ { \
+ READ_NEXT \
+ char *value_char = (char*)xmlTextReaderConstValue(reader); \
+ sscanf(value_char,"%lf",&(values[count++])); \
+ READ_NEXT/*Value ending tag*/ \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ READ_NEXT \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ } \
+ el.SetLength( (count) * vr.GetSizeof() ); \
+ int total = 0; \
+ while(total < count) \
+ { \
+ el.SetValue(values[total],total); \
+ total++; \
+ } \
+ de = el.GetAsDataElement(); \
+ }break
+
+
+#define LoadValueAT(type)\
+ case type: \
+ { \
+ int count =0; \
+ name = (const char*)xmlTextReaderConstName(reader);\
+ if(strcmp(name,"DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15)\
+ break;\
+ Tag tags[10];\
+ unsigned int group = 0, element = 0;\
+ Element el;\
+ while(strcmp(name,"Value") == 0)\
+ {\
+ READ_NEXT\
+ char *value = (char*)xmlTextReaderConstValue(reader); \
+ if( sscanf(value, "(%04x,%04x)", &group , &element) != 2 )\
+ {\
+ gdcmDebugMacro( "Problem reading AT: ");\
+ } \
+ tags[count].SetGroup( (uint16_t)group );\
+ tags[count].SetElement( (uint16_t)element );count++;\
+ READ_NEXT/*Value ending tag*/ \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ READ_NEXT \
+ name = (const char*)xmlTextReaderConstName(reader); \
+ }\
+ el.SetLength( (count) * vr.GetSizeof() ); \
+ int total = 0; \
+ while(total < count) \
+ { \
+ el.SetValue(tags[total],total); \
+ total++; \
+ } \
+ de = el.GetAsDataElement();\
+ }break
+
+
+ while( xmlTextReaderDepth(reader)!=0 )
+ {
+ if(SetSQ && (xmlTextReaderNodeType(reader) == 15) && CHECK_NAME("Item") == 0 )
+ return;
+ if(CHECK_NAME("DicomAttribute") == 0)
+ {
+ DataElement de;
+
+ /* Reading Tag */
+ char *tag_read =(char *)xmlTextReaderGetAttribute(reader,(const unsigned char*)"tag");
+ Tag t;
+ if(!t.ReadFromContinuousString((const char *)tag_read))
+ assert(0 && "Invalid Tag!");
+
+ /* Reading VR */
+ char vr_read[3] = "";
+ strcpy(vr_read, (const char *)xmlTextReaderGetAttribute(reader,(const unsigned char*)"vr"));
+ vr_read[2]='\0';
+ const gdcm::VR vr = gdcm::VR::GetVRType(vr_read);
+
+ READ_NEXT /* should be at Value tag or BulkData tag or Item Tag */
+
+ /* Load Value */
+ switch(vr)
+ {
+
+ LoadValueAT(VR::AT);
+ LoadValueASCII(VR::AE);
+ LoadValueASCII(VR::AS);
+ LoadValueASCII(VR::CS);
+ LoadValueASCII(VR::DA);
+ LoadValueFloat(VR::DS);
+ LoadValueASCII(VR::DT);
+ LoadValueInteger(VR::IS);
+ LoadValueASCII(VR::LO);
+ LoadValueASCII(VR::LT);
+ LoadValueASCII(VR::SH);
+ LoadValueASCII(VR::ST);
+ LoadValueASCII(VR::TM);
+ LoadValueASCII(VR::UI);
+ LoadValueASCII(VR::UT);
+ LoadValueInteger(VR::SS);
+ LoadValueInteger(VR::UL);
+ LoadValueInteger(VR::SL);
+ LoadValueInteger(VR::US);
+ LoadValueFloat(VR::FL);
+ LoadValueDouble(VR::FD);
+ case VR::SQ:
+ {
+ SequenceOfItems *sqi = new SequenceOfItems();
+ HandleSequence(sqi,reader,xmlTextReaderDepth(reader));
+ de.SetValue(*sqi);
+ }break;
+
+ case VR::PN:
+ {
+ //Current node must be Person Name
+ HandlePN(reader,de);
+ }break;
+
+ case VR::OF:
+ case VR::OB:
+ case VR::OW:
+ {
+ //Presently should be at BulkData
+ assert(((CHECK_NAME("BulkData")) == 0));
+ char * uuid = (char *)xmlTextReaderGetAttribute(reader,(const unsigned char*)"uuid");
+ HandleBulkData(uuid,de);
+ READ_NEXT
+ }break;
+
+ case VR::UN:
+ {
+ int depth_UN=xmlTextReaderDepth(reader);
+ while(!(CHECK_NAME("DicomAttribute") == 0 && xmlTextReaderNodeType(reader) == 15 && (depth_UN-1) == xmlTextReaderDepth(reader)))
+ {READ_NEXT}
+ //assert(0 && "UN not Handled yet");
+ }break;
+ default:
+ assert(0 && "Unknown VR");
+ };
+
+ /*Modify de before insert*/
+
+ de.SetTag(t);
+
+ DS.Insert(de);
+
+ READ_NEXT // To next Element
+
+ }
+
+ }
+}
+
+static void HandleSequence(SequenceOfItems *sqi, xmlTextReaderPtr reader,int depth)
+{
+ int ret;
+ while(!( CHECK_NAME("DicomAttribute") == 0 && xmlTextReaderDepth(reader) == (depth - 1) && xmlTextReaderNodeType(reader) == 15 ) )
+ {
+ gdcmDebugMacro( "HandleSequence (while loop)" );
+ if( CHECK_NAME("Item") == 0 && xmlTextReaderDepth(reader) == depth && xmlTextReaderNodeType(reader) == 1)
+ {
+ //at Item
+ READ_NEXT
+ //assert(0 && "Hi1");
+ //at DicomAtt
+ if( CHECK_NAME("DicomAttribute") == 0 && xmlTextReaderDepth(reader) == (depth + 1) && xmlTextReaderNodeType(reader) == 1)
+ {
+ //start of an item
+ //Create Nested DataSet
+ //assert(0 && "Hi2");
+ Item *item = new Item();
+ DataSet *NestedDS = new DataSet() ;
+ PopulateDataSet(reader,*NestedDS,xmlTextReaderDepth(reader),true);
+ item->SetNestedDataSet(*NestedDS);
+ sqi->AddItem(*item);
+ //assert(0 && "Hi3");
+
+ }
+ else
+ assert("Empty Item or Invalid XML");
+
+ READ_NEXT
+ }
+ else
+ assert("Expected Item");
+ }
+}
+
+static void WriteDICOM(xmlTextReaderPtr reader, gdcm::Filename file2)
+{
+ int ret;
+
+ READ_NEXT
+
+ READ_NEXT // at first element "DicomAttribute"
+
+ //populate DS
+ DataSet DS;
+ if(xmlTextReaderDepth(reader) == 1 && strcmp((const char*)xmlTextReaderConstName(reader),"DicomAttribute") == 0)
+ PopulateDataSet(reader,DS,1,false);
+
+ //DataElement de;
+ //Tag t(0xFFFe,0xE0DD);
+ //de.SetTag(t);
+ //DS.Insert(de);
+ //add to File
+
+ //store in heap
+ File *F = new File();
+ F->SetDataSet(DS);
+
+ //Validate - possibly from gdcmValidate Class
+ FileMetaInformation meta = F->GetHeader();
+ meta.SetDataSetTransferSyntax(ts);
+ F->SetHeader(meta);
+
+ //Validate - possibly from gdcmValidate Class
+
+ //Validate V;
+ //V.SetFile(F);
+ //V.Validation();
+ //F = V.GetValidatedFile();
+ //add to Writer
+
+ if(!file2.IsEmpty())
+ {
+ Writer W;
+ W.SetFileName(file2.GetFileName());
+ W.SetFile(*F);
+
+ //finally write to file
+ W.Write();
+ }
+ else
+ {
+ Printer printer;
+ printer.SetFile ( *F );
+ printer.SetColor(1);
+ printer.Print( std::cout );
+ }
+}
+
+static void XMLtoDICOM(gdcm::Filename file1, gdcm::Filename file2)
+{
+ xmlTextReaderPtr reader;
+ FILE *in;
+ char *buffer;
+ size_t numBytes;
+ in = fopen(file1.GetFileName(), "r");
+
+ if(in == NULL)
+ return ;
+
+ fseek(in, 0L, SEEK_END);
+ numBytes = ftell(in);
+ fseek(in, 0L, SEEK_SET);
+ buffer = (char*)calloc(numBytes, sizeof(char));
+
+ if(buffer == NULL)
+ return ;
+
+ size_t ret = fread(buffer, sizeof(char), numBytes, in);
+ if( numBytes != ret )
+ {
+ // FIXME how to return error code ?
+ return;
+ }
+ fclose(in);
+ reader = xmlReaderForMemory (buffer, (int)numBytes, NULL, NULL, 0);
+ //reader = xmlReaderForFile(filename, "UTF-8", 0); Not Working!!
+ if (reader != NULL)
+ {
+ WriteDICOM(reader, file2);
+ }
+ else
+ {
+ fprintf(stderr, "Unable to open %s\n", file1.GetFileName());
+ }
+}
+#endif // GDCM_USE_SYSTEM_LIBXML2
+
+int main (int argc, char *argv[])
+{
+ int c;
+ //int digit_optind = 0;
+ gdcm::Filename file1;
+ gdcm::Filename file2;
+// int loadTransferSyntax = 0;
+ int verbose = 0;
+ int warning = 0;
+ int debug = 0;
+ int error = 0;
+ int help = 0;
+ int version = 0;
+ while (1) {
+
+ int option_index = 0;
+
+ static struct option long_options[] = {
+ {"input", 1, 0, 0},
+ {"output", 1, 0, 0},
+ {"loadBulkData", 0, &loadBulkData, 1},
+ {"TransferSyntax", 0, &loadTransferSyntax, 1},
+ {"verbose", 0, &verbose, 1},
+ {"warning", 0, &warning, 1},
+ {"debug", 0, &debug, 1},
+ {"error", 0, &error, 1},
+ {"help", 0, &help, 1},
+ {"version", 0, &version, 1},
+ {0, 0, 0, 0} // required
+ };
+ static const char short_options[] = "i:o:BTVWDEhv";
+ c = getopt_long (argc, argv, short_options,
+ long_options, &option_index);
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch (c)
+ {
+ case 0:
+ case '-':
+ {
+ const char *s = long_options[option_index].name; (void)s;
+ if (optarg)
+ {
+ if( option_index == 0 ) /* input */
+ {
+ assert( strcmp(s, "input") == 0 );
+ assert( file1.IsEmpty() );
+ file1 = optarg;
+ }
+ }
+ }
+ break;
+
+ case 'i':
+ //printf ("option i with value '%s'\n", optarg);
+ assert( file1.IsEmpty() );
+ file1 = optarg;
+ break;
+
+ case 'o':
+ assert( file2.IsEmpty() );
+ file2 = optarg;
+ break;
+
+ case 'B':
+ loadBulkData = 1;
+ break;
+
+ case 'T':
+ loadTransferSyntax = 1;
+ break;
+
+ case 'V':
+ verbose = 1;
+ break;
+
+ case 'W':
+ warning = 1;
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'E':
+ error = 1;
+ break;
+
+ case 'h':
+ help = 1;
+ break;
+
+ case 'v':
+ version = 1;
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ int v = argc - optind;
+ if( v == 2 )
+ {
+ file1 = argv[optind];
+ file2 = argv[optind+1];
+ }
+ else if( v == 1 )
+ {
+ file1 = argv[optind];
+ }
+ else
+ {
+ PrintHelp();
+ return 1;
+ }
+ }
+
+ if( file1.IsEmpty() )
+ {
+ PrintHelp();
+ return 1;
+ }
+
+ if( version )
+ {
+ PrintVersion();
+ return 0;
+ }
+
+ if( help )
+ {
+ PrintHelp();
+ return 0;
+ }
+
+ gdcm::Trace::SetDebug( debug != 0);
+ gdcm::Trace::SetWarning( warning != 0);
+ gdcm::Trace::SetError( error != 0);
+ // when verbose is true, make sure warning+error are turned on:
+ if( verbose )
+ {
+ gdcm::Trace::SetWarning( verbose != 0);
+ gdcm::Trace::SetError( verbose!= 0);
+ }
+
+ const char *file1extension = file1.GetExtension();
+ //const char *file2extension = file2.GetExtension();
+
+ if(gdcm::System::StrCaseCmp(file1extension,".xml") != 0)// by default we assume it is a DICOM file-- as no extension is required for it
+ {
+ gdcm::Reader reader;
+ reader.SetFileName( file1.GetFileName() );
+ bool success = reader.Read();
+ if( !success )//!ignoreerrors )
+ {
+ std::cerr << "Failed to read: " << file1 << std::endl;
+ return 1;
+ }
+
+ if(loadBulkData)
+ {
+ SimpleFileXMLPrinter printer;
+ printer.SetFile ( reader.GetFile() );
+ if( file2.IsEmpty() )
+ {
+ printer.Print( std::cout );
+ }
+ else
+ {
+ std::ofstream outfile;
+ outfile.open (file2.GetFileName());
+ printer.Print( outfile );
+ outfile.close();
+ }
+ }
+ else
+ {
+ XMLPrinter printer;
+ printer.SetFile ( reader.GetFile() );
+ if( file2.IsEmpty() )
+ {
+ printer.Print( std::cout );
+ }
+ else
+ {
+ std::ofstream outfile;
+ outfile.open (file2.GetFileName());
+ printer.Print( outfile );
+ outfile.close();
+ }
+ }
+ return 0;
+ }
+ else
+ {
+#ifdef GDCM_USE_SYSTEM_LIBXML2
+ /*
+ * This initializes the library and checks potential ABI mismatches
+ * between the version it was compiled for and the actual shared
+ * library used.
+ */
+ LIBXML_TEST_VERSION
+
+ XMLtoDICOM(file1,file2);
+
+ /*
+ * Cleanup function for the XML library.
+ */
+ xmlCleanupParser();
+ /*
+ * This is to debug memory for regression tests.
+ */
+ xmlMemoryDump();
+#else
+ printf("\nPlease configure Cmake options with GDCM_USE_SYSTEM_LIBXML2 as ON and compile!\n");
+#endif
+ }
+}
diff --git a/gdcm/Applications/Cxx/pdf2dcm.cxx b/gdcm/Applications/Cxx/pdf2dcm.cxx
new file mode 100644
index 0000000..882e28e
--- /dev/null
+++ b/gdcm/Applications/Cxx/pdf2dcm.cxx
@@ -0,0 +1,21 @@
+/*
+Provide a mapping for:
+
+Title: ITK Software Guide
+Subject: Medical Image Segmentation and Registration Toolkit
+Keywords: Registration,Segmentation,Guide
+Author: Luis Ibanez and Will Schroeder
+Creator: LaTeX with hyperref package
+Producer: dvips + GPL Ghostscript 8.15
+CreationDate: Mon Nov 21 19:34:28 2005
+ModDate: Mon Nov 21 19:34:28 2005
+Tagged: no
+Pages: 836
+Encrypted: no
+Page size: 522 x 675 pts
+File size: 5580502 bytes
+Optimized: no
+PDF version: 1.4
+
+into DICOM elements...
+*/
diff --git a/gdcm/Applications/Cxx/puff.c b/gdcm/Applications/Cxx/puff.c
new file mode 100644
index 0000000..df8470c
--- /dev/null
+++ b/gdcm/Applications/Cxx/puff.c
@@ -0,0 +1,837 @@
+/*
+ * puff.c
+ * Copyright (C) 2002-2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in puff.h
+ * version 2.2, 25 Apr 2010
+ *
+ * puff.c is a simple inflate written to be an unambiguous way to specify the
+ * deflate format. It is not written for speed but rather simplicity. As a
+ * side benefit, this code might actually be useful when small code is more
+ * important than speed, such as bootstrap applications. For typical deflate
+ * data, zlib's inflate() is about four times as fast as puff(). zlib's
+ * inflate compiles to around 20K on my machine, whereas puff.c compiles to
+ * around 4K on my machine (a PowerPC using GNU cc). If the faster decode()
+ * function here is used, then puff() is only twice as slow as zlib's
+ * inflate().
+ *
+ * All dynamically allocated memory comes from the stack. The stack required
+ * is less than 2K bytes. This code is compatible with 16-bit int's and
+ * assumes that long's are at least 32 bits. puff.c uses the short data type,
+ * assumed to be 16 bits, for arrays in order to to conserve memory. The code
+ * works whether integers are stored big endian or little endian.
+ *
+ * In the comments below are "Format notes" that describe the inflate process
+ * and document some of the less obvious aspects of the format. This source
+ * code is meant to supplement RFC 1951, which formally describes the deflate
+ * format:
+ *
+ * http://www.zlib.org/rfc-deflate.html
+ */
+
+/*
+ * Change history:
+ *
+ * 1.0 10 Feb 2002 - First version
+ * 1.1 17 Feb 2002 - Clarifications of some comments and notes
+ * - Update puff() dest and source pointers on negative
+ * errors to facilitate debugging deflators
+ * - Remove longest from struct huffman -- not needed
+ * - Simplify offs[] index in construct()
+ * - Add input size and checking, using longjmp() to
+ * maintain easy readability
+ * - Use short data type for large arrays
+ * - Use pointers instead of long to specify source and
+ * destination sizes to avoid arbitrary 4 GB limits
+ * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!),
+ * but leave simple version for readabilty
+ * - Make sure invalid distances detected if pointers
+ * are 16 bits
+ * - Fix fixed codes table error
+ * - Provide a scanning mode for determining size of
+ * uncompressed data
+ * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Gailly]
+ * - Add a puff.h file for the interface
+ * - Add braces in puff() for else do [Gailly]
+ * - Use indexes instead of pointers for readability
+ * 1.4 31 Mar 2002 - Simplify construct() code set check
+ * - Fix some comments
+ * - Add FIXLCODES #define
+ * 1.5 6 Apr 2002 - Minor comment fixes
+ * 1.6 7 Aug 2002 - Minor format changes
+ * 1.7 3 Mar 2003 - Added test code for distribution
+ * - Added zlib-like license
+ * 1.8 9 Jan 2004 - Added some comments on no distance codes case
+ * 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland]
+ * - Catch missing end-of-block symbol error
+ * 2.0 25 Jul 2008 - Add #define to permit distance too far back
+ * - Add option in TEST code for puff to write the data
+ * - Add option in TEST code to skip input bytes
+ * - Allow TEST code to read from piped stdin
+ * 2.1 4 Apr 2010 - Avoid variable initialization for happier compilers
+ * - Avoid unsigned comparisons for even happier compilers
+ * 2.2 25 Apr 2010 - Fix bug in variable initializations [Oberhumer]
+ * - Add const where appropriate [Oberhumer]
+ * - Split if's and ?'s for coverage testing
+ * - Break out test code to separate file
+ * - Move NIL to puff.h
+ * - Allow incomplete code only if single code length is 1
+ * - Add full code coverage test to Makefile
+ */
+
+#include /* for setjmp(), longjmp(), and jmp_buf */
+#include "puff.h" /* prototype for puff() */
+
+#define local static /* for local function definitions */
+
+/*
+ * Maximums for allocations and loops. It is not useful to change these --
+ * they are fixed by the deflate format.
+ */
+#define MAXBITS 15 /* maximum bits in a code */
+#define MAXLCODES 286 /* maximum number of literal/length codes */
+#define MAXDCODES 30 /* maximum number of distance codes */
+#define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */
+#define FIXLCODES 288 /* number of fixed literal/length codes */
+
+/* input and output state */
+struct state {
+ /* output state */
+ unsigned char *out; /* output buffer */
+ unsigned long outlen; /* available space at out */
+ unsigned long outcnt; /* bytes written to out so far */
+
+ /* input state */
+ const unsigned char *in; /* input buffer */
+ unsigned long inlen; /* available input at in */
+ unsigned long incnt; /* bytes read so far */
+ int bitbuf; /* bit buffer */
+ int bitcnt; /* number of bits in bit buffer */
+
+ /* input limit error return state for bits() and decode() */
+ jmp_buf env;
+};
+
+/*
+ * Return need bits from the input stream. This always leaves less than
+ * eight bits in the buffer. bits() works properly for need == 0.
+ *
+ * Format notes:
+ *
+ * - Bits are stored in bytes from the least significant bit to the most
+ * significant bit. Therefore bits are dropped from the bottom of the bit
+ * buffer, using shift right, and new bytes are appended to the top of the
+ * bit buffer, using shift left.
+ */
+local int bits(struct state *s, int need)
+{
+ long val; /* bit accumulator (can use up to 20 bits) */
+
+ /* load at least need bits into val */
+ val = s->bitbuf;
+ while (s->bitcnt < need) {
+ if (s->incnt == s->inlen)
+ longjmp(s->env, 1); /* out of input */
+ val |= (long)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */
+ s->bitcnt += 8;
+ }
+
+ /* drop need bits and update buffer, always zero to seven bits left */
+ s->bitbuf = (int)(val >> need);
+ s->bitcnt -= need;
+
+ /* return need bits, zeroing the bits above that */
+ return (int)(val & ((1L << need) - 1));
+}
+
+/*
+ * Process a stored block.
+ *
+ * Format notes:
+ *
+ * - After the two-bit stored block type (00), the stored block length and
+ * stored bytes are byte-aligned for fast copying. Therefore any leftover
+ * bits in the byte that has the last bit of the type, as many as seven, are
+ * discarded. The value of the discarded bits are not defined and should not
+ * be checked against any expectation.
+ *
+ * - The second inverted copy of the stored block length does not have to be
+ * checked, but it's probably a good idea to do so anyway.
+ *
+ * - A stored block can have zero length. This is sometimes used to byte-align
+ * subsets of the compressed data for random access or partial recovery.
+ */
+local int stored(struct state *s)
+{
+ unsigned len; /* length of stored block */
+
+ /* discard leftover bits from current byte (assumes s->bitcnt < 8) */
+ s->bitbuf = 0;
+ s->bitcnt = 0;
+
+ /* get length and check against its one's complement */
+ if (s->incnt + 4 > s->inlen)
+ return 2; /* not enough input */
+ len = s->in[s->incnt++];
+ len |= s->in[s->incnt++] << 8;
+ if (s->in[s->incnt++] != (~len & 0xff) ||
+ s->in[s->incnt++] != ((~len >> 8) & 0xff))
+ return -2; /* didn't match complement! */
+
+ /* copy len bytes from in to out */
+ if (s->incnt + len > s->inlen)
+ return 2; /* not enough input */
+ if (s->out != NIL) {
+ if (s->outcnt + len > s->outlen)
+ return 1; /* not enough output space */
+ while (len--)
+ s->out[s->outcnt++] = s->in[s->incnt++];
+ }
+ else { /* just scanning */
+ s->outcnt += len;
+ s->incnt += len;
+ }
+
+ /* done with a valid stored block */
+ return 0;
+}
+
+/*
+ * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
+ * each length, which for a canonical code are stepped through in order.
+ * symbol[] are the symbol values in canonical order, where the number of
+ * entries is the sum of the counts in count[]. The decoding process can be
+ * seen in the function decode() below.
+ */
+struct huffman {
+ short *count; /* number of symbols of each length */
+ short *symbol; /* canonically ordered symbols */
+};
+
+/*
+ * Decode a code from the stream s using huffman table h. Return the symbol or
+ * a negative value if there is an error. If all of the lengths are zero, i.e.
+ * an empty code, or if the code is incomplete and an invalid code is received,
+ * then -10 is returned after reading MAXBITS bits.
+ *
+ * Format notes:
+ *
+ * - The codes as stored in the compressed data are bit-reversed relative to
+ * a simple integer ordering of codes of the same lengths. Hence below the
+ * bits are pulled from the compressed data one at a time and used to
+ * build the code value reversed from what is in the stream in order to
+ * permit simple integer comparisons for decoding. A table-based decoding
+ * scheme (as used in zlib) does not need to do this reversal.
+ *
+ * - The first code for the shortest length is all zeros. Subsequent codes of
+ * the same length are simply integer increments of the previous code. When
+ * moving up a length, a zero bit is appended to the code. For a complete
+ * code, the last code of the longest length will be all ones.
+ *
+ * - Incomplete codes are handled by this decoder, since they are permitted
+ * in the deflate format. See the format notes for fixed() and dynamic().
+ */
+#ifdef SLOW
+local int decode(struct state *s, const struct huffman *h)
+{
+ int len; /* current number of bits in code */
+ int code; /* len bits being decoded */
+ int first; /* first code of length len */
+ int count; /* number of codes of length len */
+ int index; /* index of first code of length len in symbol table */
+
+ code = first = index = 0;
+ for (len = 1; len <= MAXBITS; len++) {
+ code |= bits(s, 1); /* get next bit */
+ count = h->count[len];
+ if (code - count < first) /* if length len, return symbol */
+ return h->symbol[index + (code - first)];
+ index += count; /* else update for next length */
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ }
+ return -10; /* ran out of codes */
+}
+
+/*
+ * A faster version of decode() for real applications of this code. It's not
+ * as readable, but it makes puff() twice as fast. And it only makes the code
+ * a few percent larger.
+ */
+#else /* !SLOW */
+local int decode(struct state *s, const struct huffman *h)
+{
+ int len; /* current number of bits in code */
+ int code; /* len bits being decoded */
+ int first; /* first code of length len */
+ int count; /* number of codes of length len */
+ int index; /* index of first code of length len in symbol table */
+ int bitbuf; /* bits from stream */
+ int left; /* bits left in next or left to process */
+ short *next; /* next number of codes */
+
+ bitbuf = s->bitbuf;
+ left = s->bitcnt;
+ code = first = index = 0;
+ len = 1;
+ next = h->count + 1;
+ while (1) {
+ while (left--) {
+ code |= bitbuf & 1;
+ bitbuf >>= 1;
+ count = *next++;
+ if (code - count < first) { /* if length len, return symbol */
+ s->bitbuf = bitbuf;
+ s->bitcnt = (s->bitcnt - len) & 7;
+ return h->symbol[index + (code - first)];
+ }
+ index += count; /* else update for next length */
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ len++;
+ }
+ left = (MAXBITS+1) - len;
+ if (left == 0)
+ break;
+ if (s->incnt == s->inlen)
+ longjmp(s->env, 1); /* out of input */
+ bitbuf = s->in[s->incnt++];
+ if (left > 8)
+ left = 8;
+ }
+ return -10; /* ran out of codes */
+}
+#endif /* SLOW */
+
+/*
+ * Given the list of code lengths length[0..n-1] representing a canonical
+ * Huffman code for n symbols, construct the tables required to decode those
+ * codes. Those tables are the number of codes of each length, and the symbols
+ * sorted by length, retaining their original order within each length. The
+ * return value is zero for a complete code set, negative for an over-
+ * subscribed code set, and positive for an incomplete code set. The tables
+ * can be used if the return value is zero or positive, but they cannot be used
+ * if the return value is negative. If the return value is zero, it is not
+ * possible for decode() using that table to return an error--any stream of
+ * enough bits will resolve to a symbol. If the return value is positive, then
+ * it is possible for decode() using that table to return an error for received
+ * codes past the end of the incomplete lengths.
+ *
+ * Not used by decode(), but used for error checking, h->count[0] is the number
+ * of the n symbols not in the code. So n - h->count[0] is the number of
+ * codes. This is useful for checking for incomplete codes that have more than
+ * one symbol, which is an error in a dynamic block.
+ *
+ * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
+ * This is assured by the construction of the length arrays in dynamic() and
+ * fixed() and is not verified by construct().
+ *
+ * Format notes:
+ *
+ * - Permitted and expected examples of incomplete codes are one of the fixed
+ * codes and any code with a single symbol which in deflate is coded as one
+ * bit instead of zero bits. See the format notes for fixed() and dynamic().
+ *
+ * - Within a given code length, the symbols are kept in ascending order for
+ * the code bits definition.
+ */
+local int construct(struct huffman *h, const short *length, int n)
+{
+ int symbol; /* current symbol when stepping through length[] */
+ int len; /* current length when stepping through h->count[] */
+ int left; /* number of possible codes left of current length */
+ short offs[MAXBITS+1]; /* offsets in symbol table for each length */
+
+ /* count number of codes of each length */
+ for (len = 0; len <= MAXBITS; len++)
+ h->count[len] = 0;
+ for (symbol = 0; symbol < n; symbol++)
+ (h->count[length[symbol]])++; /* assumes lengths are within bounds */
+ if (h->count[0] == n) /* no codes! */
+ return 0; /* complete, but decode() will fail */
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1; /* one possible code of zero length */
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1; /* one more bit, double codes left */
+ left -= h->count[len]; /* deduct count from possible codes */
+ if (left < 0)
+ return left; /* over-subscribed--return negative */
+ } /* left > 0 means incomplete */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + h->count[len];
+
+ /*
+ * put symbols in table sorted by length, by symbol order within each
+ * length
+ */
+ for (symbol = 0; symbol < n; symbol++)
+ if (length[symbol] != 0)
+ h->symbol[offs[length[symbol]]++] = symbol;
+
+ /* return zero for complete set, positive for incomplete set */
+ return left;
+}
+
+/*
+ * Decode literal/length and distance codes until an end-of-block code.
+ *
+ * Format notes:
+ *
+ * - Compressed data that is after the block type if fixed or after the code
+ * description if dynamic is a combination of literals and length/distance
+ * pairs terminated by and end-of-block code. Literals are simply Huffman
+ * coded bytes. A length/distance pair is a coded length followed by a
+ * coded distance to represent a string that occurs earlier in the
+ * uncompressed data that occurs again at the current location.
+ *
+ * - Literals, lengths, and the end-of-block code are combined into a single
+ * code of up to 286 symbols. They are 256 literals (0..255), 29 length
+ * symbols (257..285), and the end-of-block symbol (256).
+ *
+ * - There are 256 possible lengths (3..258), and so 29 symbols are not enough
+ * to represent all of those. Lengths 3..10 and 258 are in fact represented
+ * by just a length symbol. Lengths 11..257 are represented as a symbol and
+ * some number of extra bits that are added as an integer to the base length
+ * of the length symbol. The number of extra bits is determined by the base
+ * length symbol. These are in the static arrays below, lens[] for the base
+ * lengths and lext[] for the corresponding number of extra bits.
+ *
+ * - The reason that 258 gets its own symbol is that the longest length is used
+ * often in highly redundant files. Note that 258 can also be coded as the
+ * base value 227 plus the maximum extra value of 31. While a good deflate
+ * should never do this, it is not an error, and should be decoded properly.
+ *
+ * - If a length is decoded, including its extra bits if any, then it is
+ * followed a distance code. There are up to 30 distance symbols. Again
+ * there are many more possible distances (1..32768), so extra bits are added
+ * to a base value represented by the symbol. The distances 1..4 get their
+ * own symbol, but the rest require extra bits. The base distances and
+ * corresponding number of extra bits are below in the static arrays dist[]
+ * and dext[].
+ *
+ * - Literal bytes are simply written to the output. A length/distance pair is
+ * an instruction to copy previously uncompressed bytes to the output. The
+ * copy is from distance bytes back in the output stream, copying for length
+ * bytes.
+ *
+ * - Distances pointing before the beginning of the output data are not
+ * permitted.
+ *
+ * - Overlapped copies, where the length is greater than the distance, are
+ * allowed and common. For example, a distance of one and a length of 258
+ * simply copies the last byte 258 times. A distance of four and a length of
+ * twelve copies the last four bytes three times. A simple forward copy
+ * ignoring whether the length is greater than the distance or not implements
+ * this correctly. You should not use memcpy() since its behavior is not
+ * defined for overlapped arrays. You should not use memmove() or bcopy()
+ * since though their behavior -is- defined for overlapping arrays, it is
+ * defined to do the wrong thing in this case.
+ */
+local int codes(struct state *s,
+ const struct huffman *lencode,
+ const struct huffman *distcode)
+{
+ int symbol; /* decoded symbol */
+ int len; /* length for copy */
+ unsigned dist; /* distance for copy */
+ static const short lens[29] = { /* Size base for length codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
+ static const short lext[29] = { /* Extra bits for length codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
+ static const short dists[30] = { /* Offset base for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+ static const short dext[30] = { /* Extra bits for distance codes 0..29 */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+ /* decode literals and length/distance pairs */
+ do {
+ symbol = decode(s, lencode);
+ if (symbol < 0)
+ return symbol; /* invalid symbol */
+ if (symbol < 256) { /* literal: symbol is the byte */
+ /* write out the literal */
+ if (s->out != NIL) {
+ if (s->outcnt == s->outlen)
+ return 1;
+ s->out[s->outcnt] = symbol;
+ }
+ s->outcnt++;
+ }
+ else if (symbol > 256) { /* length */
+ /* get and compute length */
+ symbol -= 257;
+ if (symbol >= 29)
+ return -10; /* invalid fixed code */
+ len = lens[symbol] + bits(s, lext[symbol]);
+
+ /* get and check distance */
+ symbol = decode(s, distcode);
+ if (symbol < 0)
+ return symbol; /* invalid symbol */
+ dist = dists[symbol] + bits(s, dext[symbol]);
+#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ if (dist > s->outcnt)
+ return -11; /* distance too far back */
+#endif
+
+ /* copy length bytes from distance bytes back */
+ if (s->out != NIL) {
+ if (s->outcnt + len > s->outlen)
+ return 1;
+ while (len--) {
+ s->out[s->outcnt] =
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+ dist > s->outcnt ?
+ 0 :
+#endif
+ s->out[s->outcnt - dist];
+ s->outcnt++;
+ }
+ }
+ else
+ s->outcnt += len;
+ }
+ } while (symbol != 256); /* end of block symbol */
+
+ /* done with a valid fixed or dynamic block */
+ return 0;
+}
+
+/*
+ * Process a fixed codes block.
+ *
+ * Format notes:
+ *
+ * - This block type can be useful for compressing small amounts of data for
+ * which the size of the code descriptions in a dynamic block exceeds the
+ * benefit of custom codes for that block. For fixed codes, no bits are
+ * spent on code descriptions. Instead the code lengths for literal/length
+ * codes and distance codes are fixed. The specific lengths for each symbol
+ * can be seen in the "for" loops below.
+ *
+ * - The literal/length code is complete, but has two symbols that are invalid
+ * and should result in an error if received. This cannot be implemented
+ * simply as an incomplete code since those two symbols are in the "middle"
+ * of the code. They are eight bits long and the longest literal/length\
+ * code is nine bits. Therefore the code must be constructed with those
+ * symbols, and the invalid symbols must be detected after decoding.
+ *
+ * - The fixed distance codes also have two invalid symbols that should result
+ * in an error if received. Since all of the distance codes are the same
+ * length, this can be implemented as an incomplete code. Then the invalid
+ * codes are detected while decoding.
+ */
+local int fixed(struct state *s)
+{
+ static int virgin = 1;
+ static short lencnt[MAXBITS+1], lensym[FIXLCODES];
+ static short distcnt[MAXBITS+1], distsym[MAXDCODES];
+ static struct huffman lencode, distcode;
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ int symbol;
+ short lengths[FIXLCODES];
+
+ /* construct lencode and distcode */
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+
+ /* literal/length table */
+ for (symbol = 0; symbol < 144; symbol++)
+ lengths[symbol] = 8;
+ for (; symbol < 256; symbol++)
+ lengths[symbol] = 9;
+ for (; symbol < 280; symbol++)
+ lengths[symbol] = 7;
+ for (; symbol < FIXLCODES; symbol++)
+ lengths[symbol] = 8;
+ construct(&lencode, lengths, FIXLCODES);
+
+ /* distance table */
+ for (symbol = 0; symbol < MAXDCODES; symbol++)
+ lengths[symbol] = 5;
+ construct(&distcode, lengths, MAXDCODES);
+
+ /* do this just once */
+ virgin = 0;
+ }
+
+ /* decode data until end-of-block code */
+ return codes(s, &lencode, &distcode);
+}
+
+/*
+ * Process a dynamic codes block.
+ *
+ * Format notes:
+ *
+ * - A dynamic block starts with a description of the literal/length and
+ * distance codes for that block. New dynamic blocks allow the compressor to
+ * rapidly adapt to changing data with new codes optimized for that data.
+ *
+ * - The codes used by the deflate format are "canonical", which means that
+ * the actual bits of the codes are generated in an unambiguous way simply
+ * from the number of bits in each code. Therefore the code descriptions
+ * are simply a list of code lengths for each symbol.
+ *
+ * - The code lengths are stored in order for the symbols, so lengths are
+ * provided for each of the literal/length symbols, and for each of the
+ * distance symbols.
+ *
+ * - If a symbol is not used in the block, this is represented by a zero as
+ * as the code length. This does not mean a zero-length code, but rather
+ * that no code should be created for this symbol. There is no way in the
+ * deflate format to represent a zero-length code.
+ *
+ * - The maximum number of bits in a code is 15, so the possible lengths for
+ * any code are 1..15.
+ *
+ * - The fact that a length of zero is not permitted for a code has an
+ * interesting consequence. Normally if only one symbol is used for a given
+ * code, then in fact that code could be represented with zero bits. However
+ * in deflate, that code has to be at least one bit. So for example, if
+ * only a single distance base symbol appears in a block, then it will be
+ * represented by a single code of length one, in particular one 0 bit. This
+ * is an incomplete code, since if a 1 bit is received, it has no meaning,
+ * and should result in an error. So incomplete distance codes of one symbol
+ * should be permitted, and the receipt of invalid codes should be handled.
+ *
+ * - It is also possible to have a single literal/length code, but that code
+ * must be the end-of-block code, since every dynamic block has one. This
+ * is not the most efficient way to create an empty block (an empty fixed
+ * block is fewer bits), but it is allowed by the format. So incomplete
+ * literal/length codes of one symbol should also be permitted.
+ *
+ * - If there are only literal codes and no lengths, then there are no distance
+ * codes. This is represented by one distance code with zero bits.
+ *
+ * - The list of up to 286 length/literal lengths and up to 30 distance lengths
+ * are themselves compressed using Huffman codes and run-length encoding. In
+ * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
+ * that length, and the symbols 16, 17, and 18 are run-length instructions.
+ * Each of 16, 17, and 18 are follwed by extra bits to define the length of
+ * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10
+ * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols
+ * are common, hence the special coding for zero lengths.
+ *
+ * - The symbols for 0..18 are Huffman coded, and so that code must be
+ * described first. This is simply a sequence of up to 19 three-bit values
+ * representing no code (0) or the code length for that symbol (1..7).
+ *
+ * - A dynamic block starts with three fixed-size counts from which is computed
+ * the number of literal/length code lengths, the number of distance code
+ * lengths, and the number of code length code lengths (ok, you come up with
+ * a better name!) in the code descriptions. For the literal/length and
+ * distance codes, lengths after those provided are considered zero, i.e. no
+ * code. The code length code lengths are received in a permuted order (see
+ * the order[] array below) to make a short code length code length list more
+ * likely. As it turns out, very short and very long codes are less likely
+ * to be seen in a dynamic code description, hence what may appear initially
+ * to be a peculiar ordering.
+ *
+ * - Given the number of literal/length code lengths (nlen) and distance code
+ * lengths (ndist), then they are treated as one long list of nlen + ndist
+ * code lengths. Therefore run-length coding can and often does cross the
+ * boundary between the two sets of lengths.
+ *
+ * - So to summarize, the code description at the start of a dynamic block is
+ * three counts for the number of code lengths for the literal/length codes,
+ * the distance codes, and the code length codes. This is followed by the
+ * code length code lengths, three bits each. This is used to construct the
+ * code length code which is used to read the remainder of the lengths. Then
+ * the literal/length code lengths and distance lengths are read as a single
+ * set of lengths using the code length codes. Codes are constructed from
+ * the resulting two sets of lengths, and then finally you can start
+ * decoding actual compressed data in the block.
+ *
+ * - For reference, a "typical" size for the code description in a dynamic
+ * block is around 80 bytes.
+ */
+local int dynamic(struct state *s)
+{
+ int nlen, ndist, ncode; /* number of lengths in descriptor */
+ int index; /* index of lengths[] */
+ int err; /* construct() return value */
+ short lengths[MAXCODES]; /* descriptor code lengths */
+ short lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */
+ short distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */
+ struct huffman lencode, distcode; /* length and distance codes */
+ static const short order[19] = /* permutation of code length codes */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* construct lencode and distcode */
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+
+ /* get number of lengths in each table, check lengths */
+ nlen = bits(s, 5) + 257;
+ ndist = bits(s, 5) + 1;
+ ncode = bits(s, 4) + 4;
+ if (nlen > MAXLCODES || ndist > MAXDCODES)
+ return -3; /* bad counts */
+
+ /* read code length code lengths (really), missing lengths are zero */
+ for (index = 0; index < ncode; index++)
+ lengths[order[index]] = bits(s, 3);
+ for (; index < 19; index++)
+ lengths[order[index]] = 0;
+
+ /* build huffman table for code lengths codes (use lencode temporarily) */
+ err = construct(&lencode, lengths, 19);
+ if (err != 0) /* require complete code set here */
+ return -4;
+
+ /* read length/literal and distance code length tables */
+ index = 0;
+ while (index < nlen + ndist) {
+ int symbol; /* decoded value */
+ int len; /* last length to repeat */
+
+ symbol = decode(s, &lencode);
+ if (symbol < 16) /* length in 0..15 */
+ lengths[index++] = symbol;
+ else { /* repeat instruction */
+ len = 0; /* assume repeating zeros */
+ if (symbol == 16) { /* repeat last length 3..6 times */
+ if (index == 0)
+ return -5; /* no last length! */
+ len = lengths[index - 1]; /* last length */
+ symbol = 3 + bits(s, 2);
+ }
+ else if (symbol == 17) /* repeat zero 3..10 times */
+ symbol = 3 + bits(s, 3);
+ else /* == 18, repeat zero 11..138 times */
+ symbol = 11 + bits(s, 7);
+ if (index + symbol > nlen + ndist)
+ return -6; /* too many lengths! */
+ while (symbol--) /* repeat last or zero symbol times */
+ lengths[index++] = len;
+ }
+ }
+
+ /* check for end-of-block code -- there better be one! */
+ if (lengths[256] == 0)
+ return -9;
+
+ /* build huffman table for literal/length codes */
+ err = construct(&lencode, lengths, nlen);
+ if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
+ return -7; /* incomplete code ok only for single length 1 code */
+
+ /* build huffman table for distance codes */
+ err = construct(&distcode, lengths + nlen, ndist);
+ if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
+ return -8; /* incomplete code ok only for single length 1 code */
+
+ /* decode data until end-of-block code */
+ return codes(s, &lencode, &distcode);
+}
+
+/*
+ * Inflate source to dest. On return, destlen and sourcelen are updated to the
+ * size of the uncompressed data and the size of the deflate data respectively.
+ * On success, the return value of puff() is zero. If there is an error in the
+ * source data, i.e. it is not in the deflate format, then a negative value is
+ * returned. If there is not enough input available or there is not enough
+ * output space, then a positive error is returned. In that case, destlen and
+ * sourcelen are not updated to facilitate retrying from the beginning with the
+ * provision of more input data or more output space. In the case of invalid
+ * inflate data (a negative error), the dest and source pointers are updated to
+ * facilitate the debugging of deflators.
+ *
+ * puff() also has a mode to determine the size of the uncompressed output with
+ * no output written. For this dest must be (unsigned char *)0. In this case,
+ * the input value of *destlen is ignored, and on return *destlen is set to the
+ * size of the uncompressed output.
+ *
+ * The return codes are:
+ *
+ * 2: available inflate data did not terminate
+ * 1: output space exhausted before completing inflate
+ * 0: successful inflate
+ * -1: invalid block type (type == 3)
+ * -2: stored block length did not match one's complement
+ * -3: dynamic block code description: too many length or distance codes
+ * -4: dynamic block code description: code lengths codes incomplete
+ * -5: dynamic block code description: repeat lengths with no first length
+ * -6: dynamic block code description: repeat more than specified lengths
+ * -7: dynamic block code description: invalid literal/length code lengths
+ * -8: dynamic block code description: invalid distance code lengths
+ * -9: dynamic block code description: missing end-of-block code
+ * -10: invalid literal/length or distance code in fixed or dynamic block
+ * -11: distance is too far back in fixed or dynamic block
+ *
+ * Format notes:
+ *
+ * - Three bits are read for each block to determine the kind of block and
+ * whether or not it is the last block. Then the block is decoded and the
+ * process repeated if it was not the last block.
+ *
+ * - The leftover bits in the last byte of the deflate data after the last
+ * block (if it was a fixed or dynamic block) are undefined and have no
+ * expected values to check.
+ */
+int puff(unsigned char *dest, /* pointer to destination pointer */
+ unsigned long *destlen, /* amount of output space */
+ const unsigned char *source, /* pointer to source data pointer */
+ unsigned long *sourcelen) /* amount of input available */
+{
+ struct state s; /* input/output state */
+ int last, type; /* block information */
+ int err; /* return value */
+
+ /* initialize output state */
+ s.out = dest;
+ s.outlen = *destlen; /* ignored if dest is NIL */
+ s.outcnt = 0;
+
+ /* initialize input state */
+ s.in = source;
+ s.inlen = *sourcelen;
+ s.incnt = 0;
+ s.bitbuf = 0;
+ s.bitcnt = 0;
+
+ /* return if bits() or decode() tries to read past available input */
+ if (setjmp(s.env) != 0) /* if came back here via longjmp() */
+ err = 2; /* then skip do-loop, return error */
+ else {
+ /* process blocks until last block or error */
+ do {
+ last = bits(&s, 1); /* one if last block */
+ type = bits(&s, 2); /* block type 0..3 */
+ err = type == 0 ?
+ stored(&s) :
+ (type == 1 ?
+ fixed(&s) :
+ (type == 2 ?
+ dynamic(&s) :
+ -1)); /* type == 3, invalid */
+ if (err != 0)
+ break; /* return with error */
+ } while (!last);
+ }
+
+ /* update the lengths and return */
+ if (err <= 0) {
+ *destlen = s.outcnt;
+ *sourcelen = s.incnt;
+ }
+ return err;
+}
diff --git a/gdcm/Applications/Cxx/puff.h b/gdcm/Applications/Cxx/puff.h
new file mode 100644
index 0000000..962ae5d
--- /dev/null
+++ b/gdcm/Applications/Cxx/puff.h
@@ -0,0 +1,44 @@
+/* puff.h
+ Copyright (C) 2002-2010 Mark Adler, all rights reserved
+ version 2.2, 25 Apr 2010
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Mark Adler madler@alumni.caltech.edu
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/*
+ * See puff.c for purpose and usage.
+ */
+#ifndef NIL
+# define NIL ((unsigned char *)0) /* for no output option */
+#endif
+
+int puff(unsigned char *dest, /* pointer to destination pointer */
+ unsigned long *destlen, /* amount of output space */
+ const unsigned char *source, /* pointer to source data pointer */
+ unsigned long *sourcelen); /* amount of input available */
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/gdcm/Applications/Python/README b/gdcm/Applications/Python/README
new file mode 100644
index 0000000..7706163
--- /dev/null
+++ b/gdcm/Applications/Python/README
@@ -0,0 +1,62 @@
+= !CherryWado =
+
+== About ==
+!CherryWado is a WADO server written in python, using the !CherryPy web framework.
+
+DICOM images are handled with the GDCM DICOM toolkit command line utilities.
+
+For information about DICOM and Web Access to persistent Dicom Objects see
+http://en.wikipedia.org/wiki/DICOM.
+
+== Dependencies ==
+ * !CherryPy
+ * flup
+ * Python Imaging Library (PIL)
+ * DICOM Toolkit - gdcm (+VTK: gdcm2pnm)
+
+== Installation and configuration ==
+Our WADO server doesn't make any assumption about how / where you store DICOM
+files. Therefore, in order to use it, you need to tweak accessdata.py to specify
+a way to retrieve the requested DICOM object.
+
+You can test the server as follows:
+
+$ python wado.py test
+
+# preparation:
+
+mkdir -p /tmp/dicom/1.2.840.113619.2.5.1762386977.1328.985934491.590/1.2.840.113619.2.5.1762386977.1328.985934491.643/1.2.840.113619.2.5.1762386977.1328.985934491.693
+rmdir /tmp/dicom/1.2.840.113619.2.5.1762386977.1328.985934491.590/1.2.840.113619.2.5.1762386977.1328.985934491.643/1.2.840.113619.2.5.1762386977.1328.985934491.693
+cp gdcmData/012345.002.050.dcm /tmp/dicom/1.2.840.113619.2.5.1762386977.1328.985934491.590/1.2.840.113619.2.5.1762386977.1328.985934491.643/1.2.840.113619.2.5.1762386977.1328.985934491.693
+
+# Open in your favorite browser
+http://127.0.0.1:8080/?requestType=WADO&studyUID=1.2.840.113619.2.5.1762386977.1328.985934491.590&seriesUID=1.2.840.113619.2.5.1762386977.1328.985934491.643&objectUID=1.2.840.113619.2.5.1762386977.1328.985934491.693&contentType=image/png
+
+!CherryWado uses FLUP to ease deployment with lighttpd or Apache.
+
+Here is a lighttpd.conf snippet for your convenience:
+{{{
+$HTTP["url"] =~ "" {
+ fastcgi.server = (
+ "/wado/" => (
+ "wado" => (
+ "bin-path" => "/path/to/wado.py",
+ "socket" => "/tmp/wado.sock",
+ "check-local" => "disable",
+ "disable-time" => 1,
+ "min-procs" => 1,
+ "max-procs" => 10,
+ ),
+ ),
+ )
+}}}
+
+Make sure you have "mod_fastcgi" in server.modules!
+
+== License ==
+!CherryWado is licensed under the Apache License, Version 2.0.
+
+See http://www.apache.org/licenses/LICENSE-2.0 for the full text.
+
+== Authors ==
+Emanuele Rocca, Marco De Benedetto, Mathieu Malaterre
diff --git a/gdcm/Applications/Python/accessdata.py b/gdcm/Applications/Python/accessdata.py
new file mode 100644
index 0000000..eddefb9
--- /dev/null
+++ b/gdcm/Applications/Python/accessdata.py
@@ -0,0 +1,75 @@
+# Copyright 2008 Emanuele Rocca
+# Copyright 2008 Marco De Benedetto
+# Copyright (c) 2006-2011 Mathieu Malaterre
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+DICOM data access layer.
+
+Write your own __getfile function to suit your needs.
+"""
+
+from lib import *
+import images
+
+def __getfile(studyUID, seriesUID, objectUID):
+ """Given a studyUID, a seriesUID and an objectUID, this function must
+ retrieve (somehow) the corresponding DICOM file and return the filename.
+ Take this implementation as a rough example."""
+
+ #tmpdir = "/var/spool/dicom"
+ tmpdir = "/tmp/dicom"
+
+ import os
+ import errno
+ import tempfile
+
+ studydir = "%s/%s" % (tmpdir, studyUID)
+ seriesdir = "%s/%s" % (studydir, seriesUID)
+ objectfile = "%s/%s" % (seriesdir, objectUID)
+
+ if not os.path.isdir(seriesdir):
+ dump_file = tempfile.NamedTemporaryFile()
+ dump_file.write("(0008,0052) CS [STUDY]\n(0020,000d) UI [%s]" % studyUID)
+ dump_file.flush()
+
+ dicom_filename = dump_file.name + ".dcm"
+ trycmd("dump2dcm %s %s" % (dump_file.name, dicom_filename))
+
+ # Si chiede all'aetitle DW_AM (PACS) di muovere su webapps
+ # Per come e' definito il nodo webapps sul PACS (DW_AM) lo studio
+ # viene inviato sulla porta 3000
+ # Deve essere attivo il servizio (!!! avviato da www-data !!!) simple_storage:
+ # simple_storage -s -x /var/spool/dicom -n /etc/dicom/naming-convention -c webapps -f 3000
+ # Le immagini DICOM vengono archiviate in /var/spool/dicom/studyUID/seriersUID/objectUID
+ # come descritto in /etc/dicom/naming-convention
+ # Il servizio simple_storage forka ed e' quindi possibile ricevere piu'
+ # studi contemporaneamente
+
+ movecmd = "movescu --study --aetitle webapps --move webapps --call DW_AM pacs.ceed 3102"
+ trycmd("%s %s" % (movecmd, dicom_filename))
+
+ dump_file.close()
+ os.unlink(dicom_filename)
+ return objectfile
+
+def get(studyUID, seriesUID, objectUID, format='jpeg'):
+ """Function called by the main program to get an image."""
+ objectfile = __getfile(studyUID, seriesUID, objectUID)
+ return images.Dicom(objectfile, format)
+
+if __name__ == "__main__":
+ print get(studyUID="1.3.76.13.10010.0.5.74.3996.1224256625.4053",
+ seriesUID="1.3.12.2.1107.5.4.4.1053.30000008100608242373400002493",
+ objectUID="1.3.12.2.1107.5.4.4.1053.30000008100608324685900001822")
diff --git a/gdcm/Applications/Python/images.py b/gdcm/Applications/Python/images.py
new file mode 100644
index 0000000..9bff04a
--- /dev/null
+++ b/gdcm/Applications/Python/images.py
@@ -0,0 +1,111 @@
+# Copyright 2008 Emanuele Rocca
+# Copyright 2008 Marco De Benedetto
+# Copyright (c) 2006-2011 Mathieu Malaterre
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+DICOM images conversion and manipulation.
+
+This module uses the OFFIS DICOM toolkit command line utilities.
+"""
+
+import os
+
+import Image, ImageEnhance
+import StringIO
+
+from lib import *
+
+def calc_size(orig_size, max_size):
+ tofloat = lambda x: (float(x[0]), float(x[1]))
+ toint = lambda x: (int(x[0]), int(x[1]))
+
+ orig_size = tofloat(orig_size)
+ max_size = tofloat(max_size)
+
+ h_ratio = orig_size[1] / max_size[1]
+ new_w = orig_size[0] / h_ratio
+
+ if new_w > max_size[0]:
+ w_ratio = orig_size[0] / max_size[0]
+ new_h = orig_size[1] / w_ratio
+ new_size = max_size[0], new_h
+ else:
+ new_size = new_w, max_size[1]
+
+ return toint(new_size)
+
+class Dicom:
+
+ def __init__(self, filename, format):
+ """DICOM filename with full path, format can be either jpeg or png"""
+ assert format in ('jpeg', 'png', 'dicom')
+
+ self.format = format
+ self.filename = filename
+ if format != 'dicom':
+ converted = "%s.%s" % (filename, format)
+
+ if not os.path.isfile(converted):
+
+ quality = ""
+ if format == 'jpeg':
+ quality = '--compr-quality 100'
+ try:
+ #trycmd("gdcm2pnm --write-%s %s --use-window 1 %s %s" % (format, quality, filename, converted))
+ print "gdcm2pnm %s %s" % (filename, converted)
+ trycmd("gdcm2pnm %s %s" % (filename, converted))
+ except CmdException:
+ #trycmd("gdcm2pnm --write-%s %s %s %s" % (format, quality, filename, converted))
+ print "gdcm2pnm %s %s" % (filename, converted)
+ trycmd("gdcm2pnm %s %s" % (filename, converted))
+ os.unlink(filename)
+
+ self.img = Image.open(converted)
+
+ def dump(self):
+ """To be called after PIL modifications"""
+ destfile = StringIO.StringIO()
+ self.img.save(destfile, self.format.upper())
+
+ destfile.seek(0)
+ return destfile.read()
+
+ def contrast(self, windowWidth):
+ enhancer = ImageEnhance.Brightness(self.img)
+ self.img = enhancer.enhance(float(windowWidth))
+
+ def brightness(self, windowCenter):
+ enhancer = ImageEnhance.Contrast(self.img)
+ self.img = enhancer.enhance(float(windowCenter))
+
+ def resize(self, rows, columns):
+ size = (int(rows), int(columns))
+ newsize = calc_size(self.img.size, size)
+
+ algorithm = Image.BICUBIC
+ if newsize[0] < self.img.size[0]:
+ algorithm = Image.ANTIALIAS
+
+ self.img = self.img.resize(newsize, algorithm)
+
+ def crop(self, left, upper, right, lower):
+ self.img = self.img.crop((left, upper, right, lower))
+
+ def raw(self):
+ return open(self.filename).read()
+
+if __name__ == "__main__":
+
+ img = Dicom('/var/tmp/1.3.76.13.10010.0.5.74.3996.1224513675.10543/CT.1.2.840.113619.2.81.290.3160.35958.3.9.20081020.270228', 'png')
diff --git a/gdcm/Applications/Python/lib.py b/gdcm/Applications/Python/lib.py
new file mode 100644
index 0000000..6cd496c
--- /dev/null
+++ b/gdcm/Applications/Python/lib.py
@@ -0,0 +1,27 @@
+# Copyright 2008 Emanuele Rocca
+# Copyright 2008 Marco De Benedetto
+# Copyright (c) 2006-2011 Mathieu Malaterre
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import commands
+
+class CmdException(Exception):
+ """Exception representing a command line error"""
+ pass
+
+def trycmd(cmd):
+ """Try to execute the given command, raising an Exception on errors"""
+ (exitstatus, outtext) = commands.getstatusoutput(cmd)
+ if exitstatus != 0:
+ raise CmdException, "cmd: %s\noutput: %s" % (cmd, outtext)
diff --git a/gdcm/Applications/Python/wado.py b/gdcm/Applications/Python/wado.py
new file mode 100644
index 0000000..fa16680
--- /dev/null
+++ b/gdcm/Applications/Python/wado.py
@@ -0,0 +1,189 @@
+#!/usr/bin/python
+# Copyright 2008 Emanuele Rocca
+# Copyright 2008 Marco De Benedetto
+# Copyright (c) 2006-2011 Mathieu Malaterre
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+A WADO server, compliant with section 3.18 of the DICOM standard.
+
+See http://www.dclunie.com/dicom-status/status.html for more information about
+DICOM.
+
+Basic usage example:
+
+requestType=WADO&studyUID=1.3.76.13.10010.0.5.74.6120.1224052386.1578&seriesUID=1.3.76.2.1.1.3.1.3.3115.274695982&objectUID=1.3.76.2.1.1.3.1.3.3115.274695982.26&contentType=image/png
+"""
+
+import cherrypy
+
+from flup.server.fcgi import WSGIServer
+
+import sys
+import os
+
+CURDIR = os.path.dirname(os.path.abspath(__file__))
+
+import accessdata
+
+try:
+ testing = sys.argv[1] == "test"
+ BASEURL = "/"
+except IndexError:
+ BASEURL = "/wado/"
+ testing = False
+
+REQUIRED = ( 'requestType', 'studyUID', 'seriesUID', 'objectUID' )
+
+OPTIONAL = ( 'contentType', 'charset', 'anonymize', 'annotation',
+ 'rows', 'columns', 'region',
+ 'windowWidth', 'windowCenter', 'frameNumber', 'imageQuality',
+ 'presentationUID', 'presentationSeriesUID', 'transferSyntax' )
+
+INVALID_DICOM = ( 'annotation', 'rows', 'columns', 'region',
+ 'windowWidth', 'windowCenter' )
+
+INVALID_NONDICOM = ( 'anonymize', )
+
+def err(msg):
+ """Function to handle errors"""
+ raise Exception, msg
+
+def check_params(kwargs):
+ """Validate and sanitize requests"""
+ # TODO: implement every check
+ valid = REQUIRED + OPTIONAL
+
+ curparams = kwargs.keys()
+
+ # WADO is the only requestType currently accepted by the standard
+ assert kwargs['requestType'] == "WADO"
+
+ # checking unknown parameters
+ for par in curparams:
+ if par not in valid:
+ err("Unknown parameter: " + par)
+
+ # checking missing parameters
+ for par in REQUIRED:
+ if par not in curparams:
+ err("Missing parameter: " + par)
+
+ # default content type is image/jpeg
+ kwargs['contentType'] = kwargs.get('contentType', 'image/jpeg')
+
+ # checking values for contentType = application/dicom
+ if kwargs['contentType'] == 'application/dicom':
+ for par in INVALID_DICOM:
+ if par in curparams:
+ err(par + " is not valid if contentType is application/dicom")
+
+ # Validation finished
+ return
+
+ # checking values for contentType != application/dicom
+ for par in INVALID_NONDICOM:
+ if par in curparams:
+ err(par + " is valid only if contentType is application/dicom")
+
+ if 'annotation' in curparams:
+ assert kwargs['annotation'] in ('patient', 'technique')
+
+ if 'windowWidth' in curparams:
+ assert 'windowCenter' in curparams
+
+ if 'windowCenter' in curparams:
+ assert 'windowWidth' in curparams
+
+ if 'region' in curparams:
+ region = kwargs['region'].split(',')
+
+ assert len(region) == 4
+
+ for val in region:
+ assert 0.0 <= float(val) <= 1.0
+
+class Wado:
+ """Wado controller"""
+
+ @cherrypy.expose
+ def index(self, **kwargs):
+ cherrypy.log(str(kwargs))
+
+ check_params(kwargs)
+
+ cherrypy.response.headers['Content-Type'] = kwargs['contentType']
+ cherrypy.response.headers['Pragma'] = 'cache'
+
+ if kwargs['contentType'] == "application/dicom":
+ format = "dicom"
+ else:
+ # image/png -> png, image/jpeg -> jpeg
+ format = kwargs['contentType'].replace('image/', '')
+
+ # getting DICOM image from accessdata
+ image = accessdata.get(studyUID=kwargs['studyUID'],
+ seriesUID=kwargs['seriesUID'],
+ objectUID=kwargs['objectUID'],
+ format=format)
+
+ if kwargs['contentType'] == "application/dicom":
+ return image.raw()
+
+ if 'windowWidth' in kwargs:
+ image.brightness(kwargs['windowWidth'])
+
+ if 'windowCenter' in kwargs:
+ image.contrast(kwargs['windowCenter'])
+
+ if 'region' in kwargs:
+ left, upper, right, lower = [
+ float(val) for val in kwargs['region'].split(",")
+ ]
+ # coordinates normalization
+ width, height = image.img.size
+ # 1 : width = left : x
+ image.crop(width * left, height * upper,
+ width * right, height * lower)
+
+ if 'rows' in kwargs or 'columns' in kwargs:
+ image.resize(kwargs['rows'], kwargs['columns'])
+
+ return image.dump()
+
+
+if __name__ == "__main__":
+ cherrypy.config.update({
+ #'server.socket_port': 7777,
+ #'server.thread_pool': 1,
+ #'environment': testing and 'testing' or 'production',
+ #'log.access_file': 'access.log',
+ #'log.error_file': 'errors.log',
+ #'tools.sessions.on': True,
+ 'tools.gzip.on': True,
+ 'tools.caching.on': True
+ })
+
+ conf = {}
+
+ app = cherrypy.tree.mount(Wado(), BASEURL, config=conf)
+
+ if testing:
+ cherrypy.quickstart(app)
+ else:
+ cherrypy.engine.start(blocking=False)
+ try:
+ WSGIServer(app).run()
+ finally:
+ cherrypy.engine.stop()
diff --git a/gdcm/Applications/README.txt b/gdcm/Applications/README.txt
new file mode 100644
index 0000000..3d59b12
--- /dev/null
+++ b/gdcm/Applications/README.txt
@@ -0,0 +1,10 @@
+I know I will regret that later, but for now Application are part of GDCM
+
+Naming convention, pretty much the same used for tiff (tiffinfo and tiffdump),
+ we have gdcmdump, gdcminfo (should I rename them dcmdump. dcminfo ?
+ but it will collide with dcmtk...)
+
+TODO:
+I have this file: acc-max.dcm it's pretty much a ACR-NEMA file,
+but it contains Modality. It would be great if gdcmconv would handle this file
+ and generate a file that would make dciodvfy happy
diff --git a/gdcm/CMake/CMakeCSharpCompiler.cmake.in b/gdcm/CMake/CMakeCSharpCompiler.cmake.in
new file mode 100644
index 0000000..ac5200d
--- /dev/null
+++ b/gdcm/CMake/CMakeCSharpCompiler.cmake.in
@@ -0,0 +1,56 @@
+set(CMAKE_CSharp_COMPILER "@CMAKE_CSharp_COMPILER@")
+set(CMAKE_CSharp_COMPILER_ARG1 "@CMAKE_CSharp_COMPILER_ARG1@")
+#set(CMAKE_Fortran_COMPILER_ID "@CMAKE_Fortran_COMPILER_ID@")
+#set(CMAKE_Fortran_PLATFORM_ID "@CMAKE_Fortran_PLATFORM_ID@")
+#set(CMAKE_AR "@CMAKE_AR@")
+#set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+# Should none on Win32, and 'mono' on unix
+set(CMAKE_CSharp_RUNTIME "@CMAKE_CSharp_RUNTIME@")
+set(CMAKE_CSharp_ARCHIVE "@CMAKE_CSharp_ARCHIVE@") # gacutil ??
+
+set(CMAKE_CSharp_COMPILER_LOADED 1)
+
+set(CMAKE_CSharp_COMPILER_ENV_VAR "CSC")
+
+set(CMAKE_CSharp_SOURCE_FILE_EXTENSIONS cs;CS)
+set(CMAKE_CSharp_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
+set(CMAKE_CSharp_LINKER_PREFERENCE 20)
+set(CMAKE_STATIC_LIBRARY_PREFIX_CSharp "")
+set(CMAKE_STATIC_LIBRARY_SUFFIX_CSharp ".dll")
+set(CMAKE_SHARED_LIBRARY_PREFIX_CSharp "")
+set(CMAKE_SHARED_LIBRARY_SUFFIX_CSharp ".dll")
+
+# FIXME ... where should it go
+#set(CMAKE_EXECUTABLE_SUFFIX ".exe")
+
+set(CMAKE_STATIC_LIBRARY_CREATE_CSharp_FLAGS "/target:library")
+set(CMAKE_STATIC_LIBRARY_CSharp_FLAGS "/target:library")
+#CMAKE_STATIC_LIBRARY_CSharp_FLAGS
+
+# partial library
+set(CMAKE_MODULE_LIBRARY_CREATE_CSharp_FLAGS "/target:module")
+set(CMAKE_MODULE_LIBRARY_CSharp_FLAGS "/target:module")
+
+# static
+set(CMAKE_SHARED_LIBRARY_CREATE_CSharp_FLAGS "/target:library")
+set(CMAKE_SHARED_LIBRARY_CSharp_FLAGS "/target:library")
+#set(CMAKE_SHARED_LIBRARY_LINK_CSharp_FLAGS "-r")
+#set(CMAKE_SHARED_LIBRARY_RUNTIME_CSharp_FLAG "-r")
+#set(CMAKE_SHARED_LIBRARY_LINK_CSharp_FLAGS "-r")
+
+# FIXME: CMAKE_LINK_LIBRARY_FILE_FLAG always add a space, so I cannot simply use "/r" because
+# I would need to remove the space, but instead use the -r that tolerate a space
+#set(CMAKE_LINK_LIBRARY_FILE_FLAG "")
+#set(CMAKE_LINK_LIBRARY_FLAG "-r")
+
+#set(CMAKE_CREATE_WIN32_EXE /target:winexe)
+#set(CMAKE_CREATE_CONSOLE_EXE /target:exe)
+
+#set(CMAKE_LINK_LIBRARY_FLAG "-l")
+
+#set(CMAKE_EXECUTABLE_RUNTIME_CSharp_FLAG "-foo")
+
+if(WIN32)
+else()
+ set(CMAKE_CSHARP_INTERPRETER ${MONO_EXECUTABLE})
+endif()
diff --git a/gdcm/CMake/CMakeCSharpInformation.cmake b/gdcm/CMake/CMakeCSharpInformation.cmake
new file mode 100644
index 0000000..c6dd1b3
--- /dev/null
+++ b/gdcm/CMake/CMakeCSharpInformation.cmake
@@ -0,0 +1,92 @@
+
+# This file sets the basic flags for the CSharp language in CMake.
+# It also loads the available platform file for the system-compiler
+# if it exists.
+
+#set(CMAKE_BASE_NAME)
+#get_filename_component(CMAKE_BASE_NAME ${CMAKE_CSharp_COMPILER} NAME_WE)
+#set(CMAKE_SYSTEM_AND_CSharp_COMPILER_INFO_FILE
+# #${CMAKE_ROOT}/Modules/Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME}.cmake)
+# ${CMAKE_ROOT}/Modules/Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME}.cmake)
+#message(${CMAKE_SYSTEM_AND_CSharp_COMPILER_INFO_FILE})
+#include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL)
+
+# This should be included before the _INIT variables are
+# used to initialize the cache. Since the rule variables
+# have if blocks on them, users can still define them here.
+# But, it should still be after the platform file so changes can
+# be made to those values.
+
+if(CMAKE_USER_MAKE_RULES_OVERRIDE)
+ include(${CMAKE_USER_MAKE_RULES_OVERRIDE})
+endif()
+
+# Copy from CSharp, ref CXX ???
+if(CMAKE_USER_MAKE_RULES_OVERRIDE_CSHARP)
+ include(${CMAKE_USER_MAKE_RULES_OVERRIDE_CSHARP})
+endif()
+
+#
+# the target without the suffix
+#
+#