FileChangeTSLossy.cs
/*=========================================================================
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;
}
}
gdcm::Bitmap::SetNumberOfDimensions
void SetNumberOfDimensions(unsigned int dim)
gdcm::Bitmap::SetDataElement
void SetDataElement(DataElement const &de)
Definition gdcmBitmap.h:76
gdcm::Bitmap::SetDimension
void SetDimension(unsigned int idx, unsigned int dim)
gdcm::Bitmap::SetPhotometricInterpretation
void SetPhotometricInterpretation(PhotometricInterpretation const &pi)
gdcm::DataElement
Class to represent a Data Element either Implicit or Explicit.
Definition gdcmDataElement.h:59
gdcm::DataElement::SetByteValue
void SetByteValue(const char *array, VL length)
Definition gdcmDataElement.h:126
gdcm::DataSet
Class to represent a Data Set (which contains Data Elements).
Definition gdcmDataSet.h:56
gdcm::DataSet::Insert
void Insert(const DataElement &de)
Definition gdcmDataSet.h:128
gdcm::Exception
Exception.
Definition gdcmException.h:44
gdcm::FileAnonymizer
FileAnonymizer.
Definition gdcmFileAnonymizer.h:48
gdcm::FileChangeTransferSyntax
FileChangeTransferSyntax.
Definition gdcmFileChangeTransferSyntax.h:40
gdcm::FileChangeTransferSyntax::Change
bool Change()
Change the transfer syntax.
gdcm::FileChangeTransferSyntax::SetOutputFileName
void SetOutputFileName(const char *filename_native)
Set output filename (target compressed DICOM).
gdcm::FileChangeTransferSyntax::New
static SmartPointer< FileChangeTransferSyntax > New()
for wrapped language: instantiate a reference counted object
Definition gdcmFileChangeTransferSyntax.h:62
gdcm::FileChangeTransferSyntax::SetInputFileName
void SetInputFileName(const char *filename_native)
Set input filename (raw DICOM).
gdcm::FileChangeTransferSyntax::SetTransferSyntax
void SetTransferSyntax(TransferSyntax const &ts)
Specify the Target Transfer Syntax.
gdcm::FileChangeTransferSyntax::GetCodec
ImageCodec * GetCodec()
gdcm::FileStreamer
FileStreamer.
Definition gdcmFileStreamer.h:42
gdcm::File
a DICOM File
Definition gdcmFile.h:34
gdcm::File::GetDataSet
const DataSet & GetDataSet() const
Get Data Set.
Definition gdcmFile.h:57
gdcm::ImageCodec
ImageCodec.
Definition gdcmImageCodec.h:31
gdcm::JPEGCodec
JPEG codec.
Definition gdcmJPEGCodec.h:41
gdcm::JPEGCodec::SetLossless
void SetLossless(bool l)
gdcm::JPEGCodec::SetQuality
void SetQuality(double q)
gdcm::PhotometricInterpretation
Class to represent an PhotometricInterpretation.
Definition gdcmPhotometricInterpretation.h:29
gdcm::PhotometricInterpretation::PIType
PIType
Definition gdcmPhotometricInterpretation.h:31
gdcm::PixmapWriter
PixmapWriter.
Definition gdcmPixmapWriter.h:37
gdcm::Pixmap
Pixmap class.
Definition gdcmPixmap.h:33
gdcm::SimpleSubjectWatcher
SimpleSubjectWatcher.
Definition gdcmSimpleSubjectWatcher.h:32
gdcm::System
Class to do system operation.
Definition gdcmSystem.h:27
gdcm::Tag
Class to represent a DICOM Data Element (Attribute) Tag (Group, Element).
Definition gdcmTag.h:39
gdcm::TransferSyntax
Class to manipulate Transfer Syntax.
Definition gdcmTransferSyntax.h:40
gdcm::TransferSyntax::TSType
TSType
Definition gdcmTransferSyntax.h:61
gdcm::VL
Value Length.
Definition gdcmVL.h:30
gdcm
Definition gdcmASN1.h:21