Closed aclark4life closed 3 years ago
The SCP is the one that gets to pick which transfer syntax it'll support and it looks like it's preferring Explicit VR, so I'd try adding a separate context for US with just the JPEG transfer syntax you're interested in.
The storescu
app's --required-contexts
option does something similar. Keep in mind there's a limit of 128 contexts when you're the association requestor, though.
The presentation context page in the user guide has a subsection on this (towards the bottom of that section).
@scaramallion Weee, thanks! Not the most elegant code, but works for my test data, much appreciated.
# Based on https://pydicom.github.io/pynetdicom/stable/examples/storage.html#storage-scp
import os
from pydicom import dcmread
# from pydicom.uid import JPEG2000Lossless, JPEGBaseline, JPEGLosslessSV1
from pydicom.uid import JPEG2000Lossless, JPEGBaseline, JPEGLossless
from pynetdicom import AE, StoragePresentationContexts, debug_logger, evt, build_context
from pynetdicom.sop_class import VerificationSOPClass, UltrasoundMultiframeImageStorage
# Implement a handler evt.EVT_C_STORE
def handle_store(event):
"""Handle a C-STORE request event. Write data to file system. Send data to Orthanc"""
# --------------------------------------------------------------------------
# Get the data
#
# Decode the C-STORE request's *Data Set* parameter to a pydicom Dataset
dataset = event.dataset
# Add the File Meta Information
dataset.file_meta = event.file_meta
# Get values from dataset
instance_num = str(dataset["InstanceNumber"].value)
patient_name = str(dataset["PatientName"].value)
patient_id = str(dataset["PatientID"].value)
study_date = str(dataset["StudyDate"].value)
study_time = str(dataset["StudyTime"].value)
series_desc = ""
if "SeriesDescription" in dataset:
series_desc = str(dataset["SeriesDescription"].value)
series_num = str(dataset["SeriesNumber"].value)
modality = str(dataset["Modality"].value)
# Clean up values
patient_name = patient_name.replace("^", "")
# --------------------------------------------------------------------------
# Write it the file system
#
# Use modality, patient, study, imaging series values to define directory tree and filename
# unless value is empty string, in which case filter. E.g. create dir '4' instead of '4_' when
# series_desc == ''.
subdir_0 = modality
subdir_1 = "_".join(filter(None, [patient_name, patient_id]))
subdir_2 = "_".join(filter(None, [patient_name, study_date, study_time]))
subdir_3 = "_".join(filter(None, [series_num, series_desc]))
filename = "_".join(filter(None, [patient_name, series_num, instance_num]))
filename += ".dcm"
# Make directories
try:
os.mkdir(subdir_0)
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1))
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1, subdir_2))
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1, subdir_2, subdir_3))
except FileExistsError:
pass
# Configure perms
os.chown(subdir_0, 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1), 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1, subdir_2), 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1, subdir_2, subdir_3), 101, gid=100)
path = os.path.join(subdir_0, subdir_1, subdir_2, subdir_3, filename)
# Save the dataset using modality, patient, study, imaging series as directory tree and filename
dataset.save_as(path, write_like_original=False)
os.chown(path, 101, gid=100)
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#
# Send it to Orthanc
#
# Based on https://pydicom.github.io/pynetdicom/stable/examples/storage.html#storage-scu
# Read in our DICOM dataset
# ds = dcmread(path)
# Associate with peer AE running Orthanc at ORTHANC_IP_ADDRESS and port 4242
try:
assoc = ae.associate(ORTHANC_IP_ADDRESS, 4242)
if assoc.is_established:
# Use the C-STORE service to send the dataset
assoc.send_c_store(dataset)
# Release the association
assoc.release()
except ValueError:
# https://github.com/pydicom/pynetdicom/issues/599
assoc = ae.associate(
ORTHANC_IP_ADDRESS,
4242,
contexts=[
build_context(
UltrasoundMultiframeImageStorage, "1.2.840.10008.1.2.4.70"
)
],
)
if assoc.is_established:
# Use the C-STORE service to send the dataset
assoc.send_c_store(dataset)
# Release the association
assoc.release()
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Return a 'Success' status
return 0x0000
handlers = [(evt.EVT_C_STORE, handle_store)]
# ------------------------------------------------------------------------------
# Initialise the Application Entity
ae = AE()
# Add the supported presentation contexts
# https://github.com/pydicom/pynetdicom/issues/591#issuecomment-798992575
# https://github.com/pydicom/pynetdicom/issues/591#issuecomment-801492853
for cx in StoragePresentationContexts:
cx.add_transfer_syntax(JPEGBaseline)
# cx.add_transfer_syntax(JPEGLosslessSV1)
cx.add_transfer_syntax(JPEGLossless)
cx.add_transfer_syntax(JPEG2000Lossless)
ae.supported_contexts = StoragePresentationContexts
# Ultrasound wants to verify
# https://pydicom.github.io/pynetdicom/stable/examples/verification.html#verification-scp
ae.add_supported_context(VerificationSOPClass)
# Set requested presentation context for storage-scu to send to Orthanc
ae.requested_contexts = StoragePresentationContexts
# --------------------------------------------------------------------------------
# Get Orthanc IP address or bust
ORTHANC_IP_ADDRESS = os.environ.get("ORTHANC_IP_ADDRESS", None)
if not ORTHANC_IP_ADDRESS:
print("Please `export ORTHANC_IP_ADDRESS=<ip_address>` before running scp.py")
exit(1)
# Debug if DEBUG
DEBUG = os.environ.get("DEBUG", None)
if DEBUG:
debug_logger()
# --------------------------------------------------------------------------------
# Start listening for incoming association requests
ae.start_server(("", 104), evt_handlers=handlers)
Seems like I should be able to figure this out based on the wealth of knowledge in this repo's issues … but I'm stuck. Do I need to do anything to the
assoc
to make "Send to Orthanc" work for an ultrasound image? Everything else working great!! Thanks for any infoCode
Error