Using the DICOMweb standard

This page explains how to use the Cloud Healthcare API's implementation of DICOMweb. For more information on how the Cloud Healthcare API implements various DICOMweb REST services, see the DICOM conformance statement.

Note that the DICOMweb implementation in the Cloud Healthcare API only supports REST, not RPC.

Storing DICOM data

Before you can store DICOM data, you need to create a DICOM store.

The Cloud Healthcare API implements the Store transaction RESTful web service when storing DICOM data.

There are two methods of storing DICOM data:

  • Storing a DICOM instance (typically a .dcm file)
  • Storing DICOM JSON metadata with JPEG files

When you store a DICOM instance directly, only the application/dicom Accept Header is required. However, all requests to store DICOM JSON metadata with JPEG files are multipart messages, which is designated by the multipart/related portion of their Content-Type. This means that the request is made up of multiple parts of data that are combined after the request completes. Each of these sets of data must be separated using a boundary, as designated by the boundary portion of the Content-Type.

The following samples show how to store an instance in a DICOM store. For more information, see projects.locations.datasets.dicomStores.storeInstances.

Storing a DICOM instance

The following samples show how to store a DICOM instance.

curl command

To store a DICOM instance, make a POST request and provide the name of the parent dataset, the name of the DICOM store, the DICOM instance file, and an access token. The following sample shows a POST request using curl.

curl -X POST \
    -H "Content-Type: application/dicom" \
    -H "Authorization: Bearer "$(gcloud auth print-access-token) \
    https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies --data-binary @DCM_FILE.dcm

If the request is successful, the server returns a 200 OK HTTP status code and the response in XML format:

200 OK
<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID</Value>
  </DicomAttribute>
  <DicomAttribute tag="00081199" vr="SQ" keyword="ReferencedSOPSequence">
    <Item number="1">
      <DicomAttribute tag="00081150" vr="UI" keyword="ReferencedSOPClassUID">
        <Value number="1">SOPClassUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081155" vr="UI" keyword="ReferencedSOPInstanceUID">
        <Value number="1">SOPInstanceUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
        <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

PowerShell

To store a DICOM instance, make a POST request and provide the name of the parent dataset, the name of the DICOM store, the DICOM instance file, and an access token. The following sample shows a POST request using Windows PowerShell.

$cred = gcloud auth print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Post `
  -Headers $headers `
  -ContentType: "application/dicom" `
  -InFile DCM_FILE.dcm `
  -Uri "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies" | Select-Object -Expand Content

If the request is successful, the server returns a 200 OK HTTP status code and the response in XML format:

200 OK
<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID</Value>
  </DicomAttribute>
  <DicomAttribute tag="00081199" vr="SQ" keyword="ReferencedSOPSequence">
    <Item number="1">
      <DicomAttribute tag="00081150" vr="UI" keyword="ReferencedSOPClassUID">
        <Value number="1">SOPClassUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081155" vr="UI" keyword="ReferencedSOPInstanceUID">
        <Value number="1">SOPInstanceUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
        <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

Go

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"

	healthcare "google.golang.org/api/healthcare/v1beta1"
)

// dicomWebStoreInstance stores the given dicomFile with the dicomWebPath.
func dicomWebStoreInstance(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath, dicomFile string) error {
	ctx := context.Background()

	dicomData, err := ioutil.ReadFile(dicomFile)
	if err != nil {
		return fmt.Errorf("ReadFile: %v", err)
	}

	healthcareService, err := healthcare.NewService(ctx)
	if err != nil {
		return fmt.Errorf("healthcare.NewService: %v", err)
	}

	storesService := healthcareService.Projects.Locations.Datasets.DicomStores

	parent := fmt.Sprintf("projects/%s/locations/%s/datasets/%s/dicomStores/%s", projectID, location, datasetID, dicomStoreID)

	call := storesService.StoreInstances(parent, dicomWebPath, bytes.NewReader(dicomData))
	call.Header().Set("Content-Type", "application/dicom")
	resp, err := call.Do()
	if err != nil {
		return fmt.Errorf("StoreInstances: %v", err)
	}
	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("could not read response: %v", err)
	}

	if resp.StatusCode > 299 {
		return fmt.Errorf("StoreInstances: status %d %s: %s", resp.StatusCode, resp.Status, respBytes)
	}
	fmt.Fprintf(w, "%s", respBytes)
	return nil
}

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare;
import com.google.api.services.healthcare.v1beta1.CloudHealthcareScopes;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.HttpClients;

public class DicomWebStoreInstance {
  private static final String DICOM_NAME = "projects/%s/locations/%s/datasets/%s/dicomStores/%s";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void dicomWebStoreInstance(String dicomStoreName, String filePath)
      throws IOException, URISyntaxException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");
    // String filePath = "path/to/file.dcm";

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    HttpClient httpClient = HttpClients.createDefault();
    String uri = String.format(
        "%sv1beta1/%s/dicomWeb/studies", client.getRootUrl(), dicomStoreName);
    URIBuilder uriBuilder = new URIBuilder(uri)
        .setParameter("access_token", getAccessToken());
    // Load the data from file representing the study.
    File f = new File(filePath);
    byte[] dicomBytes = Files.readAllBytes(Paths.get(filePath));
    ByteArrayEntity requestEntity = new ByteArrayEntity(dicomBytes);

    HttpUriRequest request = RequestBuilder
        .post(uriBuilder.build())
        .setEntity(requestEntity)
        .addHeader("Content-Type","application/dicom")
        .build();

    // Execute the request and process the results.
    HttpResponse response = httpClient.execute(request);
    HttpEntity responseEntity = response.getEntity();
    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
      System.err.print(String.format(
          "Exception storing DICOM instance: %s\n", response.getStatusLine().toString()));
      responseEntity.writeTo(System.err);
      throw new RuntimeException();
    }
    System.out.println("DICOM instance stored: ");
    responseEntity.writeTo(System.out);
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredential credential =
        GoogleCredential.getApplicationDefault(HTTP_TRANSPORT, JSON_FACTORY)
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          credential.initialize(request);
          request.setHeaders(new HttpHeaders().set("X-GFE-SSL", "yes"));
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }

  private static String getAccessToken() throws IOException {
    GoogleCredential credential =
        GoogleCredential.getApplicationDefault(HTTP_TRANSPORT, JSON_FACTORY)
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));
    credential.refreshToken();
    return credential.getAccessToken();
  }
}

Node.js

function dicomWebStoreInstance(
  token,
  projectId,
  cloudRegion,
  datasetId,
  dicomStoreId,
  dcmFile,
  boundary
) {
  // Token retrieved in callback
  // getToken(serviceAccountJson, function(cb) {...});
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const dcmFile = 'IMG.dcm'
  // const boundary = 'DICOMwebBoundary'
  const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`;

  const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies`;

  const binaryData = fs.readFileSync(dcmFile);

  const options = {
    url: dicomWebPath,
    headers: {
      authorization: `Bearer ${token}`,
      'Content-Type': `multipart/related; type=application/dicom; boundary=${boundary}`,
    },
    body: binaryData,
    method: 'POST',
  };

  request(options)
    .then(results => {
      console.log('Stored instance:\n');
      console.log(results);
    })
    .catch(err => {
      console.error(err);
    });
}

Python

def dicomweb_store_instance(
        service_account_json,
        base_url,
        project_id,
        cloud_region,
        dataset_id,
        dicom_store_id,
        dcm_file):
    """Handles the POST requests specified in the DICOMweb standard."""
    url = '{}/projects/{}/locations/{}'.format(base_url,
                                               project_id, cloud_region)

    dicomweb_path = '{}/datasets/{}/dicomStores/{}/dicomWeb/studies'.format(
        url, dataset_id, dicom_store_id)

    # Make an authenticated API request
    session = get_session(service_account_json)

    with open(dcm_file, 'rb') as dcm:
        dcm_content = dcm.read()

    content_type = 'application/dicom'
    headers = {'Content-Type': content_type}

    try:
        response = session.post(
            dicomweb_path,
            data=dcm_content,
            headers=headers)
        response.raise_for_status()
        print('Stored DICOM instance:')
        print(response.text)
        return response
    except HttpError as err:
        print(err)
        return ""

Creating DICOM instances from JSON metadata and JPEG images

The Cloud Healthcare API can create DICOM instances using a JSON metadata file and a JPEG image. This can be useful if you prefer not to do DICOM parsing and serialization yourself, as the Cloud Healthcare API can do these tasks for you.

The HTTP request that stores this data must include the following in the request's Content-Type:

  • The multipart/related media type
  • The MIME type application/dicom+json
  • A boundary separator

The following samples show how to store a JSON metadata file with a JPEG file.

curl command

The following sample assumes that you have an existing JPEG image.

Storing a JSON metadata file with a JPEG image comprises three steps:

  1. Create a file that contains a JSON representation of a DICOM instance containing a JPEG image. A template file is provided below.
  2. Create three boundary files:

    • opening.file: Contains the opening boundary for the JSON metadata file
    • middle.file: Contains the middle boundary for the JPEG image
    • closing.file: Contains the closing boundary for all parts of the message
  3. Enclose the JSON metadata file and the JPEG image within the boundary files, thereby creating a new file called multipart-request.file.

Note the following values that are provided by default in the JSON metadata template file:

  • The Transfer Syntax UID (1.2.840.10008.1.2.4.50) designates the Transfer Syntax as JPEG Baseline. The majority of JPEG images are in the JPEG Baseline format. The Photometric Interpretation Value (YBR_FULL_422) signifies that the image is in color, not grayscale.
  • BulkDataUri is an arbitrary descriptor for the image, and in the template it is set to jpeg-image. This value is used when creating the image boundary.

The values for Media Storage SOP Class UID, Media Storage SOP Instance UID, SOPClassUID, SOPInstanceUID, StudyInstanceUID, and SeriesInstanceUID can be any numeric value separated by periods. DICOM uses a hierarchy of identifiers for instances, patients, studies, and series, so choose a logical set of identifiers for these variables. The values for the following variables must be identical:

  • Media Storage SOP Class UID and SOPClassUID
  • Media Storage SOP Instance UID and SOPInstanceUID

Replace Media Storage SOP Class UID with a value from the table of Standard SOP Classes that designates the type of image being stored.

Replace Rows with the vertical height of the JPEG image in pixels. Replace Columns with the horizontal width of the JPEG image in pixels.

Complete the following steps:

  1. Save the following text to a file called instance.json, replacing variables where specified.

    [{
     "00020002":{"vr":"UI","Value":["Media Storage SOP Class UID"]},
     "00020003":{"vr":"UI","Value":["Media Storage SOP Instance UID"]},
     "00020010":{"vr":"UI","Value":["1.2.840.10008.1.2.4.50"]},
     "00080005":{"vr":"CS","Value":["ISO_IR 192"]},
     "00080016":{"vr":"UI","Value":["SOPClassUID"]},
     "00080018":{"vr":"UI","Value":["SOPInstanceUID"]},
     "0020000D":{"vr":"UI","Value":["StudyInstanceUID"]},
     "0020000E":{"vr":"UI","Value":["SeriesInstanceUID"]},
     "00280002":{"vr":"US","Value":[3]},
     "00280004":{"vr":"CS","Value":["YBR_FULL_422"]},
     "00280006":{"vr":"US","Value":[0]},
     "00280008":{"vr":"IS","Value":[1]},
     "00280010":{"vr":"US","Value":[Rows]},
     "00280011":{"vr":"US","Value":[Columns]},
     "00280100":{"vr":"US","Value":[8]},
     "00280101":{"vr":"US","Value":[8]},
     "00280102":{"vr":"US","Value":[7]},
     "00280103":{"vr":"US","Value":[0]},
     "7FE00010":{"vr":"OB","BulkDataURI":"jpeg-image"}
    }]
    
  2. To create the opening (for the JSON metadata), middle (for the JPEG image), and closing boundaries, run the following commands:

    echo -ne "--DICOMwebBoundary\r\nContent-Type: application/dicom+json\r\n\r\n" > opening.file
    echo -ne "\r\n--DICOMwebBoundary\r\nContent-Location: jpeg-image\r\nContent-Type: image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.50\r\n\r\n" > middle.file
    echo -ne "\r\n--DICOMwebBoundary--" > closing.file
    
  3. Wrap the JPEG image within middle and closing boundaries. The output file, which you will send to the Cloud Healthcare API, is called multipart-request.file:

    cat opening.file instance.json middle.file image.jpg closing.file > multipart-request.file
    
  4. Make a POST request and provide the name of the parent dataset, the name of the DICOM store, the multipart-request.file file, and an access token. The following sample shows a POST request using curl.

    curl -X POST \
        -H "Content-Type: multipart/related; type=\"application/dicom+json\"; boundary=DICOMwebBoundary" \
        -H "Authorization: Bearer "$(gcloud auth print-access-token) \
        https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies --data-binary @multipart-request.file
    

If the request is successful, the server returns a 200 OK HTTP status code and the response in XML format:

200 OK
<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID</Value>
  </DicomAttribute>
  <DicomAttribute tag="00081199" vr="SQ" keyword="ReferencedSOPSequence">
    <Item number="1">
      <DicomAttribute tag="00081150" vr="UI" keyword="ReferencedSOPClassUID">
        <Value number="1">SOPClassUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081155" vr="UI" keyword="ReferencedSOPInstanceUID">
        <Value number="1">SOPInstanceUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
        <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

Using the dcm4che STOW-RS command-line tool

The Cloud Healthcare API supports the use of the dcm4che DICOM Toolkit and Library when making STOW-RS requests. To use the tool to store a study, complete the following steps:

  1. Download the latest version of the tool binaries.
  2. Run the tool:

    stowrs -url "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies?access_token=$(gcloud auth print-access-token)" DICOM_FILE
    

If the request is successful, the server returns a 200 OK HTTP status code and the following response:

200 OK
16:08:05,093 INFO  - added keys for coercion:

16:08:05,094 INFO  - Check extension of first file only to determine whether STOW is for dicom or non dicom type of objects.
16:08:05,094 INFO  - Storing DICOM objects
16:08:05,249 INFO  - > POST https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies?access_token=TOKEN
16:08:05,249 INFO  - > Content-Type: multipart/related; type="application/dicom"; boundary=BOUNDARY
16:08:05,249 INFO  - > Accept: application/dicom+xml
16:08:06,160 INFO  - < Content-Length: -1
16:08:06,160 INFO  - < HTTP/1.1 Response: 200 OK
16:08:06,160 INFO  - < Transfer-Encoding: null
16:08:06,160 INFO  - < Content-Type: application/dicom+xml
16:08:06,161 INFO  - < Date: 0
16:08:06,161 INFO  - < Response Content:
16:35:55,814 DEBUG - <NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID</Value>
  </DicomAttribute>
  <DicomAttribute tag="00081199" vr="SQ" keyword="ReferencedSOPSequence">
    <Item number="1">
      <DicomAttribute tag="00081150" vr="UI" keyword="ReferencedSOPClassUID">
        <Value number="1">SOPClassUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081155" vr="UI" keyword="ReferencedSOPInstanceUID">
        <Value number="1">SOPInstanceUID</Value>
      </DicomAttribute>
      <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
        <Value number="1">https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

16:08:06,162 INFO  - STOW successful!

Searching for studies, series, instances, and frames

The following samples show an implementation of the Search transaction to search for instances in a DICOM store. For more information, see projects.locations.datasets.dicomStores.searchForInstances.

curl command

To search for instances in a DICOM store, make a GET request and provide the name of the parent dataset, the name of the DICOM store, and an access token. The following sample shows a GET request using curl.

curl -X GET \
     -H "Authorization: Bearer "$(gcloud auth print-access-token) \
     "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/instances"

If the request is successful, the server returns a 200 OK HTTP status code and the response in JSON format:

200 OK
[
   {
      "00080005":{
         "vr":"CS",
         "Value":[
            "CODE_STRING"
         ]
      },
      "00080016":{
         "vr":"UI",
         "Value":[
            "UNIQUE_IDENTIFIER"
         ]
      },
      "00080018":{
         "vr":"UI",
         "Value":[
            "UNIQUE_IDENTIFIER"
         ]
      },
      "00080020":{
         "vr":"DA",
         "Value":[
            "DATE_TIME"
         ]
      },
      "00080030":{
         "vr":"TM",
         "Value":[
            "TIME"
         ]
      },
      "00080060":{
         "vr":"CS",
         "Value":[
            "CODE_STRING"
         ]
      },
      "0008103E":{
         "vr":"LO",
         "Value":[
            "LONG_STRING"
         ]
      },
      "00100010":{
         "vr":"PN",
         "Value":[
            {
               "Alphabetic":"Anonymized"
            }
         ]
      },
   },

...

]

PowerShell

To search for instances in a DICOM store, make a GET request and provide the name of the parent dataset, the name of the DICOM store, and an access token. The following sample shows a GET request using Windows PowerShell.

$cred = gcloud auth print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-RestMethod `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/instances"

If the request is successful, the server returns a 200 OK HTTP status code and the response in JSON format:

200 OK
[
   {
      "00080005":{
         "vr":"CS",
         "Value":[
            "CODE_STRING"
         ]
      },
      "00080016":{
         "vr":"UI",
         "Value":[
            "UNIQUE_IDENTIFIER"
         ]
      },
      "00080018":{
         "vr":"UI",
         "Value":[
            "UNIQUE_IDENTIFIER"
         ]
      },
      "00080020":{
         "vr":"DA",
         "Value":[
            "DATE_TIME"
         ]
      },
      "00080030":{
         "vr":"TM",
         "Value":[
            "TIME"
         ]
      },
      "00080060":{
         "vr":"CS",
         "Value":[
            "CODE_STRING"
         ]
      },
      "0008103E":{
         "vr":"LO",
         "Value":[
            "LONG_STRING"
         ]
      },
      "00100010":{
         "vr":"PN",
         "Value":[
            {
               "Alphabetic":"Anonymized"
            }
         ]
      },
   },

...

]

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare.Projects.Locations.Datasets.DicomStores;
import com.google.api.services.healthcare.v1beta1.CloudHealthcareScopes;

import java.io.IOException;
import java.util.Collections;

public class DicomWebSearchForInstances {
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void dicomWebSearchForInstances(String dicomStoreName) throws IOException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    // Create request and configure any parameters.
    DicomStores.SearchForInstances request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .searchForInstances(dicomStoreName, "instances");

    // Execute the request and process the results.
    HttpResponse response = request.executeUnparsed();
    System.out.println("Dicom store instances found: \n" + response.toString());
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredential credential =
        GoogleCredential.getApplicationDefault(HTTP_TRANSPORT, JSON_FACTORY)
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          credential.initialize(request);
          request.setHeaders(new HttpHeaders().set("X-GFE-SSL", "yes"));
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }
}

Node.js

function dicomWebSearchInstances(
  token,
  projectId,
  cloudRegion,
  datasetId,
  dicomStoreId
) {
  // Token retrieved in callback
  // getToken(serviceAccountJson, function(cb) {...});
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`;

  const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/instances`;

  const options = {
    url: dicomWebPath,
    headers: {
      authorization: `Bearer ${token}`,
      'Content-Type': 'application/dicom+json; charset=utf-8',
    },
    method: 'GET',
  };

  request(options)
    .then(results => {
      console.log('Instances:\n');
      console.log(results);
    })
    .catch(err => {
      console.error(err);
    });
}

Python

def dicomweb_search_instance(
        service_account_json,
        base_url,
        project_id,
        cloud_region,
        dataset_id,
        dicom_store_id):
    """Handles the GET requests specified in DICOMweb standard."""
    url = '{}/projects/{}/locations/{}'.format(base_url,
                                               project_id, cloud_region)

    dicomweb_path = '{}/datasets/{}/dicomStores/{}/dicomWeb/instances'.format(
        url, dataset_id, dicom_store_id)

    # Make an authenticated API request
    session = get_session(service_account_json)

    headers = {
        'Content-Type': 'application/dicom+json; charset=utf-8'
    }

    response = session.get(dicomweb_path, headers=headers)
    response.raise_for_status()

    instances = response.json()

    print('Instances:')
    print(json.dumps(instances, indent=2))

    return instances

Retrieving a study, series, instance, or frame

The Cloud Healthcare API implements the Retrieve transaction for retrieving studies, series, instances, and frames in a DICOM store. The following samples show how to retrieve a study. For more information, see projects.locations.datasets.dicomStores.studies.retrieveStudy.

curl command

To retrieve a study, make a GET request and provide the name of the parent dataset, the name of the DICOM store, the study unique identifier (UID), and an access token. The following sample shows a GET request using curl.

curl -X GET \
     -H "Authorization: Bearer "$(gcloud auth print-access-token) \
     "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID"

If the request is successful, the server returns a 200 OK HTTP status code and the following response:

200 OK
--805ab5a0ef9814a5da0e7750305bc75633a026bcf1847d23150948387c22
Content-Type: application/dicom; transfer-syntax=1.2.840.10008.1.2.1

Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.

PowerShell

To retrieve a study, make a GET request and provide the name of the parent dataset, the name of the DICOM store, the study unique identifier (UID), and an access token. The following sample shows a GET request using Windows PowerShell.

$cred = gcloud auth print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID" | Select-Object -Expand Content

If the request is successful, the server returns a 200 OK HTTP status code and the following response:

200 OK
--805ab5a0ef9814a5da0e7750305bc75633a026bcf1847d23150948387c22
Content-Type: application/dicom; transfer-syntax=1.2.840.10008.1.2.1

Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.

Go

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"

	healthcare "google.golang.org/api/healthcare/v1beta1"
)

// dicomWebRetrieveStudy retrieves all instances in the given dicomWebPath
// study.
func dicomWebRetrieveStudy(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string) error {
	ctx := context.Background()

	healthcareService, err := healthcare.NewService(ctx)
	if err != nil {
		return fmt.Errorf("healthcare.NewService: %v", err)
	}

	storesService := healthcareService.Projects.Locations.Datasets.DicomStores.Studies

	parent := fmt.Sprintf("projects/%s/locations/%s/datasets/%s/dicomStores/%s", projectID, location, datasetID, dicomStoreID)

	resp, err := storesService.RetrieveStudy(parent, dicomWebPath).Do()
	if err != nil {
		return fmt.Errorf("Delete: %v", err)
	}

	defer resp.Body.Close()

	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("could not read response: %v", err)
	}

	if resp.StatusCode > 299 {
		return fmt.Errorf("SearchForInstances: status %d %s: %s", resp.StatusCode, resp.Status, respBytes)
	}
	fmt.Fprintf(w, "%s", respBytes)
	return nil
}

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies;
import com.google.api.services.healthcare.v1beta1.CloudHealthcareScopes;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.stream.Collectors;

public class DicomWebRetrieveStudy {
  private static final String DICOM_NAME = "projects/%s/locations/%s/datasets/%s/dicomStores/%s";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void dicomWebRetrieveStudy(String dicomStoreName, String studyId)
      throws IOException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");
    // String studyId = "your-study-id";

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    // Create request and configure any parameters.
    Studies.RetrieveStudy request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .studies()
            .retrieveStudy(dicomStoreName, "studies/" + studyId);

    // Execute the request and process the results.
    HttpResponse response = request.executeUnparsed();
    String content = new BufferedReader(
        new InputStreamReader(response.getContent())).lines().collect(Collectors.joining("\n"));
    if (!response.isSuccessStatusCode()) {
      System.err.print(String.format(
          "Exception storing DICOM instance: %s\n", response.getStatusMessage()));
      System.out.println(content);
      throw new RuntimeException();
    }
    System.out.println("DICOM study retrieved: \n" + content);
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredential credential =
        GoogleCredential.getApplicationDefault(HTTP_TRANSPORT, JSON_FACTORY)
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          credential.initialize(request);
          request.setHeaders(new HttpHeaders().set("X-GFE-SSL", "yes"));
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }
}

Node.js

function dicomWebRetrieveStudy(
  token,
  projectId,
  cloudRegion,
  datasetId,
  dicomStoreId,
  studyUid
) {
  // Token retrieved in callback
  // getToken(serviceAccountJson, function(cb) {...});
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.2.345.678901.2.345.6789.0123456.7890.1234567890.123'
  const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`;

  const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies/${studyUid}`;

  const options = {
    url: dicomWebPath,
    headers: {
      authorization: `Bearer ${token}`,
      'Content-Type': 'application/dicom+json; charset=utf-8',
    },
    method: 'GET',
  };

  request(options)
    .then(() => {
      console.log(`Retrieved study with UID: ${studyUid}`);
    })
    .catch(err => {
      console.error(err);
    });
}

Python

def dicomweb_retrieve_study(
        service_account_json,
        base_url,
        project_id,
        cloud_region,
        dataset_id,
        dicom_store_id,
        study_uid):
    """Handles the GET requests specified in the DICOMweb standard."""
    url = '{}/projects/{}/locations/{}'.format(base_url,
                                               project_id, cloud_region)

    dicomweb_path = '{}/datasets/{}/dicomStores/{}/dicomWeb/studies/{}'.format(
        url, dataset_id, dicom_store_id, study_uid)

    # Make an authenticated API request
    session = get_session(service_account_json)

    headers = {
        'Content-Type': 'application/dicom+json; charset=utf-8'
    }

    response = session.get(dicomweb_path, headers=headers)
    response.raise_for_status()

    print('Retrieved study with UID: {}'.format(study_uid))

    return response

Deleting a study, series, or instance

The Cloud Healthcare API implements a proprietary web service for deleting DICOM studies, series, and instances. This service is not part of the DICOMweb standard services.

The following samples show how to delete a DICOM study. For more information, see projects.locations.datasets.dicomStores.studies.delete.

curl command

To delete a DICOM study, make a DELETE request and provide the name of the parent dataset, the name of the DICOM store, the study's unique identifier (UID), and an access token. The following sample shows how to delete a study with a DELETE request using curl.

curl -X DELETE \
     -H "Authorization: Bearer "$(gcloud auth print-access-token) \
     "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID"

If the request is successful, the server returns a 200 OK HTTP status code and the empty response body in JSON format:

200 OK
{}

PowerShell

To delete a DICOM study, make a DELETE request and provide the name of the parent dataset, the name of the DICOM store, the study's unique identifier (UID), and an access token. The following sample shows how to delete a study with a DELETE request using Windows PowerShell.

$cred = gcloud auth print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Delete `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID" | Select-Object -Expand Content

If the request is successful, the server returns a 200 OK HTTP status code and the empty response body in JSON format:

200 OK
{}

Go

import (
	"context"
	"fmt"
	"io"

	healthcare "google.golang.org/api/healthcare/v1beta1"
)

// dicomWebDeleteStudy deletes all instances in the given dicomWebPath study.
func dicomWebDeleteStudy(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string) error {
	ctx := context.Background()

	healthcareService, err := healthcare.NewService(ctx)
	if err != nil {
		return fmt.Errorf("healthcare.NewService: %v", err)
	}

	storesService := healthcareService.Projects.Locations.Datasets.DicomStores.Studies

	parent := fmt.Sprintf("projects/%s/locations/%s/datasets/%s/dicomStores/%s", projectID, location, datasetID, dicomStoreID)

	if _, err := storesService.Delete(parent, dicomWebPath).Do(); err != nil {
		return fmt.Errorf("Delete: %v", err)
	}

	fmt.Fprintf(w, "Deleted %q\n", dicomWebPath)
	return nil
}

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare;
import com.google.api.services.healthcare.v1beta1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies;
import com.google.api.services.healthcare.v1beta1.CloudHealthcareScopes;
import java.io.IOException;
import java.util.Collections;

public class DicomWebDeleteStudy {
  private static final String DICOM_NAME = "projects/%s/locations/%s/datasets/%s/dicomStores/%s";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void dicomWebDeleteStudy(String dicomStoreName, String studyId) throws IOException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");
    // String studyId = "your-study-id";

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    // Create request and configure any parameters.
    Studies.Delete request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .studies()
            .delete(dicomStoreName, "studies/" + studyId);

    // Execute the request and process the results.
    request.execute();
    System.out.println("DICOM study deleted.");
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredential credential =
        GoogleCredential.getApplicationDefault(HTTP_TRANSPORT, JSON_FACTORY)
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          credential.initialize(request);
          request.setHeaders(new HttpHeaders().set("X-GFE-SSL", "yes"));
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }
}

Node.js

function dicomWebDeleteStudy(
  token,
  projectId,
  cloudRegion,
  datasetId,
  dicomStoreId,
  studyUid
) {
  // Token retrieved in callback
  // getToken(serviceAccountJson, function(cb) {...});
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.2.345.678901.2.345.6789.0123456.7890.1234567890.123'
  const parentName = `${BASE_URL}/projects/${projectId}/locations/${cloudRegion}`;

  const dicomWebPath = `${parentName}/datasets/${datasetId}/dicomStores/${dicomStoreId}/dicomWeb/studies/${studyUid}`;

  const options = {
    url: dicomWebPath,
    headers: {
      authorization: `Bearer ${token}`,
      'Content-Type': 'application/dicom+json; charset=utf-8',
    },
    method: 'DELETE',
  };

  request(options)
    .then(() => {
      console.log('Deleted study.');
    })
    .catch(err => {
      console.error(err);
    });
}

Python

def dicomweb_delete_study(
        service_account_json,
        base_url,
        project_id,
        cloud_region,
        dataset_id,
        dicom_store_id,
        study_uid):
    """Handles DELETE requests equivalent to the GET requests specified in
    the WADO-RS standard.
    """
    url = '{}/projects/{}/locations/{}'.format(base_url,
                                               project_id, cloud_region)

    dicomweb_path = '{}/datasets/{}/dicomStores/{}/dicomWeb/studies/{}'.format(
        url, dataset_id, dicom_store_id, study_uid)

    # Make an authenticated API request
    session = get_session(service_account_json)

    headers = {
        'Content-Type': 'application/dicom+json; charset=utf-8'
    }

    response = session.delete(dicomweb_path, headers=headers)
    response.raise_for_status()

    print('Deleted study.')

    return response

Var denne siden nyttig? Si fra hva du synes:

Send tilbakemelding om ...

Cloud Healthcare API