/*========================================================================= 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 a simple example that show typical pipeline to setup when * preprocessing incoming DICOM file from around the round, and making * sur eto remove any Patient Information. * This is actually identical to running the C++ command line tool: gdcmanon, * except this is easily integrated into another C# environment. * * PS 3.17 - 2008 Annex H. * Clinical Trial Identification Workflow Examples (Informative) * * This Annex was formerly located in Annex O of PS 3.3 in the 2003 and earlier * revisions of the standard. The Clinical Trial Identification modules are * optional. As such, there are several points in the workflow of clinical trial * data at which the Clinical Trial Identification attributes may be added to the * data. At the Clinical Trial Site, the attributes may be added at the scanner, a * PACS system, a site workstation, or a workstation provided to the site by a * Clinical Trial Coordinating Center. If not added at the site, the Clinical * Trial Identification attributes may be added to the data after receipt by the * Clinical Trial Coordinating Center. The addition of clinical trial attributes * does not itself require changes to the SOP Instance UID. However, the clinical * trial protocol or the process of de-identification may require such a change. */ /* * Typical usage on UNIX: * $ export LD_LIBRARY_PATH=$HOME/Projects/gdcm/debug-gcc/bin * $ mono bin/ClinicalTrialIdentificationWorkflow.exe input_dir output_dir */ using System; using gdcm; public class MyWatcher : SimpleSubjectWatcher { public MyWatcher(Subject s):base(s,"Override String"){} protected override void StartFilter() { System.Console.WriteLine( "This is my start" ); } protected override void EndFilter(){ System.Console.WriteLine( "This is my end" ); } protected override void ShowProgress(Subject caller, Event evt){ ProgressEvent pe = ProgressEvent.Cast(evt); System.Console.WriteLine( "This is my progress: " + pe.GetProgress() ); } protected override void ShowIteration(){ System.Console.WriteLine( "This is my iteration" ); } protected override void ShowAnonymization(Subject caller, Event evt){ /* * A couple of explanation are necessary here to understand how SWIG work * http://www.swig.org/Doc1.3/Java.html#adding_downcasts * * System.Console.WriteLine( "This is my Anonymization. Type: " + evt.GetEventName() ); * System.Type type = evt.GetType(); * System.Console.WriteLine( "This is my Anonymization. System.Type: " + type.ToString() ); * System.Console.WriteLine( "This is my Anonymization. CheckEvent: " + ae.CheckEvent( evt ) ); * System.Console.WriteLine( "This is my Anonymization. Processing Tag #" + ae.GetTag().toString() ); */ AnonymizeEvent ae = AnonymizeEvent.Cast(evt); if( ae != null ) { Tag t = ae.GetTag(); System.Console.WriteLine( "This is my Anonymization. Processing Tag #" + t.toString() ); } else { System.Console.WriteLine( "This is my Anonymization. Unhandled Event type: " + evt.GetEventName() ); } } protected override void ShowAbort(){ System.Console.WriteLine( "This is my abort" ); } } public class ClinicalTrialIdentificationWorkflow { public static bool ProcessOneFile( gdcm.Anonymizer ano , string filename, string outfilename ) { Reader reader = new Reader(); reader.SetFileName( filename ); bool ret = reader.Read(); if( !ret ) { return false; } // Pass in the file: ano.SetFile( reader.GetFile() ); // First step, let's protect all Patient information as per // PS 3.15 / E.1 / Basic Application Level Confidentiality Profile if( !ano.BasicApplicationLevelConfidentialityProfile() ) { return false; } // Now let's pass in all Clinical Trial fields // PS 3.3 - 2008 / C.7.1.3 Clinical Trial Subject Module /* Clinical Trial Sponsor Name (0012,0010) 1 The name of the clinical trial sponsor. See C.7.1.3.1.1. Clinical Trial Protocol ID (0012,0020) 1 Identifier for the noted protocol. See C.7.1.3.1.2. Clinical Trial Protocol Name (0012,0021) 2 The name of the clinical trial protocol. See C.7.1.3.1.3. Clinical Trial Site ID (0012,0030) 2 The identifier of the site responsible for submitting clinical trial data. See C.7.1.3.1.4. Clinical Trial Site Name (0012,0031) 2 Name of the site responsible for submitting clinical trial data. See C.7.1.3.1.5 Clinical Trial Subject ID (0012,0040) 1C The assigned identifier for the clinical trial subject. See C.7.1.3.1.6. Shall be present if Clinical Trial Subject Reading ID (0012,0042) is absent. May be present otherwise. Clinical Trial Subject Reading ID (0012,0042) 1C Identifies the subject for blinded evaluations. Shall be present if Clinical Trial Subject ID (0012,0040) is absent. May be present otherwise. See C.7.1.3.1.7. */ ano.Replace( new gdcm.Tag(0x0012,0x0010), "MySponsorName"); ano.Replace( new gdcm.Tag(0x0012,0x0020), "MyProtocolID"); ano.Replace( new gdcm.Tag(0x0012,0x0021), "MyProtocolName"); ano.Replace( new gdcm.Tag(0x0012,0x0030), "MySiteId"); ano.Replace( new gdcm.Tag(0x0012,0x0031), "MySiteName"); ano.Replace( new gdcm.Tag(0x0012,0x0040), "MySponsorId"); ano.Replace( new gdcm.Tag(0x0012,0x0050), "MyTPId"); ano.Replace( new gdcm.Tag(0x0012,0x0051), "MyTPDescription"); // The following two are not required as they are guaranteed to be filled in by the // Basic Application Level Confidentiality Profile. Only override if you understand what // you are doing //ano.Replace( new gdcm.Tag(0x0012,0x0062), "YES"); //ano.Replace( new gdcm.Tag(0x0012,0x0063), "My Super Duper Anonymization Overload"); // We might be generating a subdirectory. Let's make sure the subdir exist: gdcm.Filename fn = new gdcm.Filename( outfilename ); string subdir = fn.GetPath(); if( !gdcm.PosixEmulation.MakeDirectory( subdir ) ) { return false; } gdcm.FileMetaInformation fmi = ano.GetFile().GetHeader(); // The following three lines make sure to regenerate any value: fmi.Remove( new gdcm.Tag(0x0002,0x0012) ); fmi.Remove( new gdcm.Tag(0x0002,0x0013) ); fmi.Remove( new gdcm.Tag(0x0002,0x0016) ); Writer writer = new Writer(); writer.SetFileName( outfilename ); writer.SetFile( ano.GetFile() ); ret = writer.Write(); if( !ret ) { return false; } return true; } public static int Main(string[] args) { gdcm.FileMetaInformation.SetSourceApplicationEntityTitle( "My ClinicalTrial App" ); // http://www.oid-info.com/get/1.3.6.1.4.17434 string THERALYS_ORG_ROOT = "1.3.6.1.4.17434"; gdcm.UIDGenerator.SetRoot( THERALYS_ORG_ROOT ); System.Console.WriteLine( "Root dir is now: " + gdcm.UIDGenerator.GetRoot() ); gdcm.Global global = gdcm.Global.GetInstance(); if( !global.LoadResourcesFiles() ) { System.Console.WriteLine( "Could not LoadResourcesFiles" ); return 1; } if( args.Length != 2 ) { System.Console.WriteLine( "Usage:" ); System.Console.WriteLine( "ClinicalTrialIdentificationWorkflow input_dir output_dir" ); return 1; } string dir1 = args[0]; string dir2 = args[1]; // Check input is valid: if( !gdcm.PosixEmulation.FileIsDirectory(dir1) ) { System.Console.WriteLine( "Input directory: " + dir1 + " does not exist. Sorry" ); return 1; } if( !gdcm.PosixEmulation.FileIsDirectory(dir2) ) { System.Console.WriteLine( "Output directory: " + dir2 + " does not exist. Sorry" ); return 1; } // Recursively search all file within this toplevel directory: Directory d = new Directory(); uint nfiles = d.Load( dir1, true ); if(nfiles == 0) return 1; // Let's use the pre-shipped certificate of GDCM. string certpath = gdcm.Filename.Join(gdcm.Testing.GetSourceDirectory(), "/Testing/Source/Data/certificate.pem" ); gdcm.CryptoFactory fact = gdcm.CryptoFactory.GetFactoryInstance(); gdcm.CryptographicMessageSyntax cms = fact.CreateCMSProvider(); if( !cms.ParseCertificateFile( certpath ) ) { System.Console.WriteLine( "PEM Certificate : " + certpath + " could not be read. Sorry" ); return 1; } //Anonymizer ano = new Anonymizer(); // A reference to an actual C++ instance is required here: SmartPtrAno sano = Anonymizer.New(); Anonymizer ano = sano.__ref__(); //SimpleSubjectWatcher watcher = new SimpleSubjectWatcher(ano, "Anonymizer"); MyWatcher watcher = new MyWatcher(ano); // Explicitly specify the Cryptographic Message Syntax to use: ano.SetCryptographicMessageSyntax( cms ); // Process all filenames: FilenamesType filenames = d.GetFilenames(); for( uint i = 0; i < nfiles; ++i ) { string filename = filenames[ (int)i ]; string outfilename = filename.Replace( dir1, dir2 ); System.Console.WriteLine( "Filename: " + filename ); System.Console.WriteLine( "Out Filename: " + outfilename ); if( !ProcessOneFile( ano , filename, outfilename ) ) { System.Console.WriteLine( "Could not process filename: " + filename ); return 1; } } return 0; } }