1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 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.
=========================================================================*/
/*
* Simple C# example
*
* Shows multiple steps:
* Steps 1.
* Create a fake (dummy) DICOM file, with size 512 x 512 x 2 We use a small
* image to be able to create the volume in memory Of course you can use any
* existing DICOM instead
*
* Step 2.
* Hack the DICOM file to pretend the number of frames is 1000 (instead of 2)
* At this point in time this makes the DICOM file invalid (truncated). But the
* next step will fix this.
*
* Step 3.
* Use C# to create a binary data which will represent our source object for
* image.
*
* Step 4.
* We use gdcm.FileStreamer to merge the template DICOM file from Step 2, with
* the binary data from Step 3. We decide to read a scanline at a time, but
* this can be read with any number of bytes. AppendToDataElement() will always
* do the proper computation.
*
* Step 5.
* We compress this gigantic file, into [JPEG Baseline (Process 1): Default Transfer Syntax for Lossy JPEG 8 Bit Image Compression]
*
* Usage:
* $ bin/FileChangeTSLossy.exe small.dcm big.dcm raw.data merge.dcm jpeg.dcm
*/
using System;
using System.IO;
using gdcm;
public class FileChangeTS
{
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
// Create a 256 x 256 Secondary Capture Image Storage
static private void CreateSmallDICOM(string fileName)
{
using( var writer = new gdcm.PixmapWriter() )
{
gdcm.Pixmap img = writer.GetImage();
img.SetNumberOfDimensions( 3 );
img.SetDimension(0, 512 );
img.SetDimension(1, 512 );
img.SetDimension(2, 2 ); // fake a 3d volume
PhotometricInterpretation pi = new PhotometricInterpretation( PhotometricInterpretation.PIType.MONOCHROME2 );
img.SetPhotometricInterpretation( pi );
gdcm.DataElement pixeldata = new gdcm.DataElement( new gdcm.Tag(0x7fe0,0x0010) );
byte[] buffer = new byte[ 512 * 512 * 2 ];
pixeldata.SetByteValue( buffer, new gdcm.VL((uint)buffer.Length) );
img.SetDataElement( pixeldata );
gdcm.File file = writer.GetFile();
gdcm.DataSet ds = file.GetDataSet();
gdcm.DataElement ms = new gdcm.DataElement(new gdcm.Tag(0x0008,0x0016));
string mediastorage = "1.2.840.10008.5.1.4.1.1.7.2"; // Multi-frame Grayscale Byte Secondary Capture Image Storage
byte[] val = StrToByteArray(mediastorage);
ms.SetByteValue( val, new gdcm.VL( (uint)val.Length) );
ds.Insert( ms );
writer.SetFileName( fileName );
writer.Write();
}
}
static private void CreateBigDICOM(string fileName, string outfilename)
{
using( var ano = new gdcm.FileAnonymizer() )
{
// The following is somewhat dangerous, do not try at home:
string nframes = "1000";
ano.Replace( new gdcm.Tag(0x0028,0x0008), nframes );
ano.SetInputFileName(fileName);
ano.SetOutputFileName(outfilename);
ano.Write(); // at this point the DICOM is invalid !
}
}
static private void CreateDummyFile(string fileName, long length)
{
using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
// Looks like C# always init to 0 (fallocate ?)
// For the purpose of the test we could add some random noise
fileStream.SetLength(length);
}
}
static private void ReadBytesIntoArray( byte[] array, FileStream source )
{
int numBytesToRead = array.Length;
int numBytesRead = 0;
while (numBytesToRead > 0)
{
// According to spec: Read() may return anything from 0 to numBytesToRead.
int n = source.Read(array, numBytesRead, numBytesToRead);
// Break when the end of the file is reached.
if (n == 0)
break;
numBytesRead += n;
numBytesToRead -= n;
}
}
static private void AssembleDICOMAndRaw(string dicomfn, string rawdata, string outfn)
{
using ( var fs = new gdcm.FileStreamer() )
{
fs.SetTemplateFileName(dicomfn);
fs.SetOutputFileName(outfn);
gdcm.Tag pixeldata = new gdcm.Tag(0x7fe0, 0x0010);
// FileStreamer support automatic checking of pixel data length
// based on DICOM attributes, only if we say so:
fs.CheckDataElement( pixeldata );
// Declare we are working on Pixel Data attribute:
fs.StartDataElement( pixeldata );
using (FileStream rawSource = new FileStream(rawdata,
FileMode.Open, FileAccess.Read))
{
byte[] bytes = new byte[512];
// Only read one scanline at a time
// We could have been reading more at once, if this is more efficient,
// AppendToDataElement will do the logic in all cases.
for( int i = 0; i < 512 * 1000; ++i )
{
// Read the source file into a byte array.
ReadBytesIntoArray( bytes, rawSource );
fs.AppendToDataElement( pixeldata, bytes, (uint)bytes.Length );
}
}
if( !fs.StopDataElement( pixeldata ) )
{
// Most likely an issue with Pixel Data Length computation:
throw new Exception("StopDataElement failed");
}
}
}
static private void CompressIntoJPEG(string rawdicom, string jpegdicom)
{
using( var sfcts = FileChangeTransferSyntax.New() )
{
// Need to retrieve the actual C++ reference, to pass to
// SimpleSubjectWatcher:
FileChangeTransferSyntax fcts = sfcts.__ref__();
SimpleSubjectWatcher watcher = new SimpleSubjectWatcher(fcts, "FileChangeTransferSyntax");
gdcm.TransferSyntax ts = new TransferSyntax( TransferSyntax.TSType.JPEGBaselineProcess1 );
fcts.SetTransferSyntax( ts );
ImageCodec ic = fcts.GetCodec();
JPEGCodec jpeg = JPEGCodec.Cast( ic );
jpeg.SetLossless( false );
jpeg.SetQuality( 50 ); // poor quality !
fcts.SetInputFileName( rawdicom );
fcts.SetOutputFileName( jpegdicom );
fcts.Change();
}
}
public static int Main(string[] args)
{
string filename = args[0];
string outfilename = args[1];
string rawfilename = args[2];
string mergefn = args[3];
string jpegfn = args[4];
CreateSmallDICOM(filename);
CreateBigDICOM(filename, outfilename);
CreateDummyFile(rawfilename, 512 * 512 * 1000 );
AssembleDICOMAndRaw(outfilename, rawfilename, mergefn);
CompressIntoJPEG(mergefn, jpegfn);
return 0;
}
}
|