Representations and Streaming
Representations and streaming
A DICOMweb route determines which request and response representations are possible. The client’s AcceptedMediaTypes becomes an HTTP Accept preference; the server selects among the route’s media types and, for rendered retrieval, the provider’s declared media types. DicomWebMediaTypes supplies constants for Part 10 DICOM, DICOM JSON, Native DICOM Model XML, multipart, bulk octet streams, and common rendered formats. Content-Type describes the representation actually returned. A multipart response has its own media type and individual part headers, including Content-Location where supplied. The server may return 406 Not Acceptable when it cannot supply an acceptable representation.
DICOM transfer syntax and metadata
For Part 10 retrieval through the Studies service, a transfer-syntax parameter can request a specific transfer syntax. Without that parameter, the standard Studies engine writes Explicit VR Little Endian. It can transcode to supported syntaxes; it does not promise that every syntax is available. transfer-syntax=* permits each returned file to retain its stored syntax, except Implicit VR Little Endian and Explicit VR Big Endian, which are converted to Explicit VR Little Endian. For uncompressed octet-stream bulk data, the standard engine may transcode encapsulated pixel data before writing frames. Rendered retrieval does not accept a transfer-syntax parameter. Inspect the returned Content-Type, multipart part headers, and file metadata to identify the negotiated representation.
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
string syntax = Aspose.Medical.Dicom.TransferSyntax.DeflatedExplicitVrLittleEndian.Uid.Code;
Aspose.Medical.Dicom.Web.DicomWebRetrieveOptions options = new()
{
AcceptedMediaTypes = [$"{Aspose.Medical.Dicom.Web.DicomWebMediaTypes.ApplicationDicom}; transfer-syntax={syntax}"]
};
Aspose.Medical.Dicom.Web.DicomWebClientResult<Aspose.Medical.Dicom.Web.Transactions.RetrieveInstanceResult> result =
await client.Studies.RetrieveInstanceAsync("1.2.3", "1.2.3.4", "1.2.3.4.5", options);
System.Console.WriteLine(result.Value.File.MetaInfo.TransferSyntax);
Use DicomWebRetrieveOptions for metadata too. AcceptedMediaTypes = [DicomWebMediaTypes.ApplicationDicomJson] asks for DICOM JSON; MultipartRelatedDicomXml requests a multipart Native DICOM Model XML collection where the route allows one. Metadata can contain external Bulk Data references. Configure DicomWebClientOptions.BulkDataLoader when your application needs to resolve those references while parsing; do not assume a particular invocation time for the loader.
For study or series retrieval, the typed client returns a one-pass DicomWebClientStream<DicomFile> and handles multipart Part 10 parsing. The lower-level DicomWebClientResponse.ReadDicomFilesAsync accepts single-part or multipart DICOM, while ReadDicomFileAsync accepts only one single-part application/dicom body. Neither file reader parses arbitrary octet-stream frame or bulk-data parts.
Studies.RetrieveInstanceAsync accepts single-part or multipart DICOM but requires exactly one file in the response. Keep its accepted representations to Part 10 DICOM; use the raw frame or bulk-data operations for octet-stream representations. WADO-URI DICOM retrieval uses the single-part file reader. Non-patient instance retrieval accepts single-part DICOM or metadata and constructs a DicomFile from the parsed metadata when that representation is selected. See the payload reader contracts.
The current typed NonPatientInstances.RetrieveAsync helper cannot consume multipart Part 10 responses, although the server route supports them. For that representation, send a raw RetrieveNonPatientInstance transaction through SendAsync and consume the response with ReadDicomFilesAsync.
Declare a JPEG representation
IDicomWebRepresentationProvider.SupportedMediaTypes controls which rendered transaction/media-type combinations are registered and advertised. GetResponseMediaTypes can narrow the choices for a particular request. This provider declares only instance JPEG rendering; it relies on the interface’s default RetrieveAsync, which renders a single-frame instance as JPEG and honors quality. Viewport, window, ICC profile processing, whole multi-frame rendering, and other output formats require an implementation of RetrieveAsync. MPR and 3D need a custom transaction handler because the bundled standard handler does not implement them.
public sealed class JpegInstanceRepresentations :
Aspose.Medical.Dicom.Web.Providers.IDicomWebRepresentationProvider
{
public System.Collections.Immutable.ImmutableDictionary<Aspose.Medical.Dicom.Web.DicomWebTransaction,
System.Collections.Immutable.ImmutableArray<string>> SupportedMediaTypes { get; } =
ImmutableDictionary<DicomWebTransaction,
ImmutableArray<string>>.Empty.Add(
Aspose.Medical.Dicom.Web.Transactions.RetrieveRenderedInstance.Instance,
[DicomWebMediaTypes.ImageJpeg]);
public System.Collections.Immutable.ImmutableArray<string> GetResponseMediaTypes(
Aspose.Medical.Dicom.Web.Providers.DicomWebRepresentationRequest request) =>
[Aspose.Medical.Dicom.Web.DicomWebMediaTypes.ImageJpeg];
}
Register new JpegInstanceRepresentations() alongside an IDicomWebStudiesProvider with AddStandardStudiesService. The default renderer obtains the stored file through the Studies provider; the application still supplies that storage. Do not declare PNG, GIF, or another transaction unless your provider’s RetrieveAsync actually produces it. The chosen output’s MediaType, ordered Items, ContentLocation, and optional headers are the contract between a custom representation provider and the standard response packager.
When overriding RetrieveAsync, call its selectMediaType callback with the single-part types you can produce before encoding the output. DicomWebRepresentationResult.MediaType and each DicomWebEncodedRepresentation.Content.ContentType must match a choice obtained from that callback, and the first item’s type must match the result’s MediaType. Return at least one item; a single-part response requires exactly one. Each item contains its encoded resource bytes and resource ContentLocation; the standard handler supplies multipart framing when negotiated. Keep data captured by an item’s DicomWebContent writer available until response writing finishes.
The corresponding client call supplies quality and streams the returned bytes directly to a destination. The destination file is illustrative; use a path and access policy appropriate for your application.
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
Aspose.Medical.Dicom.Web.DicomWebRenderOptions render = new()
{
AcceptedMediaTypes = [Aspose.Medical.Dicom.Web.DicomWebMediaTypes.ImageJpeg],
Quality = 80
};
await using Aspose.Medical.Dicom.Web.DicomWebClientResponse image =
await client.Studies.RetrieveRenderedInstanceAsync("1.2.3", "1.2.3.4", "1.2.3.4.5", render);
image.EnsureSuccessStatusCode();
await using System.IO.FileStream output = File.Create("instance.jpg");
await image.ContentReader.CopyToAsync(output);
Rendering controls
Set DicomWebRenderOptions properties to decoded values; the client escapes them when constructing the query. The following formats apply to the common controls on rendered Studies resources. Null scalar properties and an empty Annotation array omit those controls.
| Property | Format and accepted values |
|---|---|
Quality |
Integer from 1 through 100 for lossy image quality. |
Viewport |
"width,height" or "width,height,sourceX,sourceY,sourceWidth,sourceHeight". Output dimensions are positive integers. Each of the four source-region values is a decimal number or an empty component that requests its default. For example, "512,512,,,256,256" omits the source origin. |
Window |
"center,width,function". Center is a decimal number; width is positive. Function is linear, linear-exact, or sigmoid; linear requires width of at least 1. For example, "40,400,linear". |
IccProfile |
One or a comma-separated list of the lowercase tokens no, yes, srgb, adobergb, rommrgb, and displayp3. |
Annotation |
Keywords such as "patient" and "technique". Each keyword starts with an ASCII letter and then contains only ASCII letters, digits, underscores, or hyphens. Array entries become repeated annotation parameters; comma-separated keywords are also accepted. The renderer determines which annotations it supports. |
AdditionalParameters |
Ordered DicomWebQueryParameter values for other rendering controls or extensions. Their meaning and support depend on the requested transaction and renderer. |
Thumbnail transactions accept only the two-component viewport, such as "128,128"; a source-region viewport is invalid. Their parsed rendering controls ignore quality, window, ICC profile, and annotations. Set only the thumbnail viewport and representation preferences when using these options for a thumbnail request.
The server’s representation provider must implement the requested controls. The default JPEG implementation above supports quality, rejects viewport and window overrides or ICC processing, and reports unsupported annotations in a Warning header. The following request therefore requires a provider that implements these controls:
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
Aspose.Medical.Dicom.Web.DicomWebRenderOptions render = new()
{
AcceptedMediaTypes = [Aspose.Medical.Dicom.Web.DicomWebMediaTypes.ImageJpeg],
Quality = 85,
Viewport = "512,512",
Window = "40,400,linear",
IccProfile = "srgb",
Annotation = ["patient", "technique"]
};
await using Aspose.Medical.Dicom.Web.DicomWebClientResponse image =
await client.Studies.RetrieveRenderedInstanceAsync("1.2.3", "1.2.3.4", "1.2.3.4.5", render);
image.EnsureSuccessStatusCode();
await using System.IO.FileStream output = File.Create("windowed-instance.jpg");
await image.ContentReader.CopyToAsync(output);
Bulk data, frames, and multipart bodies
RetrieveBulkDataAsync takes a decoded path relative to the configured Studies service root. For a root of https://example.org/dicomweb/, "bulkdata/1.2.3/1.2.3.4/1.2.3.4.5/7FE00010" addresses Pixel Data through the standard provider. Pass query parameters through DicomWebRetrieveOptions.Query. An absolute BulkDataURI is not resolved by this method: convert a reference under the same service root to its relative path, or retrieve an external URI with an application-configured HTTP transport. For external references encountered while parsing metadata, supply an IAsyncBulkDataLoader through DicomWebClientOptions.BulkDataLoader.
The standard provider resolves bulkdata/{study}/{series}/{instance}/{attributePath}. The attribute path contains zero or more sequence-tag/item-index pairs followed by the value’s tag. Sequence item indices are zero-based: 0040A730/0/00420011 identifies Encapsulated Document in the first Content Sequence item. Use the references returned in metadata when available. Malformed tag/index paths produce 400 Bad Request; a missing sequence, item, or element produces 404 Not Found, and an element whose VR has no Bulk Data representation produces 406 Not Acceptable.
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
await using Aspose.Medical.Dicom.Web.DicomWebClientResponse bulk =
await client.Studies.RetrieveBulkDataAsync("bulkdata/1.2.3/1.2.3.4/1.2.3.4.5/7FE00010",
new DicomWebRetrieveOptions
{
AcceptedMediaTypes = [Aspose.Medical.Dicom.Web.DicomWebMediaTypes.MultipartRelatedOctetStream]
});
bulk.EnsureSuccessStatusCode();
System.Console.WriteLine(bulk.ContentType);
await using System.IO.FileStream output = File.Create("bulk.multipart");
await bulk.ContentReader.CopyToAsync(output);
Bulk-data and frame methods return a raw DicomWebClientResponse because their response can be binary or multipart and the caller must choose how to consume it. Frame numbers in RetrieveFramesAsync are one-based, and the collection must be nonempty with no duplicates. The next example preserves the entire multipart response, including its boundaries and part headers, as received. An application that needs individual frames must parse the multipart body using a suitable multipart reader; the DICOM Part 10 readers above are for files, not frame bytes.
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
Aspose.Medical.Dicom.Web.DicomWebRetrieveOptions options = new()
{
AcceptedMediaTypes = [Aspose.Medical.Dicom.Web.DicomWebMediaTypes.MultipartRelatedOctetStream]
};
await using Aspose.Medical.Dicom.Web.DicomWebClientResponse frames =
await client.Studies.RetrieveFramesAsync("1.2.3", "1.2.3.4", "1.2.3.4.5", [1, 2], options);
frames.EnsureSuccessStatusCode();
System.Console.WriteLine(frames.ContentType);
await using System.IO.FileStream output = File.Create("frames.multipart");
await frames.ContentReader.CopyToAsync(output);
DicomWebContent supplies the reverse direction for request or custom response bodies. FromDataset defaults to DICOM JSON, FromDatasets writes a JSON array or XML multipart collection, and FromDicomFiles streams a multipart Part 10 sequence. FromBytes retains the supplied memory until writing finishes; keep it unchanged. WriteToAsync accepts a Stream or PipeWriter. A host must keep services and source data alive until the write completes, even if a handler has already returned its result.
The following sends two files without constructing one combined byte array. It assumes two existing valid Part 10 files at the supplied paths:
using System.Net.Http.HttpClient transport = new();
Aspose.Medical.Dicom.Web.DicomWebClient client = new(transport,
new DicomWebClientOptions
{
BaseUri = new System.Uri("https://example.org/dicomweb/")
});
Aspose.Medical.Dicom.Web.DicomWebContent content =
DicomWebContent.FromDicomFiles(ReadFilesAsync(["first.dcm", "second.dcm"]));
Aspose.Medical.Dicom.Web.DicomWebClientResult<Aspose.Medical.Dicom.Web.Transactions.StoreInstancesResult> stored =
await client.Studies.StoreAsync(content);
System.Console.WriteLine(stored.Value.Response.Count);
static async System.Collections.Generic.IAsyncEnumerable<Aspose.Medical.Dicom.DicomFile> ReadFilesAsync(
System.Collections.Generic.IEnumerable<string> paths,
[System.Runtime.CompilerServices.EnumeratorCancellation] System.Threading.CancellationToken cancellationToken = default)
{
foreach (string path in paths)
{
cancellationToken.ThrowIfCancellationRequested();
yield return await Aspose.Medical.Dicom.DicomFile.OpenAsync(path, cancellationToken: cancellationToken);
}
}