This tutorial shows how to configure the LEADTOOLS DICOM Writer filter to create a dataset with H.264 in MPEG-2 Transport in a C# .NET 6 application. This tutorial encodes HD video, through resizing one of the sample LEADTOOLS media files, to create a MPEG-4 AVC/H.264 BD-compatible DICOM file.
Overview | |
---|---|
Summary | This tutorial covers how to use the LEADTOOLS DICOM Writer filter in a C# .NET 6 Console application. |
Completion Time | 20 minutes |
Visual Studio Project | Download tutorial project (3 KB) |
Platform | C# .NET 6 Console Application |
IDE | Visual Studio 2022 |
Runtime Target | .NET 6 or Higher |
Development License | Download LEADTOOLS |
Get familiar with the basic steps of creating a project by reviewing the Add References and Set a License tutorial, before working on this tutorial.
Start with a copy of the project created in the Add References and Set a License tutorial. If you do not have that project, follow the steps in that tutorial to create it.
The references needed depend upon the purpose of the project. References can be added by the use of local DLL references. The following DLLs are needed.
The DLLs are located at <INSTALL_DIR>\LEADTOOLS23\Bin\net
:
Leadtools.dll
Leadtools.Codecs.dll
Leadtools.Core.dll
Leadtools.Dicom.dll
Leadtools.Dicom.Common.dll
Leadtools.Dicom.Tables.dll
Leadtools.Multimedia.dll
Interop.LMH264EncoderLib.dll
Interop.LMVResizeLib.dll
System.Windows.Forms.dll
The need DLL below is located at <INSTALL_DIR>\LEADTOOLS23\Bin\DotNet4\x64
:
Interop.LTDicWrtLib.dll
For a complete list of which DLL files are required for your application, refer to Files to be Included in 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:
With the project created, the references added, and the license set, coding can begin.
In the Solution Explorer, open Program.cs
. Add the following statements to the using block at the top of Program.cs
:
using Leadtools;
using Leadtools.Dicom;
using Leadtools.Dicom.Common.DataTypes;
using Leadtools.Dicom.Common.Extensions;
using Leadtools.Multimedia;
using LTDicWrtLib;
Be sure to add the following global variable:
static ConvertCtrl convert;
Inside the Program
class, add a new method named CreateDICOMFile()
. Call this method inside the Main()
method, as shown below. Also, be sure to indicate that the COM threading model for the application is Single-threaded Apartment (STA)
by adding [STAThread]
.
[STAThread]
static void Main(string[] args)
{
InitLEAD();
CreateDICOMFile();
}
Add the below code to the CreateDICOMFile()
method to encode HD video to create an MPEG-4 AVC/H.264 BD-compatible DICOM file. You can download the .DS
temporary file used for this tutorial here.
private static void CreateDICOMFile()
{
string templateFile = @"FILE PATH TO TEMP Video_Photography.DS FILE";
string inputFile = @"C:\LEADTOOLS23\Resources\Media\DaDa_H264.mp4";
string outputFile = @"FILE PATH TO OUTPUT THE DCM FILE TO";
// Create instance of Convert control.
convert = new ConvertCtrl();
// Set the source and target files
convert.SourceFile = inputFile;
convert.TargetFile = outputFile;
// Need to resize sample video to be HD since no HD videos are in the SDK setup
convert.SelectedVideoProcessors.Add(convert.VideoProcessors.Resize);
LMVResizeLib.LMVResize resizeFilter = convert.GetSubObject(ConvertObject.SelVideoProcessor + 0) as LMVResizeLib.LMVResize;
resizeFilter.ForceSquarePixelOutput = true;
resizeFilter.Height = 1080;
resizeFilter.Width = 1920;
resizeFilter.OutputAspectRatioMode = LMVResizeLib.OutputAspectRatioModeConstants.OUTPUTASPECTRATIO_16_9;
resizeFilter.HeightControlMode = LMVResizeLib.SizeControlModeConstants.SIZECONTROL_FIXED;
resizeFilter.WidthControlMode = LMVResizeLib.SizeControlModeConstants.SIZECONTROL_FIXED;
// Done configuring the resize filter
System.Runtime.InteropServices.Marshal.ReleaseComObject(resizeFilter);
resizeFilter = null;
convert.VideoCompressors.H264.Selected = true;
convert.AudioCompressors.AAC.Selected = true;
LMH264EncoderLib.LMH264Encoder h264Encoder = convert.GetSubObject(ConvertObject.VideoCompressor) as LMH264EncoderLib.LMH264Encoder;
h264Encoder.EnableRateControl = false;
h264Encoder.QualityFactor = 28;
h264Encoder.FrameRate = 29.970f;
h264Encoder.EncodingSpeed = LMH264EncoderLib.eH264ENCODINGSPEED.H264SPEED_4;
h264Encoder.EncodingThreads = LMH264EncoderLib.eH264ENCODINGTHREADS.H264THREAD_AUTO;
h264Encoder.IFrameInterval = 11;
h264Encoder.PFrameInterval = 1;
h264Encoder.OutputFormat = LMH264EncoderLib.eH264OUTPUTFORMAT.H264FORMAT_HIGH_H264;
// Done configuring the filter
System.Runtime.InteropServices.Marshal.ReleaseComObject(h264Encoder);
h264Encoder = null;
// set the format
convert.TargetFormat = TargetFormatType.MPEG2TransportDICOM;
// Get the Dicom writer filter and set some options
//NOTE: MPEG-2 Transport is the target filter here
// DICOM writer is the file writer/Sink filter
ILTDicWrt2 dicomWriter = (ILTDicWrt2)convert.GetSubObject(ConvertObject.Sink);
dicomWriter.DICOMTemplateFile = templateFile;
dicomWriter.MPEG2DicomCompatibilityOption = MPEG2DicomCompatibilityConstants.MPEG2DICOMCOMP_IGNORE;
// Done configuring the filter
System.Runtime.InteropServices.Marshal.ReleaseComObject(dicomWriter);
dicomWriter = null;
// Add some event handlers
convert.Progress += new ProgressEventHandler(convert_Progress);
convert.Complete += new EventHandler(convert_Complete);
Console.WriteLine(string.Format("Starting to Convert {0}", convert.TargetFile));
convert.StartConvert();
while (convert.State != ConvertState.Stopped)
{
System.Windows.Forms.Application.DoEvents();
}
DicomEngine.Startup();
DicomDataSet ds = new DicomDataSet();
ds.Load(convert.TargetFile, DicomDataSetLoadFlags.LoadAndClose);
PatientBase patientBase = ds.Get<PatientBase>();
patientBase.PatientName = new PersonName("Smith^John");
patientBase.PatientSex = PatientSex.Male;
patientBase.PatientID = "1234567890";
patientBase.PatientBirthDate = DateTime.Parse("09/18/1970");
patientBase.PatientBirthTime = DateTime.Parse(DateTime.Now.ToShortTimeString());
ds.Set(patientBase);
DicomElement element;
element = ds.FindFirstElement(null, DicomTag.SOPInstanceUID, true);
if (element != null)
ds.SetValue(element, Leadtools.DicomDemos.Utils.GenerateDicomUniqueIdentifier());
GenerateUidTag(ds, DicomTag.StudyInstanceUID);
GenerateUidTag(ds, DicomTag.SeriesInstanceUID);
GenerateUidTag(ds, DicomTag.SOPInstanceUID);
ds.Save(convert.TargetFile, DicomDataSetSaveFlags.None);
DicomEngine.Shutdown();
Console.WriteLine("Patient information updated and new UIDs generated.");
}
Inside the Program
class, add a new method named GenerateUidTag(DicomDataSet dicom, long UidTag)
. This method will be called inside the CreateDICOMFile()
method, as shown above, to create unique identifiers.
private static void GenerateUidTag(DicomDataSet dicom, long UidTag)
{
DicomElement element;
element = dicom.FindFirstElement(null, UidTag, true);
if (element != null)
dicom.SetValue(element, Leadtools.DicomDemos.Utils.GenerateDicomUniqueIdentifier());
}
Add two new event handlers inside the Program
class named convert_Complete(object sender, EventArgs e)
and convert_Progress(object sender, ProgressEventArgs e)
. These will be used inside the CreateDICOMFile()
method as shown above and will serve the purpose of printing out the application progress to the console. These are optional.
private static void convert_Complete(object sender, EventArgs e)
{
Console.WriteLine(string.Format("\nConversion of \"{0}\" complete!", convert.TargetFile));
}
private static void convert_Progress(object sender, ProgressEventArgs e)
{
Console.Write(string.Format("\rConversion progress: {0}%", e.percent));
}
Right-click on <PROJECT_NAME>.csproj
in the Solution Explorer and select Add -> New Item.... Select the Class option and name the file Utils.cs
. Change the namespace of the new class to namespace Leadtools.DicomDemos
.
Add the following statements to the using block at the top of Utils.cs
:
using System.Text;
using System.Diagnostics;
Inside the Utils
class, add the following variables.
private static string _prevTime;
private static string _leadRoot = null;
private static Object _lock = new object();
private static int _count = 0;
private const int _maxCount = int.MaxValue;
Add a new method to the Utils
class named GenerateDicomUniqueIdentifier()
. Add the code below to generate the UIDs.
public static string GenerateDicomUniqueIdentifier()
{
try
{
lock (_lock)
{
// yyyy four digit year
// MM month from 01 to 12
// dd 01 to 31
// HH hours using a 24-hour clock form 00 to 23
// mm minute 00 to 59
// ss second 00 to 59
// fffffff ten millionths of a second
const string dateFormatString = "yyyyMMdd.HHmmss.fffffff";
string sUidRet = "";
if (_leadRoot == null)
{
StringBuilder sb = new StringBuilder();
sb.Append("1.2.840.114257.1.1");
// Process Id
sb.AppendFormat(".{0}", (uint)Process.GetCurrentProcess().Id);
_leadRoot = sb.ToString();
_prevTime = DateTime.UtcNow.ToString(dateFormatString);
}
StringBuilder uid = new StringBuilder();
uid.Append(_leadRoot);
String time = DateTime.UtcNow.ToString(dateFormatString);
if (time.Equals(_prevTime))
{
if (_count == _maxCount)
throw new Exception("GenerateDicomUniqueIdentifier error -- max count reached.");
_count++;
}
else
{
_count = 1;
_prevTime = time;
}
uid.AppendFormat(".{0}.{1}", time, _count);
sUidRet = uid.ToString();
// This should not happen
if (sUidRet.Length > 64)
sUidRet = sUidRet.Substring(0, 64);
return sUidRet;
}
}
catch (Exception ex)
{
throw ex;
}
}
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps were followed correctly, the application runs and creates a dataset with H.264 in MPEG-2 Transport. You can find the expected output DCM and AVI files here.
This tutorial showed how to use the LEADTOOLS DICOM Writer filter to create a dataset with H.264 in MPEG-2 Transport. It also covered how to use the DicomElement
class, DicomDataSet
class, ConvertCtrl
class, and the ILTDicWrt2
interface.