Common issues and solutions when working with DICOMKit.
Problem: DICOMError.invalidFile when reading a file.
Causes and Solutions:
-
Missing DICM prefix: Some legacy files don't have the standard header.
// Use force mode for legacy files let file = try DICOMFile.read(from: data, force: true)
-
Corrupted file: Verify the file integrity.
// Check file size and magic bytes print("File size: \(data.count)") if data.count > 132 { let prefix = String(data: data[128..<132], encoding: .ascii) print("Prefix: \(prefix ?? "none")") // Should be "DICM" }
-
Not a DICOM file: Verify file format.
- Check file extension
- Verify source system exports valid DICOM
Problem: File uses a compression format not supported.
Solutions:
-
Check which transfer syntax is used:
if let tsUID = file.fileMetaInformation[.transferSyntaxUID]?.stringValue { print("Transfer Syntax: \(tsUID)") }
-
Supported transfer syntaxes:
1.2.840.10008.1.2- Implicit VR Little Endian1.2.840.10008.1.2.1- Explicit VR Little Endian1.2.840.10008.1.2.2- Explicit VR Big Endian1.2.840.10008.1.2.4.50- JPEG Baseline1.2.840.10008.1.2.4.70- JPEG Lossless1.2.840.10008.1.2.4.90/91- JPEG 20001.2.840.10008.1.2.5- RLE Lossless
-
Convert the file using external tools:
# Using dcmtk dcmconv --write-xfer-little input.dcm output.dcm
Problem: App crashes or runs out of memory with large files.
Solutions:
-
Use metadata-only parsing:
let options = ParsingOptions(mode: .metadataOnly) let file = try DICOMFile.read(from: data, options: options)
-
Use lazy pixel data loading:
let options = ParsingOptions(mode: .lazyPixelData) let file = try DICOMFile.read(from: data, options: options)
-
Use memory-mapped files:
let dataSource = try MemoryMappedDataSource(url: fileURL) // Use dataSource with parsing options
Problem: Rendered image appears completely black or white.
Causes and Solutions:
-
Wrong window/level settings:
// Get values from the file first let windowCenter = file.dataSet.windowCenter ?? 0 let windowWidth = file.dataSet.windowWidth ?? 1000 // Use these values for rendering let renderer = PixelDataRenderer( pixelData: pixelData, windowCenter: windowCenter, windowWidth: windowWidth )
-
MONOCHROME1 interpretation (inverted):
// DICOMKit handles this automatically, but verify: print("Photometric: \(pixelData.photometricInterpretation)")
-
16-bit data with wrong bit interpretation:
print("Bits Allocated: \(pixelData.bitsAllocated)") print("Bits Stored: \(pixelData.bitsStored)") print("High Bit: \(pixelData.highBit)")
Problem: Color images appear wrong (shifted colors, inverted).
Solutions:
-
Check photometric interpretation:
let photometric = pixelData.photometricInterpretation print("Photometric: \(photometric)") // Expected: RGB, YBR_FULL, YBR_FULL_422, PALETTE COLOR
-
For palette color images:
// Ensure LUT data is present if let redLUT = file.dataSet[.redPaletteColorLookupTableData] { print("Red LUT present") }
Problem: Rendered image quality is poor.
Solutions:
-
Check image dimensions:
print("Size: \(pixelData.columns) x \(pixelData.rows)")
-
Disable scaling interpolation:
// In SwiftUI Image(cgImage, scale: 1.0, label: Text("")) .interpolation(.none)
-
Use appropriate display size:
// Scale to fit while maintaining aspect ratio .aspectRatio(contentMode: .fit)
Problem: Cannot connect to PACS server.
Debugging:
// Enable logging
DICOMLogger.shared.logLevel = .debug
// Test with verification first
let verificationService = VerificationService(client: client)
let success = try await verificationService.verify()
print("Verification: \(success)")Common causes:
- Wrong port: Verify DICOM port (usually 104 or 11112)
- Firewall blocking: Ensure port is open
- Wrong AE Title: Verify both calling and called AE titles
- TLS mismatch: Check if server requires TLS
Problem: Server rejects the association.
Debugging:
do {
try await client.associate()
} catch let error as DICOMNetworkError {
switch error {
case .associationRejected(let result, let source, let reason):
print("Rejected by: \(source)")
print("Reason: \(reason)")
default:
print("Error: \(error)")
}
}Common causes:
- Unknown AE Title: Register your AE with the PACS
- IP not allowed: Add your IP to PACS allowed list
- SOP Class not supported: Check server capabilities
Problem: C-FIND returns empty results.
Solutions:
-
Simplify the query:
// Start with minimal criteria let query = QueryKeys.study() // Then add filters incrementally
-
Check query keys format:
// Use wildcards for partial matches let query = QueryKeys.study(patientName: "SMITH*")
-
Verify data exists on PACS using vendor tools
Problem: DICOMweb server rejects requests.
Solutions:
-
Configure OAuth2:
let oauth2 = OAuth2Client(configuration: ...) let client = DICOMwebClient( configuration: config, oauth2Client: oauth2 )
-
Check token expiration:
if oauth2.isTokenExpired { try await oauth2.refreshToken() }
Problem: Resources not found.
Solutions:
-
Verify URL configuration:
let config = DICOMwebConfiguration( baseURL: URL(string: "https://server/dicom-web")!, wadoRoot: "/wado-rs", // Verify these paths qidoRoot: "/qido-rs", stowRoot: "/stow-rs" )
-
Check UIDs are correct:
print("Study UID: \(studyUID)") // Ensure no extra spaces or encoding issues
Problem: WADO-RS retrieval is slow.
Solutions:
-
Enable caching:
let cache = InMemoryCache(maxSize: 500 * 1024 * 1024) let client = DICOMwebClient( configuration: config, cache: cache )
-
Request rendered frames for preview:
// Get JPEG instead of full DICOM let jpeg = try await client.retrieveRenderedFrame( studyUID: study, seriesUID: series, instanceUID: instance, frameNumber: 1, mediaType: .jpeg )
Problem: Patient names or descriptions show incorrect characters.
Solutions:
-
Check Specific Character Set:
if let charset = file.dataSet[.specificCharacterSet]?.stringValue { print("Character Set: \(charset)") }
-
DICOMKit supports common character sets:
- ISO IR 100 (Latin-1)
- ISO IR 192 (UTF-8)
- Japanese, Korean, Chinese
- Arabic, Hebrew, Greek, Cyrillic
-
If charset is missing:
// Try force-reading as UTF-8 let options = ParsingOptions(defaultCharacterSet: .utf8)
Problem: File parsing takes too long.
Solutions:
-
Use partial parsing:
var options = ParsingOptions(mode: .metadataOnly) options.stopAfterTag = .seriesInstanceUID
-
Enable SIMD processing:
// SIMD is automatically used on Apple platforms // Verify Accelerate framework is available #if canImport(Accelerate) print("SIMD acceleration available") #endif
-
Profile with Instruments to find bottlenecks
Problem: App uses too much memory.
Solutions:
-
Configure image cache:
// Use low memory configuration let cache = ImageCache(configuration: .lowMemory)
-
Clear cache periodically:
ImageCache.shared.clear()
-
Use memory mapping for files >100MB
Enable detailed logging for debugging:
// Enable DICOM network logging
DICOMLogger.shared.logLevel = .debug
// Enable DICOMweb logging
DICOMwebLogger.shared.logLevel = .verboseIf you can't resolve an issue:
- Check the GitHub Issues
- Create a minimal reproduction case
- Include relevant DICOM tag values (anonymized)
- Share error messages and stack traces