Testing Tools and Servers

Section 20 of 27
74% complete

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.

Learn more about DICOM testing and tools:

General Testing:

Quiz: Testing Tools and Servers

Question 1 of 4

What is Orthanc?