Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add DICOM patcher rule to fix compressed ultrasound discolorization #7656

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 48 additions & 1 deletion Modules/Scripted/DICOMPatcher/DICOMPatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ def setup(self):
" Useful if many ultrasound acquisitions appear in the same series."))
parametersFormLayout.addRow("Split ultrasound series by instance number", self.splitUltrasoundSeriesByInstanceNumberCheckBox)

self.fixCompressedUltrasoundPhotometricInterpretationCheckBox = qt.QCheckBox()
self.fixCompressedUltrasoundPhotometricInterpretationCheckBox.checked = True
self.fixCompressedUltrasoundPhotometricInterpretationCheckBox.setToolTip(_(
"If checked, then photometric interpretation of JPEG-compressed ultrasound will be set to YBR_FULL_422."
" This is useful for fixing inconsistency in JPEG-compressed images that used RGB as photometric interpretation"
" due to ambiguity in the DICOM standard before CP156."
" See https://groups.google.com/g/comp.protocols.dicom/c/EPkbFOMBOhE"
" and https://dicom.nema.org/medical/dicom/Final/cp156_ft.pdf for more details."))
parametersFormLayout.addRow("Fix compressed ultrasound photometric interpretation", self.fixCompressedUltrasoundPhotometricInterpretationCheckBox)


characterSetLayout = qt.QHBoxLayout()

self.specifyCharacterSetCheckBox = qt.QCheckBox()
Expand Down Expand Up @@ -182,6 +193,8 @@ def onPatchButton(self):
self.logic.addRule("FixExposureFiasco")
if self.splitUltrasoundSeriesByInstanceNumberCheckBox.checked:
self.logic.addRule("SplitUltrasoundSeriesByInstanceNumber")
if self.fixCompressedUltrasoundPhotometricInterpretationCheckBox.checked:
self.logic.addRule("FixCompressedUltrasoundPhotometricInterpretation")
if self.forceSamePatientNameIdInEachDirectoryCheckBox.checked:
self.logic.addRule("ForceSamePatientNameIdInEachDirectory")
if self.forceSameSeriesInstanceUidInEachDirectoryCheckBox.checked:
Expand Down Expand Up @@ -223,7 +236,7 @@ def __init__(self, parameters=None):
def addLog(self, text):
logging.info(text)
if self.logCallback:
self.logCallback(text)
self.logCallback(" " + text)

def processStart(self, inputRootDir, outputRootDir):
pass
Expand Down Expand Up @@ -642,6 +655,40 @@ def processDataSet(self, ds):
ds.SeriesNumber = ds.SeriesNumber * 1000 + ds.InstanceNumber


#
#
#


class FixCompressedUltrasoundPhotometricInterpretation(DICOMPatcherRule):
def __init__(self, parameters=None):
super().__init__(parameters)
self.requiredTags = ["SOPClassUID"]
self.supportedSOPClassUIDs = [
"1.2.840.10008.5.1.4.1.1.3.1", # Ultrasound Multiframe Image Storage
"1.2.840.10008.5.1.4.1.1.6.1", # Ultrasound Image Storage
]

def processStart(self, inputRootDir, outputRootDir):
self.seriesInstanceUidAndInstanceNumberToNewSeriesInstanceUidMap = {}

def processDataSet(self, ds):
import pydicom

# Return if this is not an ultrasound series
for tag in self.requiredTags:
if not hasattr(ds, tag):
return
if ds.SOPClassUID not in self.supportedSOPClassUIDs:
return

# If the image is JPEG-compressed then
if ds.file_meta.TransferSyntaxUID == pydicom.uid.JPEGBaseline8Bit:
if ds.PhotometricInterpretation == "RGB":
self.addLog("Compressed JPEG image was found with RGB photometric interpretation. Changed it to YBR_FULL_422.")
ds.PhotometricInterpretation = "YBR_FULL_422"


#
# DICOMPatcherLogic
#
Expand Down