Setting up a proper test environment is essential for DICOM development.
Open Source DICOM Servers
Orthanc
Orthanc is a lightweight, open-source DICOM server ideal for development and testing.
Website: https://www.orthanc-server.com/
Features:
- Built-in PACS functionality
- RESTful API
- DICOMweb support
- Plugin architecture
- Cross-platform
Installation:
# Docker (recommended)
docker run -p 4242:4242 -p 8042:8042 jodogne/orthanc
# Access web interface at http://localhost:8042
# Default credentials: orthanc/orthanc
Configuration (orthanc.json):
{
"Name": "MyOrthanc",
"DicomAet": "ORTHANC",
"DicomPort": 4242,
"HttpPort": 8042,
"RemoteAccessAllowed": true,
"AuthenticationEnabled": false,
"DicomModalities": {
"sample": [ "MY_SCU", "localhost", 11112 ]
},
"DicomWeb": {
"Enable": true,
"Root": "/dicom-web/",
"EnableWado": true,
"WadoRoot": "/wado",
"Ssl": false
}
}
C# Integration:
public class OrthancClient
{
private readonly HttpClient httpClient;
private readonly string baseUrl;
public OrthancClient(string url = "http://localhost:8042")
{
baseUrl = url;
httpClient = new HttpClient();
}
public async Task<string> UploadDicomFile(string filePath)
{
byte[] fileBytes = File.ReadAllBytes(filePath);
var content = new ByteArrayContent(fileBytes);
HttpResponseMessage response = await httpClient.PostAsync(
$"{baseUrl}/instances", content);
return await response.Content.ReadAsStringAsync();
}
public async Task<List<string>> GetStudies()
{
HttpResponseMessage response = await httpClient.GetAsync($"{baseUrl}/studies");
string json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<string>>(json);
}
public async Task<byte[]> DownloadStudy(string studyId)
{
HttpResponseMessage response = await httpClient.GetAsync(
$"{baseUrl}/studies/{studyId}/archive");
return await response.Content.ReadAsByteArrayAsync();
}
}
dcm4che / dcm4chee-arc
Website: https://www.dcm4che.org/
Features:
- Java-based DICOM toolkit
- Enterprise PACS solution
- IHE integration profiles
- Extensive DICOM support
Installation:
# Docker
docker run -p 8080:8080 -p 11112:11112 dcm4che/dcm4chee-arc-psql
# Access at http://localhost:8080/dcm4chee-arc/ui2
Microsoft DICOM Server
Website: https://github.com/microsoft/dicom-server
Features:
- Open-source .NET DICOM server
- DICOMweb support (QIDO, WADO, STOW)
- Azure integration
- FHIR integration
Installation:
# Docker
docker run -p 8080:8080 mcr.microsoft.com/healthcareapis/dicom-server:latest
DICOM Toolkit (DCMTK)
DCMTK provides command-line tools for DICOM operations.
Website: https://dicom.offis.de/dcmtk
Key Commands
dcmdump - Display DICOM file contents:
# Basic dump
dcmdump image.dcm
# Show specific tags
dcmdump +P 0010,0010 +P 0008,0060 image.dcm
# Output to file
dcmdump image.dcm > output.txt
storescu - Send DICOM files:
# Send single file
storescu -aec PACS_AE -aet MY_SCU localhost 104 image.dcm
# Send all files in directory
storescu -aec PACS_AE -aet MY_SCU localhost 104 *.dcm
# With verbose output
storescu -v -aec PACS_AE -aet MY_SCU localhost 104 image.dcm
storescp - Receive DICOM files:
# Start storage receiver
storescp -aet STORE_SCP 11112 -od /output/directory
# With verbose output
storescp -v -aet STORE_SCP 11112 -od /output/directory
findscu - Query DICOM servers:
# Study-level query
findscu -S -k 0008,0052="STUDY" -k 0010,0020="12345" \
-aec PACS_AE -aet MY_SCU localhost 104
# Date range query
findscu -S -k 0008,0052="STUDY" -k 0008,0020="20240101-20240131" \
-aec PACS_AE -aet MY_SCU localhost 104
movescu - Retrieve DICOM files:
# Move study to destination
movescu -S -k 0008,0052="STUDY" -k 0020,000D="1.2.3.4.5" \
-aec PACS_AE -aet MY_SCU -aem DEST_AE localhost 104
echoscu - Test connectivity:
# C-ECHO test
echoscu -aec PACS_AE -aet MY_SCU localhost 104
fo-dicom (.NET Library)
Website: https://github.com/fo-dicom/fo-dicom
Features:
- Open-source .NET DICOM library
- Cross-platform (.NET Standard 2.0+)
- Modern async/await patterns
- Extensive SOP class support
Installation:
dotnet add package fo-dicom
Example Usage:
using FellowOakDicom;
using FellowOakDicom.Network;
// Read DICOM file
var dicomFile = await DicomFile.OpenAsync("test.dcm");
var dataset = dicomFile.Dataset;
// Access data
string patientName = dataset.GetSingleValue<string>(DicomTag.PatientName);
Console.WriteLine($"Patient: {patientName}");
// Send C-STORE
var client = DicomClientFactory.Create("localhost", 104, false, "SCU", "SCP");
await client.AddRequestAsync(new DicomCStoreRequest("test.dcm"));
await client.SendAsync();
// Start SCP server
var server = DicomServerFactory.Create<DicomCStoreProvider>(11112);
Creating a Test Environment
public class DicomTestEnvironment
{
private DicomStorageServer storageServer;
private ModalityWorklistProvider worklistServer;
private string testStoragePath;
public void Setup()
{
testStoragePath = Path.Combine(Path.GetTempPath(), "TestDicomStorage");
Directory.CreateDirectory(testStoragePath);
// Start Storage SCP
storageServer = new DicomStorageServer("TEST_STORAGE", 11112, testStoragePath);
storageServer.Start();
// Start Worklist SCP
worklistServer = new ModalityWorklistProvider("TEST_MWL", 11113);
// Add sample worklist items
for (int i = 0; i < 5; i++)
{
worklistServer.AddWorklistItem(CreateSampleWorklistItem(i));
}
worklistServer.Start();
Console.WriteLine("Test environment started:");
Console.WriteLine($" Storage SCP: TEST_STORAGE @ localhost:11112");
Console.WriteLine($" Worklist SCP: TEST_MWL @ localhost:11113");
}
public void RunTests()
{
Console.WriteLine("\n=== Running Integration Tests ===\n");
// Test 1: C-ECHO
Console.WriteLine("Test 1: C-ECHO Verification");
var echoService = new DicomVerificationService();
bool echoSuccess = echoService.PerformEcho(
"TEST_SCU", "TEST_STORAGE", "localhost", 11112);
Console.WriteLine($"Result: {(echoSuccess ? "PASSED" : "FAILED")}\n");
// Test 2: C-STORE
Console.WriteLine("Test 2: C-STORE Storage");
string testFile = CreateTestDicomFile();
var storageService = new DicomStorageService();
bool storeSuccess = storageService.StoreImage(
"TEST_SCU", "TEST_STORAGE", "localhost", 11112, testFile);
Console.WriteLine($"Result: {(storeSuccess ? "PASSED" : "FAILED")}\n");
// Test 3: Modality Worklist Query
Console.WriteLine("Test 3: Modality Worklist Query");
var wlService = new ModalityWorklistService();
var items = wlService.QueryWorklist(
"TEST_SCU", "TEST_MWL", "localhost", 11113);
Console.WriteLine($"Result: Found {items.Count} items - " +
$"{(items.Count > 0 ? "PASSED" : "FAILED")}\n");
// Test 4: Verify stored files
Console.WriteLine("Test 4: Verify Stored Files");
var storedFiles = Directory.GetFiles(testStoragePath, "*.dcm",
SearchOption.AllDirectories);
Console.WriteLine($"Result: Found {storedFiles.Length} stored files - " +
$"{(storedFiles.Length > 0 ? "PASSED" : "FAILED")}\n");
Console.WriteLine("=== Tests Complete ===");
}
private string CreateTestDicomFile()
{
var creator = new DicomFileCreator();
var ds = creator.CreateCTImage("Test^Patient", "TEST001");
string testFile = Path.Combine(Path.GetTempPath(), "test_image.dcm");
creator.SaveDicomFile(ds, testFile);
return testFile;
}
private WorklistItem CreateSampleWorklistItem(int index)
{
return new WorklistItem
{
PatientName = $"Patient{index}^Test",
PatientID = $"TEST{index:D5}",
PatientBirthDate = "19800101",
PatientSex = "M",
AccessionNumber = $"ACC{DateTime.Now.Ticks}{index}",
RequestedProcedureID = $"RP{index:D3}",
RequestedProcedureDescription = "CT Chest",
StudyInstanceUID = DicomUID.Generate(),
Modality = "CT",
ScheduledStationAE = "CT_SCANNER_1",
ScheduledDate = DateTime.Now.ToString("yyyyMMdd"),
ScheduledTime = DateTime.Now.AddHours(index).ToString("HHmmss"),
ScheduledProcedureStepID = $"SPS{index:D3}",
ScheduledProcedureStepDescription = $"Test Procedure {index}",
ScheduledPerformingPhysician = "Dr. Test"
};
}
public void Cleanup()
{
storageServer?.Stop();
worklistServer?.Stop();
try
{
if (Directory.Exists(testStoragePath))
{
Directory.Delete(testStoragePath, true);
}
}
catch (Exception ex)
{
Console.WriteLine($"Cleanup warning: {ex.Message}");
}
Console.WriteLine("Test environment cleaned up");
}
}
Usage
var testEnv = new DicomTestEnvironment();
try
{
testEnv.Setup();
Console.WriteLine("Press Enter to run tests...");
Console.ReadLine();
testEnv.RunTests();
Console.WriteLine("Press Enter to cleanup...");
Console.ReadLine();
}
finally
{
testEnv.Cleanup();
}
Testing tools and proper test environments are essential for reliable DICOM development.
Related Articles
Learn more about DICOM testing and tools:
- DICOM Basics using Java - Orthanc DICOM Server for Testing - Java Orthanc setup
- DICOM Basics using .NET - Orthanc DICOM Server for Testing - .NET Orthanc setup
- DICOM Troubleshooting - Common issues and solutions
- DICOM Conformance Statements - Understanding conformance
General Testing:
- Unit Testing 101 For Non-Programmers - Testing fundamentals
- Why Testing is Important - Value of testing in healthcare IT