This tutorial shows how to load and display 2D and 3D images from a DICOM file-set's DICOMDIR through a series browser dialog in the LEADTOOLS MedicalViewer
WinForms control.
Overview | |
---|---|
Summary | This tutorial shows how to load medical images from the DICOMDIR file in a DICOM file-set using a series browser dialog in a WinForms C# Application. |
Completion Time | 45 minutes |
Visual Studio Project | Download tutorial project (22 KB) |
Platform | Windows WinForms C# Application |
IDE | Visual Studio 2022 |
Development License | Download LEADTOOLS |
Get familiar with the basic steps of creating a project and displaying a DICOM image by reviewing the Add References and Set a License and Load and Display a DICOM Image in the Medical Viewer tutorials, before working on the Load DICOMDIR using a Series Browser Dialog - WinForms C# tutorial.
Start with a copy of the project created in the Load and Display a DICOM Image in the Medical Viewer tutorial. If the project is not available, follow the steps in that tutorial to create it.
The references needed depend upon the purpose of the project. References can be added by one or the other of the following two methods (but not both).
If using NuGet references, this tutorial requires the following NuGet package:
Leadtools.Medical.Viewer.WinForms
Leadtools.Jpeg2000
If using local DLL references, the following DLLs are needed.
The DLLs are located at <INSTALL_DIR>\LEADTOOLS22\Bin\Dotnet4\x64
:
Leadtools.dll
Leadtools.Codecs.Cmp.dll
Leadtools.Codecs.J2k.dll
Leadtools.Dicom.dll
Leadtools.MedicalViewer.dll
Leadtools.Medical3D.dll
For a complete list of which DLL files are required for your application, refer to Files to be Included With Your Application.
The License unlocks the features needed for the project. It must be set before any toolkit function is called. For details, including tutorials for different platforms, refer to Setting a Runtime License.
There are two types of runtime licenses:
Now that the LEADTOOLS references have been added to the project and the license has been set, coding can begin.
In the Solution Explorer, right-click the project and select Add -> Add New Item... then choose to add a Form (Windows Form) called "SeriesBrowser". From the Toolbox, add a Panel that will hold the dialog's buttons. Set the Dock property to Bottom.
Add a SplitContainer and set the Orientation property to Horizontal.
Add a DataGridView control to each of the panels. Set the name for the top DataGridView control to studiesDataGridView and the bottom control to seriesDataGridView. Set the following properties for each of the DataGridView controls:
Modify the Columns property for the studiesDataGridView control and add the following columns:
Modify the Columns property for the seriesDataGridView control and add the following columns:
Add the following buttons to the Panel control:
Name: btnClear, Enabled: False, Text: Clear, Visible: False
With the dialog form created, right-click the SeriesBrowser.cs
file in the Solution Explorer and select View Code.
Modify the Using
block at the top of the file to be as follows:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Leadtools;
using Leadtools.Dicom;
using Leadtools.MedicalViewer;
Double-click the Add DICOMDIR button to add the onClick event handler. Add the following code to show an Open File Dialog
to select the DICOMDIR file from a DICOM file-set then load the Studies and Series information contained for display in the DataGridView controls.
private void btnAddDICOMDIR_Click(object sender, EventArgs e)
{
DicomDataSet dicomdirDataSet = new DicomDataSet();
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "DICOMDIR Files|DICOMDIR|All files (*.*)|*.*";
dialog.FilterIndex = 1;
try
{
if (dialog.ShowDialog() == DialogResult.OK)
{
if (dialog.FileName == null)
return;
dicomdirDataSet.Load(dialog.FileName, DicomDataSetLoadFlags.None);
// Add the DICOMDIR study to the study Grid.
AddStudyRows(dicomdirDataSet, dialog.FileName);
// Select last loaded study
int lastRowIndex = studiesDataGridView.Rows.Count - 1;
studiesDataGridView.CurrentCell = studiesDataGridView.Rows[lastRowIndex].Cells[0];
ViewSeriesRowsForThisStudy(lastRowIndex);
// Show and enable Buttons
btnLoad.Visible = true;
btnClear.Visible = true;
dicomdirDataSet.Dispose();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, dialog.FileName, MessageBoxButtons.OK);
}
}
Add a new function named AddStudyRows(DicomDataSet dicomdirDataSet, string dicomdirFileName)
. This method will be called inside the btnAddDICOMDIR_Click
event handler, as shown above. Add the code below to fill in the rows' information for the studiesDataGridView control for every patient element in the DICOM file-set.
private void AddStudyRows(DicomDataSet dicomdirDataSet, string dicomdirFileName)
{
// Get Patient Dicom Element
DicomElement patientElement = dicomdirDataSet.GetFirstKey(null, true);
if (patientElement == null)
{
MessageBox.Show("Patient Element is Null", "Invalid DICOMDIR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
try
{
DicomElement studyElement;
DicomElement patientNameInformation;
DicomElement patientIdInformation;
DicomElement patientTagInformation;
string[] row = new string[8];
string patientName = "";
string patientId = "";
// All the information that will be extracted from the studyElement.
// This method will extract these tags and 2 others (Patient Name and Patient ID).
long[] tags =
{
DicomTag.StudyDescription,
DicomTag.AccessionNumber,
DicomTag.StudyDate,
DicomTag.ReferringPhysicianName,
DicomTag.StudyInstanceUID
};
// Patient Name
patientNameInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientName, false);
if (patientNameInformation != null)
patientName = dicomdirDataSet.GetConvertValue(patientNameInformation);
// Patient ID
patientIdInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientID, false);
if (patientIdInformation != null)
patientId = dicomdirDataSet.GetConvertValue(patientIdInformation);
while (patientNameInformation != null)
{
// Get the first Study
studyElement = dicomdirDataSet.GetChildKey(patientElement);
studyElement = dicomdirDataSet.GetChildElement(studyElement, true);
while (studyElement != null)
{
row[0] = patientName;
row[1] = patientId;
// Rest of Tags
for (int i = 2; i < 7; i++)
{
patientTagInformation = dicomdirDataSet.FindFirstElement(studyElement, tags[i - 2], true);
if (patientTagInformation != null)
row[i] = dicomdirDataSet.GetConvertValue(patientTagInformation);
}
row[7] = dicomdirFileName;
bool alreadyExists = false;
foreach (DataGridViewRow studyRow in studiesDataGridView.Rows)
if (studyRow.Cells[6].Value.ToString().Trim().Contains(row[6].Trim()))
alreadyExists = true;
// After retrieving all required study information, add them to the study Grid
if (!alreadyExists)
studiesDataGridView.Rows.Add(row);
// Add series rows for all studies in the DICOMDIR
AddSeriesRows(dicomdirDataSet, studyElement, row[6], dicomdirFileName);
// Go to the next study element
studyElement = dicomdirDataSet.GetNextKey(studyElement, true);
studyElement = dicomdirDataSet.GetChildElement(studyElement, true);
}
// Go to the next patient element
// Next Patient Name
patientNameInformation = dicomdirDataSet.FindNextElement(patientNameInformation, false);
if (patientNameInformation != null)
patientName = dicomdirDataSet.GetConvertValue(patientNameInformation);
// Next Patient ID
patientIdInformation = dicomdirDataSet.FindNextElement(patientIdInformation, false);
if (patientIdInformation != null)
patientId = dicomdirDataSet.GetConvertValue(patientIdInformation);
patientElement = dicomdirDataSet.GetNextKey(patientElement, true);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Adding Study", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
Add a new function named AddSeriesRows(DicomDataSet dicomdirDataSet, DicomElement studyElement, string studyID, string dicomDirFileName)
and call it inside the AddStudyRows
method, as shown above. Add the code below to add and display the associated series from the DICOM file-set, for each study loaded.
private void AddSeriesRows(DicomDataSet dicomdirDataSet, DicomElement studyElement, string studyID, string dicomDirFileName)
{
try
{
int i;
bool hasImage = false;
Object[] row = new Object[9];
// All the information that will be extracted from the series element.
// This method will extract these tags and 2 others (studyID and frame count).
long[] tags =
{
DicomTag.Modality,
DicomTag.SeriesDate,
DicomTag.SeriesNumber,
DicomTag.OrganExposed,
DicomTag.SeriesDescription,
DicomTag.NumberOfSeriesRelatedInstances,
DicomTag.SeriesInstanceUID
};
DicomElement seriesDicomElement;
DicomElement seriesInformation;
seriesDicomElement = dicomdirDataSet.GetChildKey(studyElement);
seriesDicomElement = dicomdirDataSet.GetChildElement(seriesDicomElement, true);
while (seriesDicomElement != null)
{
for (i = 0; i < tags.Length; i++)
{
seriesInformation = dicomdirDataSet.FindFirstElement(seriesDicomElement, tags[i], true);
if (seriesInformation != null)
row[i] = dicomdirDataSet.GetConvertValue(seriesInformation);
}
row[i] = studyID; // row[8]
// Get Frame Count
DicomElement seriesElement;
int frameCount = 0;
seriesElement = dicomdirDataSet.GetChildKey(seriesDicomElement);
while (seriesElement != null)
{
frameCount++;
seriesElement = dicomdirDataSet.GetNextKey(seriesElement, true);
}
row[i + 1] = frameCount; // row[9]
// Check to see if series contains images
DicomElement imageElement;
int index = 0;
int thumbnailIndex = frameCount / 2;
imageElement = dicomdirDataSet.GetChildKey(seriesDicomElement);
imageElement = dicomdirDataSet.GetChildElement(imageElement, true);
while (imageElement != null)
{
if (index >= thumbnailIndex)
{
imageElement = dicomdirDataSet.FindFirstElement(imageElement, DicomTag.ReferencedFileID, true);
int position = dicomDirFileName.LastIndexOf("\\");
string directoryPath = dicomDirFileName.Remove(position);
string fileName = directoryPath + "\\" + dicomdirDataSet.GetConvertValue(imageElement);
DicomDataSet imageDataSet = new DicomDataSet();
imageDataSet.Load(fileName, DicomDataSetLoadFlags.None);
int imageCount = imageDataSet.GetImageCount(null);
if (imageCount != 0)
hasImage = true;
}
index++;
imageElement = dicomdirDataSet.GetNextKey(imageElement, true);
imageElement = dicomdirDataSet.GetChildElement(imageElement, true);
}
bool alreadyExists = false;
foreach (DataGridViewRow seriesRow in seriesDataGridView.Rows)
if (seriesRow.Cells[6].Value.ToString().Trim().Contains((string)row[6]))
alreadyExists = true;
// After retrieving all required series information, add them to the series Grid
if (hasImage && !alreadyExists)
seriesDataGridView.Rows.Add(row);
// Get to the next series element.
seriesDicomElement = dicomdirDataSet.GetNextKey(seriesDicomElement, true);
seriesDicomElement = dicomdirDataSet.GetChildElement(seriesDicomElement, true);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Adding Series", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
Add a new method named ViewSeriesRowsForThisStudy(int studyRowIndex)
, which will be called inside the btnAddDICOMDIR_Click
event handler as shown above. Add the code below to make it so that only the series associated with the selected study is displayed in the dialog.
private void ViewSeriesRowsForThisStudy(int studyRowIndex)
{
string studyInstanceUID = studiesDataGridView.Rows[studyRowIndex].Cells[studyInstanceUIDColumn.Name].Value.ToString();
bool firstVisible = false;
foreach (DataGridViewRow row in seriesDataGridView.Rows)
{
// Find first series row matching the study instance UID
if (row.Cells[7].Value.ToString().Trim().Contains(studyInstanceUID.Trim()))
{
row.Visible = true;
if (!firstVisible)
{
seriesDataGridView.CurrentCell = row.Cells[0];
firstVisible = true;
btnLoad.Enabled = true;
}
}
else
{
row.Visible = false;
if (!firstVisible)
btnLoad.Enabled = false;
}
}
}
Select the studiesDataGridView and add an handler(double-click SelectionChanged) for the SelectionChanged event so that when a different study is selected, the displayed series is updated accordingly. Add the code below.
private void studiesDataGridView_SelectionChanged(object sender, EventArgs e)
{
if (studiesDataGridView.SelectedRows.Count != 0)
{
string studyInstanceUID = studiesDataGridView.SelectedRows[0].Cells[studyInstanceUIDColumn.Name].Value.ToString();
bool firstVisible = false;
btnLoad.Enabled = false;
foreach (DataGridViewRow seriesRow in seriesDataGridView.Rows)
{
if (seriesRow.Cells[7].Value.ToString().Trim().Contains(studyInstanceUID.Trim()))
{
seriesRow.Visible = true;
if (!firstVisible)
{
seriesDataGridView.CurrentCell = seriesRow.Cells[0];
btnLoad.Enabled = true;
}
}
else
seriesRow.Visible = false;
}
}
}
Double click on the Clear button to add the click handler and add the code below to it, to remove all loaded studies and series from the dialog.
private void btnClear_Click(object sender, EventArgs e)
{
studiesDataGridView.Rows.Clear();
seriesDataGridView.Rows.Clear();
btnLoad.Visible = false;
btnClear.Visible = false;
}
Double-click on the Load button to add the click handler.
Loading the selected series involves the following:
MedicalViewerSeriesManager
class to sort the frames into the correct order using image position.The MedicalViewerSeriesManager
sorts the frames and separates them into sorted Stacks and separate Localizers. Stacks contain frame images sorted based on their locations and help in the creation of 3D objects. Localizers are separate images that have a different angle than the angle of the slices used to create the 3D object.
Add the code below to the btnLoad_Click
event handler to load the selected series from DICOM file-set.
private void btnLoad_Click(object sender, EventArgs e)
{
// Get information from the selected study data-grid row
DataGridViewRow studyRow = studiesDataGridView.SelectedRows[0];
string selectedStudyInstanceUID = studyRow.Cells[StudyInstanceUIDColumn.Name].Value.ToString();
string dicomDirFileName = studyRow.Cells[DICOMDIRFileNameColumn.Name].Value.ToString();
// Get information from the selected series data grid row
DataGridViewRow seriesRow = seriesDataGridView.SelectedRows[0];
string seriesInstanceUID = seriesRow.Cells[SeriesIDColumn.Name].Value.ToString();
int frameCount = Convert.ToInt32(seriesRow.Cells[FrameCountColumn.Name].Value);
// Load DICOMDIR dataset
DicomDataSet dicomdirDataSet = new DicomDataSet();
dicomdirDataSet.Load(dicomDirFileName, DicomDataSetLoadFlags.None);
// Used to iterate through DICOM elements
DicomElement element = null;
// Get the selected study element
DicomElement studyElement = null;
DicomElement patientElement = dicomdirDataSet.GetFirstKey(null, true);
DicomElement studyInstanceUIDElement = null;
while (patientElement != null)
{
element = dicomdirDataSet.GetChildKey(patientElement);
element = dicomdirDataSet.GetChildElement(element, true);
while (element != null)
{
studyInstanceUIDElement = dicomdirDataSet.FindFirstElement(element, DicomTag.StudyInstanceUID, true);
if (studyInstanceUIDElement != null)
{
string studyUID = dicomdirDataSet.GetConvertValue(studyInstanceUIDElement);
if (studyUID == selectedStudyInstanceUID)
{
studyElement = studyInstanceUIDElement;
break;
}
}
element = dicomdirDataSet.GetNextKey(element, true);
element = dicomdirDataSet.GetChildElement(element, true);
}
patientElement = dicomdirDataSet.GetNextKey(patientElement, true);
}
// Get the selected series element
DicomElement seriesElement = null;
DicomElement seriesInformationElement = null;
element = dicomdirDataSet.GetChildKey(studyElement);
element = dicomdirDataSet.GetChildElement(element, true);
while (element != null)
{
seriesInformationElement = dicomdirDataSet.FindFirstElement(element, DicomTag.SeriesInstanceUID, true);
if (seriesInformationElement != null)
{
string seriesID = dicomdirDataSet.GetConvertValue(seriesInformationElement);
if (seriesID == seriesInstanceUID)
{
seriesElement = seriesInformationElement;
break;
}
}
element = dicomdirDataSet.GetNextKey(element, true);
element = dicomdirDataSet.GetChildElement(element, true);
}
// Get the path to the location of the series frames in the DICOMDIR
int position = dicomDirFileName.LastIndexOf("\\");
string seriesPath = dicomDirFileName.Remove(position);
// Get path to first image frame in the series
string imagePath = "";
DicomElement imageIDElement = null;
element = dicomdirDataSet.GetChildKey(seriesElement);
element = dicomdirDataSet.GetChildElement(element, true);
while (element != null)
{
imageIDElement = dicomdirDataSet.FindFirstElement(element, DicomTag.ReferencedFileID, true);
if (imageIDElement != null)
{
imagePath = seriesPath + "\\" + dicomdirDataSet.GetConvertValue(imageIDElement);
break;
}
}
// Load the information from each frame image and store in imageData
DicomDataSet imageDataSet;
double[] doubleArray = new double[3];
string _referenceUID = "";
MedicalViewerImageData imageData = new MedicalViewerImageData();
List<MedicalViewerImageData> imageDataList = new List<MedicalViewerImageData>();
string[] userDataTags = new string[6];
for (int imageIndex = 0; imageIndex < frameCount; imageIndex++)
{
// Load image dataset
imageDataSet = new DicomDataSet();
imageDataSet.Load(imagePath, DicomDataSetLoadFlags.None);
// Get User Data Tags
if (imageIndex == 0)
{
userDataTags[0] = GetDicomTag(imageDataSet, DicomTag.InstitutionName);
userDataTags[1] = GetDicomTag(imageDataSet, DicomTag.PatientName);
userDataTags[2] = GetDicomTag(imageDataSet, DicomTag.PatientAge);
userDataTags[3] = GetDicomTag(imageDataSet, DicomTag.PatientBirthDate);
userDataTags[4] = GetDicomTag(imageDataSet, DicomTag.PatientSex);
userDataTags[5] = GetDicomTag(imageDataSet, DicomTag.PatientID);
}
// Create new imageData instance for each frame
imageData = new MedicalViewerImageData();
// Image
DicomGetImageFlags dicomGetImageFlags = DicomGetImageFlags.AutoDetectInvalidRleCompression | DicomGetImageFlags.AutoApplyModalityLut | DicomGetImageFlags.AutoApplyVoiLut | DicomGetImageFlags.AutoScaleModalityLut | DicomGetImageFlags.AutoScaleVoiLut;
imageData.Image = imageDataSet.GetImage(null, 0, 0, RasterByteOrder.Gray, dicomGetImageFlags);
// Frame of Reference UID
patientElement = imageDataSet.FindFirstElement(null, DicomTag.FrameOfReferenceUID, true);
if (patientElement != null)
imageData.FrameOfReferenceUID = imageDataSet.GetConvertValue(patientElement);
else
imageData.FrameOfReferenceUID = "";
// Skip if the reference ID is not the same for first frame
if (imageIndex == 0)
_referenceUID = imageData.FrameOfReferenceUID;
else if (_referenceUID != imageData.FrameOfReferenceUID)
continue;
// Echo Number
int echoNumber = -1;
DicomElement echoElement = imageDataSet.FindFirstElement(null, DicomTag.EchoNumber, true);
if (echoElement != null)
{
string patientID = String.Empty;
patientID = imageDataSet.GetConvertValue(echoElement);
echoNumber = Convert.ToInt32(patientID);
}
imageData.EchoNumber = echoNumber;
// Sequence Name
patientElement = imageDataSet.FindFirstElement(null, DicomTag.SequenceName, true);
if (patientElement != null)
{
imageData.SequenceName = imageDataSet.GetConvertValue(patientElement);
}
// Instance Number
patientElement = imageDataSet.FindFirstElement(null, DicomTag.InstanceNumber, true);
if (patientElement != null)
{
int[] integerArray;
integerArray = imageDataSet.GetIntValue(patientElement, 0, 1);
if (integerArray.Length != 0)
imageData.InstanceNumber = integerArray[0];
}
// Image Position
patientElement = imageDataSet.FindFirstElement(null, DicomTag.ImagePositionPatient, true);
doubleArray = imageDataSet.GetDoubleValue(patientElement, 0, 3);
if (doubleArray.Length == 0)
doubleArray = new double[3];
imageData.ImagePosition = Point3D.FromDoubleArray(doubleArray);
// Image Orientation
patientElement = imageDataSet.FindFirstElement(null, DicomTag.ImageOrientationPatient, true);
if (patientElement != null)
imageData.ImageOrientation = imageDataSet.GetConvertValue(patientElement);
// Pixel Spacing
patientElement = imageDataSet.FindFirstElement(null, DicomTag.PixelSpacing, true);
doubleArray = imageDataSet.GetDoubleValue(patientElement, 0, 2);
if (doubleArray.Length != 0)
imageData.PixelSpacing = new Point2D((float)doubleArray[0], (float)doubleArray[1]);
else
imageData.PixelSpacing = new Point2D(1, 1);
// Add the to the list of image data to be loaded
imageDataList.Add(imageData);
// Get path to the next frame
DicomElement nextImageElement = null;
element = dicomdirDataSet.GetNextKey(element, true);
element = dicomdirDataSet.GetChildElement(element, true);
while (element != null)
{
nextImageElement = dicomdirDataSet.FindFirstElement(element, DicomTag.ReferencedFileID, true);
if (element != null)
{
imagePath = seriesPath + "\\" + dicomdirDataSet.GetConvertValue(nextImageElement);
break;
}
}
if (element == null)
imagePath = "";
}
// Sort the images based on their position
MedicalViewerSeriesManager seriesManager = new MedicalViewerSeriesManager();
seriesManager.Sort(imageDataList);
// 3D stacks
for (int stackIndex = 0; stackIndex < seriesManager.Stacks.Count; stackIndex++)
{
int count = seriesManager.Stacks[stackIndex].Items.Count;
for (int frameIndex = 0; frameIndex < count; frameIndex++)
{
imageData = seriesManager.Stacks[0].Items[frameIndex];
SendFrameLoaded(
imageData.Image,
frameIndex,
count,
imageData.PixelSpacing,
imageData.ImagePosition,
imageData.ImageOrientationArray,
userDataTags);
}
}
// Localizers
foreach (MedicalViewerSeriesLocalizer localizer in seriesManager.Localizers)
{
imageData = localizer.LocalizerData;
SendFrameLoaded(
imageData.Image,
0,
1,
imageData.PixelSpacing,
imageData.ImagePosition,
imageData.ImageOrientationArray,
userDataTags);
}
this.DialogResult = DialogResult.OK;
}
string GetDicomTag(DicomDataSet ds, long tag)
{
DicomElement patientElement = ds.FindFirstElement(null, tag, true);
if (patientElement != null)
return ds.GetConvertValue(patientElement);
return null;
}
Add a FrameLoadedEventArgs
class, inside the SeriesBrowser
class, to use for sending the extracted sorted information for each frame to the main form.
public class FrameLoadedEventArgs : EventArgs
{
private RasterImage _image;
private int _frameIndex;
private int _pageCount;
private Point2D _pixelSpacing;
private Point3D _imagePosition;
private float[] _imageOrientation;
private string[] _userDataTags;
public FrameLoadedEventArgs(RasterImage image, int frameIndex, int pageCount, Point2D pixelSpacing, Point3D imagePosition, float[] imageOrientation, string[] userDataTags)
{
_image = image;
_frameIndex = frameIndex;
_pageCount = pageCount;
_pixelSpacing = pixelSpacing;
_imagePosition = imagePosition;
_imageOrientation = imageOrientation;
_userDataTags = userDataTags;
}
public RasterImage Image { get { return _image; } }
public int FrameIndex { get { return _frameIndex; } }
public int PageCount { get { return _pageCount; } }
public Point2D PixelSpacing { get { return _pixelSpacing; } }
public Point3D ImagePosition { get { return _imagePosition; } }
public float[] ImageOrientation { get { return _imageOrientation; } }
public string[] UserDataTags { get { return _userDataTags; } }
}
public delegate void FrameLoadedEventHandler(object sender, FrameLoadedEventArgs e);
public event FrameLoadedEventHandler FrameLoaded;
void SendFrameLoaded(RasterImage image, int frameIndex, int pageCount, Point2D pixelSpacing, Point3D imagePosition, float[] imageOrientation, string[] userDataTags)
{
// If the event handler is not registered, then exit
if (FrameLoaded == null)
return;
FrameLoadedEventArgs args = new FrameLoadedEventArgs(image, frameIndex, pageCount, pixelSpacing, imagePosition, imageOrientation, userDataTags);
FrameLoaded(this, args);
}
View the code for Form1
and add the following to the using block at the top.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using Leadtools;
using Leadtools.Dicom;
using Leadtools.Medical3D;
using Leadtools.MedicalViewer;
using static <Namespace>.SeriesBrowser; // Replace with Namespace
Add the following to the global variables:
private MedicalViewer _medicalViewer;
private MedicalViewerMultiCell _cell;
private Medical3DControl _control3D;
private List<Medical3DControl> _controls3D = new List<Medical3DControl>();
private SeriesBrowser seriesBrowserDialog;
In the Solution Explorer, open the designer for Form1
and add a new menu item to the File menu strip with the text Load &DICOMDIR. Double-click on this menu item and use the following code for the handler.
private void loadDICOMDIRToolStripMenuItem_Click(object sender, EventArgs e)
{
// Clear existing cells and 3D controls
_control3D = null;
if (_medicalViewer.Cells.Count > 0)
_medicalViewer.Cells.Clear();
// Clear check-marks from cell type menu
foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems)
toolStripMenuItem.Checked = false;
// Reset viewer Columns and Rows
_medicalViewer.Columns = 1;
_medicalViewer.Rows = 1;
if (seriesBrowserDialog == null)
{
seriesBrowserDialog = new SeriesBrowser();
seriesBrowserDialog.FrameLoaded += new FrameLoadedEventHandler(seriesBrowserDialog_FrameLoaded);
}
seriesBrowserDialog.ShowDialog();
}
Add the following code to the FrameLoadedEventHandler
handle, created above, which will fire for each passed frame from the dialog and create the multi-cell and 3D object used for each passed frame.
private RasterImage _image;
void seriesBrowserDialog_FrameLoaded(object sender, FrameLoadedEventArgs e)
{
try
{
if (e.FrameIndex == 0)
{
_image = e.Image.Clone();
Initialize3DControl(e.Image.Width, e.Image.Height, e.PageCount);
}
else
_image.AddPage(e.Image.Clone());
// Add image pages to the 3D control frame
if (_control3D != null)
_control3D.ObjectsContainer.Objects[0].MemoryEfficientSetFrame(e.Image.Clone(), e.FrameIndex, e.ImagePosition, true);
if (e.FrameIndex + 1 == e.PageCount) // Finished loading last frame
{
InitializeMultiCell(_image);
if (_cell != null)
{
// Set Tags
SetTags(e.UserDataTags);
// Add column if localizer cell added previously
if (_medicalViewer.Cells.Count > 0)
_medicalViewer.Columns = _medicalViewer.Columns + 1;
// Clear existing cells and add initialized cell to the viewer
_medicalViewer.Cells.Add(_cell);
if (_control3D != null)
{
// Set tags
Set3DTags();
// Finish
_control3D.ObjectsContainer.Objects[0].MemoryEfficientEnd(e.ImageOrientation, e.PixelSpacing);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Because DICOM file-sets may contain multiple stacks or localizers, modify the code to the below code to support loading multiple cells and 3D controls.
Add two List collections, _cells
and _controls3D
, to the global variables.
private MedicalViewer _medicalViewer;
private MedicalViewerMultiCell _cell;
private Medical3DControl _control3D;
private SeriesBrowser seriesBrowserDialog;
private List<MedicalViewerMultiCell> _cells = new List<MedicalViewerMultiCell>();
private List<Medical3DControl> _controls3D = new List<Medical3DControl>();
In the loadDICOMToolStripMenuItem_Click
and seriesBrowserDialog_FrameLoaded
handlers, modify the code to add the created cell to the _cells
collection. In addition, add the created 3D control to the _controls3D
collection after it is finished being set.
// Add initialized cell
_medicalViewer.Cells.Add(_cell);
_cells.Add(_cell);
// Finish
_control3D.ObjectsContainer.Objects[0].MemoryEfficientEnd(_cell.ImageOrientation, _cell.PixelSpacing);
_controls3D.Add(_control3D);
Use the _cells
and _controls3D
collections in the cellTypeToolStripMenuItem_DropDownOpening
, _menuVolumeVRT_Click
, and _menu2DCell_Click
event handlers when selecting the cell type from the menu strip to display multiple loaded cells or 3D controls.
private void cellTypeToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
{
bool Allow3D = false;
bool Allow2D = false;
if (_cells.Count > 0)
Allow2D = true;
if (_controls3D.Count > 0)
Allow3D = true;
_menuVolumeVRT.Enabled = Allow3D;
_menu2DCell.Enabled = Allow2D;
}
private void _menuVolumeVRT_Click(object sender, EventArgs e)
{
foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems)
toolStripMenuItem.Checked = false;
_menuVolumeVRT.Checked = true;
if (_medicalViewer.Cells[0] != _controls3D[0])
{
_medicalViewer.Cells.Clear();
foreach (Medical3DControl control3D in _controls3D)
{
if(_controls3D.Count > _medicalViewer.Rows * _medicalViewer.Columns)
if(_medicalViewer.Rows == _medicalViewer.Columns)
_medicalViewer.Rows++;
else
_medicalViewer.Columns++;
_medicalViewer.Cells.Add(control3D);
}
}
}
private void _menu2DCell_Click(object sender, EventArgs e)
{
foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems)
toolStripMenuItem.Checked = false;
_menu2DCell.Checked = true;
if (_medicalViewer.Cells[0] != _cells[0])
{
_medicalViewer.Cells.Clear();
foreach (MedicalViewerMultiCell cell in _cells)
{
if (_cells.Count > _medicalViewer.Rows * _medicalViewer.Columns)
if (_medicalViewer.Rows == _medicalViewer.Columns)
_medicalViewer.Rows++;
else
_medicalViewer.Columns++;
_medicalViewer.Cells.Add(cell);
}
}
}
In the loadDICOMDIRToolStripMenuItem_Click
and seriesBrowserDialog_FrameLoaded
handlers, clear the collections before continuing with loading the image.
Finally, reset the MedicalViewer
Rows and Columns to 1 when loading a new image.
// loadDICOMDIRToolStripMenuItem_Click and seriesBrowserDialog_FrameLoaded
{
// Clear existing cells and 3D controls
if (_controls3D.Count > 0)
{
_controls3D.Clear();
_control3D = null;
}
if (_medicalViewer.Cells.Count > 0)
_medicalViewer.Cells.Clear();
// Clear check-marks from cell type menu
foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems)
toolStripMenuItem.Checked = false;
// Reset viewer Columns and Rows
_medicalViewer.Columns = 1;
_medicalViewer.Rows = 1;
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps were followed correctly, the application runs and displays a series browser which loads DICOM medical studies and series from DICOM file-sets containing a DICOMDIR file.
Sample DICOM file-sets containing 3D studies and series can be found here: https://download.leadtools.com/images/3d/
This tutorial showed how to add a series browser dialog that displays the studies and series in a DICOM file-set using the DICOMDIR file. In addition, the dialog loads and passes the frame images that were sorted by the MedicalViewerSeriesManager
class to the medical viewer to be displayed.