DICOMweb 표준 사용

이 페이지에서는 Cloud Healthcare API의 DICOMweb 구현을 사용하는 방법을 설명합니다. Cloud Healthcare API가 여러 DICOMweb REST 서비스를 구현하는 방법에 대한 자세한 내용은 DICOM 적합성 명세를 참조하세요.

Cloud Healthcare API의 DICOM 웹 구현은 RPC가 아닌 REST만 지원합니다.

Cloud Healthcare API DICOMweb CLI

이 페이지의 일부 샘플에는 DICOMweb 서버와의 상호작용 방법을 단순화하는 오픈소스 도구인 Cloud Healthcare API DICOMweb CLI가 사용됩니다. 이 도구는 DICOM 파일 저장, 검색, 삭제, 검색을 위한 기능을 제공합니다. 이 도구의 GitHub 페이지에는 세부 설치 요구사항 및 도구 맞춤설정 방법과 같은 추가 정보가 포함되어 있습니다.

이 도구는 Python을 사용하여 실행됩니다. Google Cloud에서 Python을 설정하는 방법에 대한 자세한 내용은 Python 개발 환경 설정을 참조하세요.

Python을 설정한 후에는 PIP를 사용해서 도구를 설치할 수 있습니다.

pip install https://github.com/GoogleCloudPlatform/healthcare-api-dicomweb-cli/archive/v1.0.zip

이 도구를 사용하려면 Google Cloud 서버로 인증해야 합니다. 이를 위해서는 다음 방법 중 하나를 사용하면 됩니다.

이러한 옵션을 구성하면 도구가 사용자 인증 정보를 자동으로 검색합니다.

DICOM 데이터 저장

DICOM 데이터를 저장하려면 먼저 DICOM 저장소를 만들어야 합니다.

Cloud Healthcare API는 DICOM 데이터를 저장할 때 저장소 트랜잭션 RESTful 웹 서비스를 구현합니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 저장소 트랜잭션을 참조하세요.

DICOM 데이터를 저장하는 방법은 두 가지입니다.

  • DICOM 인스턴스 저장(일반적으로 .dcm 파일)
  • JPEG 파일로 DICOM JSON 메타데이터 저장

DICOM 인스턴스를 직접 저장할 때는 application/dicom Accept 헤더만 필요합니다. 하지만 JPEG 파일로 DICOM JSON 메타데이터를 저장하기 위한 모든 요청은 Content-Typemultipart/related 부분으로 지정되는 멀티파트 메시지입니다. Content-Typemultipart/related 부분은 요청이 요청 완료 후 조합되는 여러 데이터 부분으로 구성되었음을 나타냅니다. Content-Typeboundary 부분으로 지정된 대로 경계를 사용하여 이러한 각 데이터 세트를 구분해야 합니다.

다음 샘플은 DICOM 저장소에 인스턴스를 저장하는 방법을 보여줍니다. 자세한 내용은 projects.locations.datasets.dicomStores.storeInstances를 참조하세요.

DICOM 인스턴스 저장

다음 샘플은 DICOM 인스턴스를 저장하는 방법을 보여줍니다.

curl

DICOM 인스턴스를 저장하려면 POST 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • DICOM 인스턴스 파일
  • 액세스 토큰

다음 샘플은 curl을 사용하는 POST 요청을 보여줍니다.

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

요청이 성공하면 서버가 XML 형식으로 응답을 반환합니다.

<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/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/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

PowerShell

DICOM 인스턴스를 저장하려면 POST 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • DICOM 인스턴스 파일
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 POST 요청을 보여줍니다.

$cred = gcloud auth application-default 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/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies" | Select-Object -Expand Content

요청이 성공하면 서버가 XML 형식으로 응답을 반환합니다.

<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/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/v1/projects/PROJECT_ID/locations/LOCATION/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/v1"
)

// 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
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
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("%sv1/%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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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 {
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    return credential.refreshAccessToken().getTokenValue();
  }
}

Node.js

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');
const fs = require('fs');

const dicomWebStoreInstance = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({
    auth,
    headers: {
      'Content-Type': 'application/dicom',
      Accept: 'application/dicom+json',
    },
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const dcmFile = 'file.dcm';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = 'studies';
  // Use a stream because other types of reads overwrite the client's HTTP
  // headers and cause storeInstances to fail.
  const binaryData = fs.createReadStream(dcmFile);
  const request = {
    parent,
    dicomWebPath,
    requestBody: binaryData,
  };

  const instance = await healthcare.projects.locations.datasets.dicomStores.storeInstances(
    request
  );
  console.log('Stored DICOM instance:\n', JSON.stringify(instance.data));
};

dicomWebStoreInstance();

Python

def dicomweb_store_instance(
    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()

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

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

    response = session.post(dicomweb_path, data=dcm_content, headers=headers)
    response.raise_for_status()
    print("Stored DICOM instance:")
    print(response.text)
    return response

JSON 메타데이터 및 JPEG 이미지에서 DICOM 인스턴스 만들기

Cloud Healthcare API는 JSON 메타데이터 파일 및 JPEG 이미지를 사용하여 DICOM 인스턴스를 만들 수 있습니다. Cloud Healthcare API로 수행할 수 있으므로, DICOM 파싱 및 직렬화를 직접 수행하지 않으려면 JSON 메타데이터 및 JPEG 이미지에서 DICOM 인스턴스를 만듭니다.

이 데이터를 저장하는 HTTP 요청에는 요청의 Content-Type에 다음이 포함되어야 합니다.

  • multipart/related 미디어 유형
  • MIME 유형 application/dicom+json
  • boundary 구분 기호

다음 샘플은 JPEG 파일로 JSON 메타데이터 파일을 저장하는 방법을 보여줍니다.

curl

다음 샘플은 기존 JPEG 이미지가 있다고 가정합니다.

JPEG 이미지로 JSON 메타데이터 파일을 저장하는 작업은 다음 세 가지 단계로 구성됩니다.

  1. JPEG 이미지가 포함된 DICOM 인스턴스의 JSON 표현이 포함된 파일을 만듭니다. 템플릿 파일은 아래에 제공됩니다.
  2. 3개의 경계 파일을 만듭니다.

    • opening.file: JSON 메타데이터 파일의 시작 경계를 포함합니다.
    • middle.file: JPEG 이미지의 중간 경계를 포함합니다.
    • closing.file: 메시지의 모든 부분에 대한 종료 경계를 포함합니다.
  3. 경계 파일 내에서 JSON 메타데이터 파일 및 JPEG 이미지를 묶어서 multipart-request.file이라는 파일을 만듭니다.

JSON 메타데이터 템플릿 파일에 기본적으로 제공되는 다음 값을 확인합니다.

  • 전송 구문 UID(1.2.840.10008.1.2.4.50)는 전송 구문을 JPEG 기준으로 지정합니다. 대부분의 JPEG 이미지는 JPEG 기준 형식입니다. 사진 해석 값(YBR_FULL_422)은 이미지가 그레이 스케일이 아닌 컬러라는 것을 나타냅니다.
  • BulkDataUri는 이미지의 임의 설명자이고, 템플릿에서 jpeg-image로 설정됩니다. 이 값은 이미지 경계를 만들 때 사용됩니다.

SOPClassUID, SOPInstanceUID, StudyInstanceUID, SeriesInstanceUID 값은 마침표로 구분된 숫자 값일 수 있습니다. DICOM은 인스턴스, 환자, 연구, 시리즈에 대해 식별자 계층을 사용하므로, 이러한 변수에 대해 논리적인 식별자 세트를 선택합니다.

SOP Class UID를 저장되는 이미지 유형을 지정하는 표준 SOP 클래스 테이블의 값으로 바꿉니다.

Rows를 JPEG 이미지의 세로 높이(픽셀)로 바꿉니다. Columns를 JPEG 이미지의 가로 너비(픽셀)로 바꿉니다.

다음 단계를 완료합니다.

  1. 다음 텍스트를 instance.json이라는 파일에 저장하고, 지정된 경우 변수를 바꿉니다.

    [{
     "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. 시작(JSON 메타데이터), 중간(JPEG 이미지), 종료 경계를 만들기 위해 다음 명령어를 실행합니다.

    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. 중간 및 종료 경계 내에서 JPEG 이미지를 래핑합니다. Cloud Healthcare API로 전송하는 출력 파일은 multipart-request.file이라고 부릅니다.

    cat opening.file instance.json middle.file image.jpg closing.file > multipart-request.file
    
  4. POST 요청을 수행하고 다음 정보를 지정합니다.

    • 상위 데이터 세트의 이름
    • DICOM 저장소의 이름
    • multipart-request.file 파일
    • 액세스 토큰

다음 샘플은 curl을 사용하는 POST 요청을 보여줍니다.

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

요청이 성공하면 서버가 XML 형식으로 응답을 반환합니다.

<NativeDicomModel>
  <DicomAttribute tag="00081190" vr="UR" keyword="RetrieveURL">
    <Value number="1">https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/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/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID</Value>
      </DicomAttribute>
    </Item>
  </DicomAttribute>
</NativeDicomModel>

DICOMweb CLI 사용

다음 샘플은 Cloud Healthcare API DICOMweb CLI를 사용하여 하나 이상의 DICOM 인스턴스를 저장하는 방법을 보여줍니다. DICOMweb CLI GitHub 저장소에는 사용 가능한 샘플이 더 있습니다.

단일 DICOM 인스턴스 저장:

dcmweb \
  https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb \
  store DCM_FILE

요청이 성공하면 서버가 다음 샘플과 비슷한 응답을 반환합니다.

TIMESTAMP -- DCM_FILE.dcm uploaded as https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID TIMESTAMP -- INSTANCE_UID
TIMESTAMP -- Transferred SIZE in COUNT files

와일드 카드를 사용하여 여러 파일을 병렬로 저장:

다음 샘플은 현재 작동 중인 디렉터리에서 여러 DICOM 파일을 병렬로 반복해서 저장하는 방법을 보여줍니다. 파일을 병렬로 저장하려면 -m 플래그를 추가합니다.

dcmweb -m \
  https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb \
  store "./**.dcm"

요청이 성공하면 서버가 다음 샘플과 비슷한 응답을 반환합니다.

TIMESTAMP -- DCM_FILE_1.dcm uploaded as https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID TIMESTAMP -- INSTANCE_UID
TIMESTAMP -- DCM_FILE_2.dcm uploaded as https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID TIMESTAMP -- INSTANCE_UID
TIMESTAMP -- DCM_FILE_3.dcm uploaded as https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID TIMESTAMP -- INSTANCE_UID
...
TIMESTAMP -- Transferred SIZE in COUNT files

연구, 시리즈, 인스턴스, 프레임 검색

다음 샘플은 DICOM 저장소에서 인스턴스를 검색하기 위한 검색 트랜잭션의 구현을 보여줍니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 검색 트랜잭션을 참조하세요.

다음 샘플은 DICOM 저장소에서 인스턴스 검색 방법을 보여줍니다. 자세한 내용은 projects.locations.datasets.dicomStores.searchForInstances를 참조하세요.

curl

DICOM 저장소에서 인스턴스를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 액세스 토큰

다음 샘플은 curl을 사용하는 GET 요청을 보여줍니다.

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

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

[
   {
      "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

DICOM 저장소에서 인스턴스를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 GET 요청을 보여줍니다.

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

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

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

[
   {
      "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"
            }
         ]
      },
   },

...

]

Go

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

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

// dicomWebSearchInstances searches instances.
func dicomWebSearchInstances(w io.Writer, projectID, location, datasetID, dicomStoreID string) error {
	// projectID := "my-project"
	// location := "us-central1"
	// datasetID := "my-dataset"
	// dicomStoreID := "my-dicom-store"
	ctx := context.Background()

	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)

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

	defer resp.Body.Close()

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

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

	respString := string(respBytes)
	fmt.Fprintf(w, "Found instances: %s\n", respString)
	return nil
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.Collections;

public class DicomWebSearchForInstances {
  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 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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');

const dicomWebSearchForInstances = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({
    auth,
    headers: {Accept: 'application/dicom+json,multipart/related'},
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = 'instances';
  const request = {parent, dicomWebPath};

  const instances = await healthcare.projects.locations.datasets.dicomStores.searchForInstances(
    request
  );
  console.log(`Found ${instances.data.length} instances:`);
  console.log(JSON.stringify(instances.data));
};

dicomWebSearchForInstances();

Python

def dicomweb_search_instance(
    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()

    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

DICOM 태그를 사용하여 검색

쿼리 매개변수 형식으로 요청에 DICOM 태그를 추가하여 검색을 세분화할 수 있습니다. 예를 들어 환자 이름이 포함된 연구를 검색해야 할 수 있습니다.

앞의 샘플에서와 같이 다음 샘플은 DICOM 저장소에서 연구를 검색하기 위한 검색 트랜잭션 구현을 보여줍니다. 하지만 이 샘플은 환자 이름이 'Sally Zhang'인 연구를 검색하는 방법을 보여줍니다.

다음 샘플은 환자 이름이 나열된 DICOM 인스턴스의 메타데이터 부분을 보여줍니다.

...
{
  "vr": "PN",
  "Value": [
    {
      "Alphabetic": "Sally Zhang"
    }
  ]
}
...

DICOM 저장소에서 환자와 관련된 연구를 검색하려면 PatientName DICOM 태그로 검색하는 요청에 쿼리 매개변수를 추가합니다. Cloud Healthcare API에서 지원되는 검색 매개변수 목록을 보려면 검색 트랜잭션 문서를 참조하세요.

curl

DICOM 저장소에서 연구를 검색할 때 태그를 사용하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 쿼리 매개변수
  • 액세스 토큰

다음 샘플은 curl을 사용하는 GET 요청을 보여줍니다.

curl -X GET \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies?PatientName=Sally%20Zhang"

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

[
  {
    "00080005": {
      "vr": "CS",
      "Value": [
        "CODE_STRING"
      ]
    },
    "00080020": {
      "vr": "DA",
      "Value": [
        "DATE_TIME"
      ]
    },
    "00080030": {
      "vr": "TM",
      "Value": [
        "TIME"
      ]
    },
    "00080050": {
      "vr": "SH"
    },
    "00080090": {
      "vr": "PN",
      "Value": [
        {
          "Alphabetic": "VALUE"
        }
      ]
    },
    "00100010": {
      "vr": "PN",
      "Value": [
        {
          "Alphabetic": "Sally Zhang"
        }
      ]
    },
    "00100020": {
      "vr": "LO",
      "Value": [
        "VALUE"
      ]
    },
    "00100030": {
      "vr": "DA"
    },
    "00100040": {
      "vr": "CS",
      "Value": [
        "F"
      ]
    },
    "0020000D": {
      "vr": "UI",
      "Value": [
        "STUDY_INSTANCE_UID"
      ]
    },
    "00200010": {
      "vr": "SH"
    }
  }
]

PowerShell

DICOM 저장소에서 연구를 검색할 때 태그를 사용하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 쿼리 매개변수
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 GET 요청을 보여줍니다.

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

Invoke-RestMethod `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies?PatientName=Sally%20Zhang"

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

[
  {
    "00080005": {
      "vr": "CS",
      "Value": [
        "CODE_STRING"
      ]
    },
    "00080020": {
      "vr": "DA",
      "Value": [
        "DATE_TIME"
      ]
    },
    "00080030": {
      "vr": "TM",
      "Value": [
        "TIME"
      ]
    },
    "00080050": {
      "vr": "SH"
    },
    "00080090": {
      "vr": "PN",
      "Value": [
        {
          "Alphabetic": "VALUE"
        }
      ]
    },
    "00100010": {
      "vr": "PN",
      "Value": [
        {
          "Alphabetic": "Sally Zhang"
        }
      ]
    },
    "00100020": {
      "vr": "LO",
      "Value": [
        "VALUE"
      ]
    },
    "00100030": {
      "vr": "DA"
    },
    "00100040": {
      "vr": "CS",
      "Value": [
        "F"
      ]
    },
    "0020000D": {
      "vr": "UI",
      "Value": [
        "STUDY_INSTANCE_UID"
      ]
    },
    "00200010": {
      "vr": "SH"
    }
  }
]

Go

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

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

// queryParamOpt is a googleapi.Option (https://godoc.org/google.golang.org/api/googleapi#CallOption)
// that adds query parameters to an API call.
type queryParamOpt struct {
	key, value string
}

func (qp queryParamOpt) Get() (string, string) { return qp.key, qp.value }

// dicomWebSearchStudies refines a DICOMweb studies search by appending DICOM tags to the request.
func dicomWebSearchStudies(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string) error {
	// projectID := "my-project"
	// location := "us-central1"
	// datasetID := "my-dataset"
	// dicomStoreID := "my-dicom-store"
	// dicomWebPath := "studies"
	ctx := context.Background()

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

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

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

	call := storesService.SearchForStudies(name, dicomWebPath)
	// Refine your search by appending DICOM tags to the
	// request in the form of query parameters. This sample
	// searches for studies containing a patient's name.
	patientName := queryParamOpt{key: "PatientName", value: "Sally Zhang"}
	resp, err := call.Do(patientName)
	if err != nil {
		return fmt.Errorf("Get: %v", err)
	}

	defer resp.Body.Close()

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

	if resp.StatusCode > 299 {
		return fmt.Errorf("SearchForStudies: status %d %s: %s", resp.StatusCode, resp.Status, respBytes)
	}
	respString := string(respBytes)
	fmt.Fprintf(w, "Found studies: %s\n", respString)

	return nil
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.Collections;

public class DicomWebSearchStudies {
  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 dicomWebSearchStudies(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();

    DicomStores.SearchForStudies request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .searchForStudies(dicomStoreName, "studies")
            // Refine your search by appending DICOM tags to the
            // request in the form of query parameters. This sample
            // searches for studies containing a patient's name.
            .set("PatientName", "Sally Zhang");

    // Execute the request and process the results.
    HttpResponse response = request.executeUnparsed();
    System.out.println("Studies 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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');

const dicomWebSearchStudies = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });

  google.options({
    auth,
    // Refine your search by appending DICOM tags to the
    // request in the form of query parameters. This sample
    // searches for studies containing a patient's name.
    params: {PatientName: 'Sally Zhang'},
    headers: {Accept: 'application/dicom+json'},
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = 'studies';
  const request = {parent, dicomWebPath};

  const studies = await healthcare.projects.locations.datasets.dicomStores.searchForStudies(
    request
  );
  console.log(studies);

  console.log(`Found ${studies.data.length} studies:`);
  console.log(JSON.stringify(studies.data));
};

dicomWebSearchStudies();

Python

def dicomweb_search_studies(
    base_url, project_id, cloud_region, dataset_id, dicom_store_id
):
    """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
    )

    # Refine your search by appending DICOM tags to the
    # request in the form of query parameters. This sample
    # searches for studies containing a patient's name.
    params = {"PatientName": "Sally Zhang"}

    session = get_session()

    response = session.get(dicomweb_path, params=params)

    response.raise_for_status()

    print("Studies found: response is {}".format(response))

    # Uncomment the following lines to process the response as JSON.
    # patients = response.json()
    # print('Patients found matching query:')
    # print(json.dumps(patients, indent=2))

    # return patients

DICOMweb CLI 사용

다음 샘플은 Cloud Healthcare API DICOMweb CLI를 사용하여 DICOM 저장소에서 인스턴스를 검색하는 방법을 보여줍니다. 검색 필터링 방법을 포함하여 DICOMweb CLI GitHub 저장소에서 사용 가능한 샘플이 더 있습니다.

dcmweb \
  https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb \
  search instances

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

[
   {
      "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"
            }
         ]
      },
   },

...

]

DICOM 데이터 검색

Cloud Healthcare API는 DICOM 저장소에서 연구, 시리즈, 인스턴스, 프레임을 검색하기 위해 검색 트랜잭션을 구현합니다.

자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 검색 트랜잭션을 참조하세요.

연구 검색

다음 샘플은 연구를 검색하는 방법을 보여줍니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 DICOM 연구/시리즈/인스턴스를 참조하세요.

출력 파일을 지정할 때 .multipart와 같은 확장자를 사용합니다. 그런 후 멀티파트 파일을 파싱하여 연구에서 개별 시리즈 및 인스턴스를 가져옵니다.

자세한 내용은 projects.locations.datasets.dicomStores.studies.retrieveStudy를 참조하세요.

curl

연구를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 출력 파일
  • 액세스 토큰

다음 샘플은 curl을 사용하는 GET 요청을 보여줍니다.

curl -X GET \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     -H "Accept: multipart/related; type=application/dicom; transfer-syntax=*" \
     "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID"
     --output FILENAME.multipart

요청이 성공하면 DICOM 파일이 머신에 기록됩니다.

PowerShell

연구를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 출력 파일
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 GET 요청을 보여줍니다.

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred"; Accept = "multipart/related; type=application/dicom; transfer-syntax=*" }

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

요청이 성공하면 DICOM 파일이 머신에 기록됩니다.

Go

import (
	"context"
	"fmt"
	"io"
	"os"

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

// dicomWebRetrieveStudy retrieves all instances in the given dicomWebPath
// study.
func dicomWebRetrieveStudy(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string, outputFile string) error {
	// projectID := "my-project"
	// location := "us-central1"
	// datasetID := "my-dataset"
	// dicomStoreID := "my-dicom-store"
	// dicomWebPath := "studies/1.3.6.1.4.1.11129.5.5.111396399857604"
	// outputFile := "study.multipart"
	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("RetrieveStudy: %v", err)
	}

	defer resp.Body.Close()

	if resp.StatusCode > 299 {
		return fmt.Errorf("RetrieveStudy: status %d %s: %s", resp.StatusCode, resp.Status, resp.Body)
	}

	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("os.Create: %v", err)
	}
	defer file.Close()
	if _, err := io.Copy(file, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %v", err)
	}

	// When specifying the output file, use an extension like ".multipart".
	// Then, parse the downloaded multipart file to get each individual DICOM
	// file.
	fmt.Fprintf(w, "Study retrieved and downloaded to file: %v\n", outputFile)

	return nil
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;

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();

    // When specifying the output file, use an extension like ".multipart".
    // Then, parse the downloaded multipart file to get each individual
    // DICOM file.
    String outputPath = "study.multipart";
    OutputStream outputStream = new FileOutputStream(new File(outputPath));
    try {
      response.download(outputStream);
      System.out.println("DICOM study written to file " + outputPath);
    } finally {
      outputStream.close();
    }

    if (!response.isSuccessStatusCode()) {
      System.err.print(
          String.format("Exception retrieving DICOM study: %s\n", response.getStatusMessage()));
      throw new RuntimeException();
    }
  }

  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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    HttpHeaders headers = new HttpHeaders();
    // The response's default transfer syntax is Little Endian Explicit.
    // As a result, if the file was uploaded using a compressed transfer syntax,
    // the returned object will be decompressed. This can negatively impact performance and lead
    // to errors for transfer syntaxes that the Cloud Healthcare API doesn't support.
    // To avoid these issues, and if the returned object's transfer syntax doesn't matter to
    // your application, use the
    // multipart/related; type="application/dicom"; transfer-syntax=* Accept Header.
    headers.setAccept("multipart/related; type=application/dicom; transfer-syntax=*");
    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');
const fs = require('fs');
const util = require('util');
const writeFile = util.promisify(fs.writeFile);
// When specifying the output file, use an extension like ".multipart."
// Then, parse the downloaded multipart file to get each individual
// DICOM file.
const fileName = 'study_file.multipart';

const dicomWebRetrieveStudy = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({
    auth,
    headers: {
      Accept: 'multipart/related; type=application/dicom; transfer-syntax=*',
    },
    responseType: 'arraybuffer',
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.3.6.1.4.1.5062.55.1.2270943358.716200484.1363785608958.61.0';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = `studies/${studyUid}`;
  const request = {parent, dicomWebPath};

  const study = await healthcare.projects.locations.datasets.dicomStores.studies.retrieveStudy(
    request
  );

  const fileBytes = Buffer.from(study.data);

  await writeFile(fileName, fileBytes);
  console.log(
    `Retrieved study and saved to ${fileName} in current directory`
  );
};

dicomWebRetrieveStudy();

Python

def dicomweb_retrieve_study(
    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
    )

    # When specifying the output file, use an extension like ".multipart."
    # Then, parse the downloaded multipart file to get each individual
    # DICOM file.
    file_name = "study.multipart"

    # Make an authenticated API request
    session = get_session()

    response = session.get(dicomweb_path)

    response.raise_for_status()

    with open(file_name, "wb") as f:
        f.write(response.content)
        print("Retrieved study and saved to {} in current directory".format(file_name))

    return response

인스턴스 검색

다음 샘플은 인스턴스 검색 방법을 보여줍니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 DICOM 인스턴스를 참조하세요.

인스턴스를 검색할 때 Accept: application/dicom HTTP 헤더를 사용하면 멀티파트 경계를 파싱할 필요가 없습니다. transfer-syntax=*를 추가하면 원래 저장된 형식으로 파일을 반환하여 트랜스코딩이 필요하지 않습니다.

자세한 내용은 projects.locations.datasets.dicomStores.studies.series.instances.retrieveInstance를 참조하세요.

curl

인스턴스를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID, 인스턴스 UID
  • 출력 파일 이름
  • 액세스 토큰

다음 샘플은 curl을 사용하는 GET 요청을 보여줍니다.

curl -X GET \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     -H "Accept: application/dicom; transfer-syntax=*" \
     "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID"
     --output FILENAME.dcm

요청이 성공하면 DICOM 파일이 머신에 기록됩니다.

PowerShell

인스턴스를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID
  • 인스턴스 UID
  • 출력 파일 이름
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 GET 요청을 보여줍니다.

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred"; Accept = "application/dicom; transfer-syntax=*" }

Invoke-RestMethod `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID"
  -OutFile FILENAME.dcm `

요청이 성공하면 DICOM 파일이 머신에 기록됩니다.

Go

import (
	"context"
	"fmt"
	"io"
	"os"

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

// dicomWebRetrieveInstance retrieves a specific instance.
func dicomWebRetrieveInstance(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string, outputFile string) error {
	// projectID := "my-project"
	// location := "us-central1"
	// datasetID := "my-dataset"
	// dicomStoreID := "my-dicom-store"
	// dicomWebPath := "studies/1.3.6.1.4.1.11129.5.5.1113639985/series/1.3.6.1.4.1.11129.5.5.1953511724/instances/1.3.6.1.4.1.11129.5.5.9562821369"
	// outputFile := "instance.dcm"
	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.Series.Instances

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

	call := storesService.RetrieveInstance(parent, dicomWebPath)
	call.Header().Set("Accept", "application/dicom; transfer-syntax=*")
	resp, err := call.Do()
	if err != nil {
		return fmt.Errorf("RetrieveInstance: %v", err)
	}

	defer resp.Body.Close()

	if resp.StatusCode > 299 {
		return fmt.Errorf("RetrieveInstance: status %d %s: %s", resp.StatusCode, resp.Status, resp.Body)
	}

	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("os.Create: %v", err)
	}
	defer file.Close()
	if _, err := io.Copy(file, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %v", err)
	}

	fmt.Fprintf(w, "DICOM instance retrieved and downloaded to file: %v\n", outputFile)

	return nil
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies.Series.Instances;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;

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

  public static void dicomWebRetrieveInstance(String dicomStoreName, String dicomWebPath)
      throws IOException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");
    // String dicomWebPath = String.format(DICOMWEB_PATH, "your-study-id", "your-series-id",
    // "your-instance-id");

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

    // Create request and configure any parameters.
    Instances.RetrieveInstance request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .studies()
            .series()
            .instances()
            .retrieveInstance(dicomStoreName, dicomWebPath);

    // Execute the request and process the results.
    HttpResponse response = request.executeUnparsed();

    String outputPath = "instance.dcm";
    OutputStream outputStream = new FileOutputStream(new File(outputPath));
    try {
      response.download(outputStream);
      System.out.println("DICOM instance written to file " + outputPath);
    } finally {
      outputStream.close();
    }

    if (!response.isSuccessStatusCode()) {
      System.err.print(
          String.format("Exception retrieving DICOM instance: %s\n", response.getStatusMessage()));
      throw new RuntimeException();
    }
  }

  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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    HttpHeaders headers = new HttpHeaders();
    headers.set("X-GFE-SSL", "yes");
    // Avoid parsing multipart boundaries by setting 'application/dicom' HTTP header.
    // Add 'transfer-syntax=*' to avoid transcoding by returning the file in the format it
    // was originally stored in.
    headers.setAccept("application/dicom; transfer-syntax=*");
    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');
const fs = require('fs');
const util = require('util');
const writeFile = util.promisify(fs.writeFile);
const fileName = 'instance_file.dcm';

const dicomWebRetrieveInstance = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({
    auth,
    headers: {Accept: 'application/dicom; transfer-syntax=*'},
    responseType: 'arraybuffer',
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.3.6.1.4.1.5062.55.1.2270943358.716200484.1363785608958.61.0';
  // const seriesUid = '2.24.52329571877967561426579904912379710633';
  // const instanceUid = '1.3.6.2.4.2.14619.5.2.1.6280.6001.129311971280445372188125744148';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = `studies/${studyUid}/series/${seriesUid}/instances/${instanceUid}`;
  const request = {parent, dicomWebPath};

  const instance = await healthcare.projects.locations.datasets.dicomStores.studies.series.instances.retrieveInstance(
    request
  );
  const fileBytes = Buffer.from(instance.data);

  await writeFile(fileName, fileBytes);
  console.log(
    `Retrieved DICOM instance and saved to ${fileName} in current directory`
  );
};

dicomWebRetrieveInstance();

Python

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

    dicom_store_path = "{}/datasets/{}/dicomStores/{}".format(
        url, dataset_id, dicom_store_id
    )

    dicomweb_path = "{}/dicomWeb/studies/{}/series/{}/instances/{}".format(
        dicom_store_path, study_uid, series_uid, instance_uid
    )

    file_name = "instance.dcm"

    # Make an authenticated API request
    session = get_session()

    headers = {"Accept": "application/dicom; transfer-syntax=*"}
    response = session.get(dicomweb_path, headers=headers)
    response.raise_for_status()

    with open(file_name, "wb") as f:
        f.write(response.content)
        print(
            "Retrieved DICOM instance and saved to {} in current directory".format(
                file_name
            )
        )

    return response

소비자 이미지 형식 검색

다음 샘플은 렌더링된 리소스에 대한 Cloud Healthcare API 구현을 사용하여 JPEG 또는 PNG와 같은 소비자 이미징 형식을 검색하는 방법을 보여줍니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 렌더링된 리소스를 참조하세요.

자세한 내용은 projects.locations.datasets.dicomStores.studies.series.instances.retrieveRendered를 참조하세요.

curl

이미지를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID
  • 인스턴스 UID
  • 출력 파일 이름
  • 액세스 토큰

다음 샘플에서는 curl을 사용하여 GET 요청으로 PNG 이미지를 검색하는 방법을 보여줍니다.

curl -X GET \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     -H "Accept: image/png" \
     "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID/rendered"
     --output FILENAME.png

요청이 성공하면 PNG 파일이 머신에 기록됩니다.

PowerShell

이미지를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID
  • 인스턴스 UID
  • 출력 파일 이름
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용하여 GET 요청으로 PNG 이미지를 검색하는 방법을 보여줍니다.

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred"; Accept = "image/png" }

Invoke-RestMethod `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID/rendered"
  -OutFile FILENAME.png `

요청이 성공하면 PNG 파일이 머신에 기록됩니다.

Go

import (
	"context"
	"fmt"
	"io"
	"os"

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

// dicomWebRetrieveRendered retrieves a consumer imaging format like JPEG or PNG.
func dicomWebRetrieveRendered(w io.Writer, projectID, location, datasetID, dicomStoreID, dicomWebPath string, outputFile string) error {
	// projectID := "my-project"
	// location := "us-central1"
	// datasetID := "my-dataset"
	// dicomStoreID := "my-dicom-store"
	// dicomWebPath := "studies/1.3.6.1.4.1.11129.5.5.1113639985/series/1.3.6.1.4.1.11129.5.5.1953511724/instances/1.3.6.1.4.1.11129.5.5.9562821369/rendered"
	// outputFile := "rendered_image.png"
	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.Series.Instances

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

	call := storesService.RetrieveRendered(parent, dicomWebPath)
	call.Header().Set("Accept", "image/png")
	resp, err := call.Do()
	if err != nil {
		return fmt.Errorf("RetrieveRendered: %v", err)
	}

	defer resp.Body.Close()

	if resp.StatusCode > 299 {
		return fmt.Errorf("RetrieveRendered: status %d %s: %s", resp.StatusCode, resp.Status, resp.Body)
	}

	file, err := os.Create(outputFile)
	if err != nil {
		return fmt.Errorf("os.Create: %v", err)
	}
	defer file.Close()
	if _, err := io.Copy(file, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %v", err)
	}

	fmt.Fprintf(w, "Rendered PNG image retrieved and downloaded to file: %v\n", outputFile)

	return nil
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies.Series.Instances;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;

public class DicomWebRetrieveRendered {
  private static final String DICOM_NAME = "projects/%s/locations/%s/datasets/%s/dicomStores/%s";
  private static final String DICOMWEB_PATH = "studies/%s/series/%s/instances/%s/rendered";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void dicomWebRetrieveRendered(String dicomStoreName, String dicomWebPath)
      throws IOException {
    // String dicomStoreName =
    //    String.format(
    //        DICOM_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-dicom-id");
    // String dicomWebPath = String.format(DICOMWEB_PATH, "your-study-id", "your-series-id",
    // "your-instance-id");

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

    // Create request and configure any parameters.
    Instances.RetrieveRendered request =
        client
            .projects()
            .locations()
            .datasets()
            .dicomStores()
            .studies()
            .series()
            .instances()
            .retrieveRendered(dicomStoreName, dicomWebPath);

    // Execute the request and process the results.
    HttpResponse response = request.executeUnparsed();

    String outputPath = "image.png";
    OutputStream outputStream = new FileOutputStream(new File(outputPath));
    try {
      response.download(outputStream);
      System.out.println("DICOM rendered PNG image written to file " + outputPath);
    } finally {
      outputStream.close();
    }

    if (!response.isSuccessStatusCode()) {
      System.err.print(
          String.format(
              "Exception retrieving DICOM rendered image: %s\n", response.getStatusMessage()));
      throw new RuntimeException();
    }
  }

  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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    HttpHeaders headers = new HttpHeaders();
    headers.set("X-GFE-SSL", "yes");
    // Retrieve using the PNG consumer imaging format.
    headers.setAccept("image/png");
    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');
const fs = require('fs');
const util = require('util');
const writeFile = util.promisify(fs.writeFile);
const fileName = 'rendered_image.png';

const dicomWebRetrieveRendered = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({
    auth,
    headers: {Accept: 'image/png'},
    responseType: 'arraybuffer',
  });

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.3.6.1.4.1.5062.55.1.2270943358.716200484.1363785608958.61.0';
  // const seriesUid = '2.24.52329571877967561426579904912379710633';
  // const instanceUid = '1.3.6.2.4.2.14619.5.2.1.6280.6001.129311971280445372188125744148';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = `studies/${studyUid}/series/${seriesUid}/instances/${instanceUid}/rendered`;
  const request = {parent, dicomWebPath};

  const rendered = await healthcare.projects.locations.datasets.dicomStores.studies.series.instances.retrieveRendered(
    request
  );
  const fileBytes = Buffer.from(rendered.data);

  await writeFile(fileName, fileBytes);
  console.log(
    `Retrieved rendered image and saved to ${fileName} in current directory`
  );
};

dicomWebRetrieveRendered();

Python

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

    dicom_store_path = "{}/datasets/{}/dicomStores/{}".format(
        url, dataset_id, dicom_store_id
    )

    instance_path = "{}/dicomWeb/studies/{}/series/{}/instances/{}".format(
        dicom_store_path, study_uid, series_uid, instance_uid
    )

    dicomweb_path = "{}/rendered".format(instance_path)

    file_name = "rendered_image.png"

    # Make an authenticated API request
    session = get_session()

    headers = {"Accept": "image/png"}
    response = session.get(dicomweb_path, headers=headers)
    response.raise_for_status()

    with open(file_name, "wb") as f:
        f.write(response.content)
        print(
            "Retrieved rendered image and saved to {} in current directory".format(
                file_name
            )
        )

    return response

메타데이터 검색

연구 또는 시리즈에서 모든 인스턴스에 대해 메타데이터를 검색할 수 있습니다. 다음 샘플은 인스턴스에 대해 메타데이터를 검색하는 방법을 보여줍니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 명세에서 메타데이터 리소스를 참조하세요.

자세한 내용은 projects.locations.datasets.dicomStores.studies.series.instances.retrieveMetadata를 참조하세요. retrieveMetadata를 호출하면 includefield=all 쿼리 매개변수로 인스턴스를 검색할 때 반환되는 것과 동일한 필드 집합이 메서드로 반환됩니다. 애플리케이션이 지연 시간에 민감하고 모든 필드 대신 특정 필드 집합에 대해 메타데이터를 검색하려는 경우에는 retrieveMetadata를 호출하지 마세요. 대신 searchForInstances 메서드 중 하나를 호출하고 필드를 지정합니다. 더 작은 필드 집합이 응답되고 작은 필드 집합은 지연 시간에 민감한 애플리케이션에 도움이 됩니다.

curl

일괄 데이터가 삭제된 인스턴스에 대해 메타데이터를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID
  • 인스턴스 UID
  • 액세스 토큰

다음 샘플은 curl을 사용하는 GET 요청을 보여줍니다.

curl -X GET \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID/metadata"

요청이 성공하면 JSON 형식의 인스턴스에 있는 DICOM 속성에 대한 메타데이터가 응답에 포함됩니다.

[
  {
    "00020002": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020003": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020010": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020012": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020013": {
      "vr": "SH",
      "Value": [
        "OFFIS_DCMTK_"
      ]
    },
    "00080016": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    ...
]

PowerShell

일괄 데이터가 삭제된 인스턴스에 대해 메타데이터를 검색하려면 GET 요청을 수행하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구 고유 식별자(UID)
  • 시리즈 UID
  • 인스턴스 UID
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용한 GET 요청을 보여줍니다.

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

Invoke-RestMethod `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID/metadata"

요청이 성공하면 JSON 형식의 인스턴스에 있는 DICOM 속성에 대한 메타데이터가 응답에 포함됩니다.

[
  {
    "00020002": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020003": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020010": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020012": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    "00020013": {
      "vr": "SH",
      "Value": [
        "OFFIS_DCMTK_"
      ]
    },
    "00080016": {
      "vr": "UI",
      "Value": [
        "UNIQUE_IDENTIFIER"
      ]
    },
    ...
]

DICOMweb CLI 사용

다음 샘플에서는 Cloud Healthcare API DICOMweb CLI를 사용하여 DICOM 저장소에서 모든 인스턴스를 검색하고 머신에서 현재 작업 디렉터리에 저장하는 방법을 보여줍니다. DICOMweb CLI GitHub 저장소에는 사용 가능한 샘플이 더 있습니다.

dcmweb \
  https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb \
  retrieve

요청이 성공하면 서버가 다음과 비슷한 응답을 반환하고 DICOM 파일이 머신에 기록됩니다.

TIMESTAMP -- Saving files into ./
TIMESTAMP -- Transferred SIZE in COUNT files

연구, 시리즈, 인스턴스 삭제

Cloud Healthcare API는 DICOM 연구, 시리즈, 인스턴스를 삭제하기 위한 독점 웹 서비스를 구현합니다. 이 서비스는 DICOMweb 표준 서비스의 일부가 아닙니다. 자세한 내용은 Cloud Healthcare API DICOM 적합성 문의 삭제 섹션을 참조하세요.

연구 및 시리즈의 삭제 요청은 장기 실행 작업을 반환합니다. 작업이 완료되면 연구 또는 시리즈의 모든 인스턴스가 삭제됩니다.

인스턴스의 삭제 요청은 장기 실행 작업을 반환하지 않으며 대신 다음과 같이 빈 응답 본문을 반환합니다.

{}

다음 샘플은 DICOM 연구를 삭제하는 방법을 보여줍니다. 자세한 내용은 projects.locations.datasets.dicomStores.studies.delete를 참조하세요.

curl

DICOM 연구를 삭제하려면 DELETE 요청을 생성하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구의 고유 식별자(UID)
  • 액세스 토큰

다음 샘플은 curl을 사용하여 DELETE 요청으로 연구를 삭제하는 방법을 보여줍니다.

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

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

응답에 작업 이름이 포함됩니다. 작업 상태를 추적하려면 작업 get 메서드를 사용합니다.

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"

요청이 성공하면 서버는 JSON 형식의 작업 상태가 포함된 응답을 반환합니다.

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.dicomweb.DicomWebService.DeleteStudy",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/viewer/CLOUD_LOGGING_URL",
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.protobuf.Empty",
  }
}

PowerShell

DICOM 연구를 삭제하려면 DELETE 요청을 생성하고 다음 정보를 지정합니다.

  • 상위 데이터 세트의 이름
  • DICOM 저장소의 이름
  • 연구의 고유 식별자(UID)
  • 액세스 토큰

다음 샘플은 Windows PowerShell을 사용하여 DELETE 요청으로 연구를 삭제하는 방법을 보여줍니다.

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

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

요청이 성공하면 서버가 JSON 형식으로 응답을 반환합니다.

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

응답에 작업 이름이 포함됩니다. 작업 상태를 추적하려면 작업 get 메서드를 사용합니다.

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

Invoke-WebRequest `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID" | Select-Object -Expand Content

요청이 성공하면 서버는 JSON 형식의 작업 상태가 포함된 응답을 반환합니다.

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.dicomweb.DicomWebService.DeleteStudy",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/viewer/CLOUD_LOGGING_URL",
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.protobuf.Empty",
  }
}

Go

import (
	"context"
	"fmt"
	"io"

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

// 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
}

자바

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.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.DicomStores.Studies;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
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
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          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

const {google} = require('googleapis');
const healthcare = google.healthcare('v1');

const dicomWebDeleteStudy = async () => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  google.options({auth});

  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const dicomStoreId = 'my-dicom-store';
  // const studyUid = '1.3.6.1.4.1.5062.55.1.2270943358.716200484.1363785608958.61.0';
  const parent = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/dicomStores/${dicomStoreId}`;
  const dicomWebPath = `studies/${studyUid}`;
  const request = {parent, dicomWebPath};

  await healthcare.projects.locations.datasets.dicomStores.studies.delete(
    request
  );
  console.log('Deleted DICOM study');
};

dicomWebDeleteStudy();

Python

def dicomweb_delete_study(
    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()

    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

DICOMweb CLI 사용

다음 샘플은 Cloud Healthcare API DICOMweb CLI를 사용하여 연구를 삭제하는 방법을 보여줍니다.

dcmweb \
    https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb \
   delete studies/STUDY_UID

요청이 성공하면 서버는 삭제 도구가 완료될 때까지 CLI 도구가 폴링하는 작업을 반환합니다.