Solutions for common issues when using the DICOM Decoder.
- Installation Issues
- File Loading Issues
- Image Issues
- Performance Issues
- Metadata Issues
- Common Errors
Cause: The package was not added correctly to the project.
Solution:
- Confirm the package is added in File -> Add Packages.
- Ensure
DicomCoreappears in Target Dependencies. - Clean the build folder (Product -> Clean Build Folder, Shift+Command+K).
- Reopen the project.
import DicomCore // Correct
import DICOM-Decoder // IncorrectMessage: "Requires minimum Swift 5.9"
Solution:
- Update Xcode to version 15.0 or later.
- Check Swift version:
swift --version
- Set the minimum Swift version in Build Settings.
Diagnostics:
do {
let decoder = try DCMDecoder(contentsOfFile: path)
// File loaded successfully
} catch {
print("Error: \(error)")
let tempDecoder = DCMDecoder()
let validation = tempDecoder.validateDICOMFile(path)
print("Valid: \(validation.isValid)")
print("Issues:")
for issue in validation.issues {
print(" - \(issue)")
}
}Cause: Invalid path or missing file.
Solutions:
// Use absolute paths
let decoder = try DCMDecoder(contentsOfFile: "/Users/name/Documents/file.dcm")
// Build from a bundle
if let path = Bundle.main.path(forResource: "example", ofType: "dcm") {
let decoder = try DCMDecoder(contentsOfFile: path)
}
// Using URL
let url = URL(fileURLWithPath: "/path/file.dcm")
let decoder = try DCMDecoder(contentsOf: url)Cause: The file is not valid DICOM or is corrupted.
Solutions:
- Check the file with a DICOM tool (e.g.,
dcmdump file.dcmif dcmtk is installed). - Open the file in a DICOM viewer to confirm it is valid.
- Verify the extension (
.dcm,.dicom,.ima). - Some DICOM files omit the standard prefix; consider a more permissive parser if needed.
Cause: File is under 132 bytes.
Solutions:
- Verify the file was fully downloaded or copied.
- Re-download if corruption is suspected.
- Check read permissions.
Message: No image loads and compressedImage = true.
Cause: The file uses a compressed transfer syntax (JPEG, JPEG2000, RLE).
Diagnostics:
let decoder = try DCMDecoder(contentsOfFile: path)
if decoder.compressedImage {
print("Compressed transfer syntax detected")
print("UID: \(decoder.info(for: .transferSyntaxUID))")
}Solutions:
Option 1: Convert to an uncompressed format
# Using dcmtk
dcmconv --write-xfer-little compressed.dcm uncompressed.dcmOption 2: Use built-in JPEG decoding (ImageIO)
let decoder = try DCMDecoder(contentsOfFile: path)
if let pixels = decoder.getPixels8() ?? decoder.getPixels16() {
print("JPEG decoding succeeded")
}Supported transfer syntaxes:
- Little Endian Implicit VR
- Little Endian Explicit VR
- Big Endian Explicit VR
- JPEG Lossless (Process 14, Selection Value 1) - native decoder
- JPEG Baseline (via ImageIO)
- JPEG 2000 (via ImageIO)
Not supported:
- JPEG Lossless processes other than Process 14, Selection Value 1
- RLE
Diagnostics:
let status = decoder.getValidationStatus()
print("Has pixels: \(status.hasPixels)")
print("Width: \(status.width), Height: \(status.height)")
print("Samples/Pixel: \(decoder.samplesPerPixel)")
print("Bit Depth: \(decoder.bitDepth)")Problem 1: Wrong pixel type requested
Cause: Requesting 16-bit pixels when the image is 8-bit or RGB.
Solution:
if decoder.samplesPerPixel == 1 {
if decoder.bitDepth == 8 {
let pixels = decoder.getPixels8()
} else if decoder.bitDepth == 16 {
let pixels = decoder.getPixels16()
}
} else if decoder.samplesPerPixel == 3 {
let pixels = decoder.getPixels24()
}
if decoder.isGrayscale {
let pixels = decoder.bitDepth == 16
? decoder.getPixels16()
: decoder.getPixels8()
}Problem 2: File has no pixel data
Cause: DICOMDIR or metadata-only file.
Solution:
if decoder.info(for: 0x00041220).isEmpty == false {
print("This is a DICOMDIR (index), it does not contain pixels")
}Cause: Incorrect window/level.
Diagnostics:
let window = decoder.windowSettings
print("Center: \(window.center), Width: \(window.width)")
if window.center == 0 && window.width == 0 {
print("No default window defined")
}Solutions:
- Calculate an optimal window automatically:
if let optimal = decoder.calculateOptimalWindow(),
let image = DCMWindowingProcessor.applyWindowLevel(
pixels16: pixels16!,
center: optimal.center,
width: optimal.width
) {
// Use image
}- Use an appropriate preset:
let preset = DCMWindowingProcessor.getPresetValues(preset: .lung)
let image = DCMWindowingProcessor.applyWindowLevel(
pixels16: pixels16!,
center: preset.center,
width: preset.width
)- Suggest presets automatically:
let modality = decoder.info(for: .modality)
let bodyPart = decoder.info(for: .bodyPartExamined)
let suggestions = DCMWindowingProcessor.suggestPresets(
for: modality,
bodyPart: bodyPart
)
if let first = suggestions.first {
let preset = DCMWindowingProcessor.getPresetValues(preset: first)
// Apply preset...
}Cause: MONOCHROME1 where 0 = white.
Solution: The decoder inverts this automatically. If reading raw pixels, invert manually:
let photoInterp = decoder.photometricInterpretation
if photoInterp == "MONOCHROME1" {
pixels = pixels.map { 65535 - $0 } // For 16-bit
}Cause: Files larger than 100MB can be slow.
Solutions:
- Use async/await to avoid blocking the UI:
Task {
let decoder = try await DCMDecoder(contentsOfFile: path)
}- Use thumbnails first:
if let thumb = decoder.getDownsampledPixels16(maxDimension: 150) {
showThumbnail(thumb.pixels, thumb.width, thumb.height)
}
Task {
if let pixels = await decoder.getPixels16Async() {
showFullImage(pixels)
}
}- Monitor performance:
let start = Date()
let decoder = try DCMDecoder(contentsOfFile: path)
let elapsed = Date().timeIntervalSince(start)
print("Load time: \(elapsed)s")Cause: Processing large pixel arrays.
Solution: Use automatic backend selection:
let windowed = DCMWindowingProcessor.applyWindowLevel(
pixels16: pixels16,
center: 40.0,
width: 80.0,
processingMode: .auto
)Diagnostics:
let value = decoder.info(for: .patientName)
if value.isEmpty {
let allTags = decoder.getAllTags()
print("Available tags: \(allTags.count)")
for (tag, value) in allTags where tag.hasPrefix("0010") {
print("\(tag): \(value)")
}
}Solutions:
- Check if the tag exists:
let all = decoder.getAllTags()
let tagHex = String(format: "%08X", 0x00100010)
if let value = all[tagHex] {
print("Tag exists: \(value)")
} else {
print("Tag not found in the file")
}- Use convenience methods with defaults:
let info = decoder.getPatientInfo()
let name = info["Name"] ?? "Unknown"Cause: DICOM uses specific formats (YYYYMMDD, HHMMSS).
Solution: Parse correctly.
let dateString = decoder.info(for: .studyDate) // "20240115"
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
let date = formatter.date(from: dateString)
let timeString = decoder.info(for: .studyTime) // "143025.123456"
formatter.dateFormat = "HHmmss.SSSSSS"
let time = formatter.date(from: timeString)Cause: Shared mutable decoder state, custom decoder implementations, or legacy code paths that bypass the public synchronized API.
Solution: Use the public DCMDecoder API, which synchronizes instance state internally. For batch work, prefer factory-based services that create a decoder per file.
// Preferred: use isolated decoder instances in concurrent work
await withTaskGroup(of: Void.self) { group in
for file in files {
group.addTask {
let decoder = try await DCMDecoder(contentsOfFile: file)
process(decoder)
}
}
}Cause: Many decoders loaded simultaneously.
Solution:
// Wrong: holding all decoders in memory
var decoders: [DCMDecoder] = []
for file in files {
let decoder = try DCMDecoder(contentsOfFile: file)
decoders.append(decoder) // Holds everything in memory
}
// Correct: process and release each decoder
for file in files {
autoreleasepool {
let decoder = try DCMDecoder(contentsOfFile: file)
process(decoder)
}
}
// Or extract only metadata
for file in files {
let decoder = try DCMDecoder(contentsOfFile: file)
let metadata = decoder.getStudyInfo()
saveMetadata(metadata)
}Cause: Trying to access pixels without loading a file first.
Solution: Use throwing initializers, which guarantee the file is loaded on success.
do {
let decoder = try DCMDecoder(contentsOfFile: path)
guard let pixels = decoder.getPixels16() else {
print("No pixel data")
return
}
process(pixels)
} catch {
print("Load error: \(error)")
}- Enable detailed logs:
// The decoder prints performance and error logs.
// Look for lines with [DCMDecoder] or [PERF].- Create a minimal example:
do {
let decoder = try DCMDecoder(contentsOfFile: "your_file.dcm")
print("Width: \(decoder.width), Height: \(decoder.height)")
print("Modality: \(decoder.info(for: .modality))")
} catch {
print("Error: \(error)")
}- Inspect the file with external tools:
dcmdump file.dcm
pip install pydicom
python -c "import pydicom; print(pydicom.dcmread('file.dcm'))"- Open a GitHub Issue:
- Include the decoder version.
- Provide a minimal code sample.
- Include
dcmdumpoutput (without sensitive data). - Describe expected vs. current behavior.
Previous: DICOM Glossary | Next: README Documentation Section