Annoter des données anonymisées

Cette page explique comment configurer des magasins et des enregistrements d'annotations lors de l'anonymisation des données FHIR et DICOM sensibles.

Présentation de l'annotation des données anonymisées

Chaque fois que vous anonymisez des données FHIR ou DICOM sensibles, vous pouvez générer des informations sur les données sensibles qui ont été supprimées d'un magasin d'annotations. Ces informations sont stockées dans un ou plusieurs enregistrements d'annotations dans le magasin d'annotations.

Vous pouvez créer le magasin d'annotations dans un ensemble de données existant ou le créer dans un nouvel ensemble de données lors de l'opération d'anonymisation. Si vous créez le magasin d'annotations dans un ensemble de données existant, un magasin d'annotations du même nom ne peut pas exister dans cet ensemble de données.

Le magasin d'annotations créé doit se trouver dans le même projet que les données sources anonymisées. Par exemple, vous ne pouvez pas à la fois anonymiser des données dans un projet et générer des enregistrements d'annotations dans un magasin d'annotations d'un autre projet.

Pour spécifier un magasin d'annotations et son comportement lors de l'anonymisation, définissez le champ annotation_store_name dans un objet annotation de l'objet DeidentifyConfig.

Vous pouvez éventuellement définir le champ store_quote en fonction de votre cas d'utilisation. Vous trouverez des informations sur la définition du champ store_quote dans la section suivante.

Définir le champ store_quote

Les informations suivantes s'appliquent aux données FHIR et DICOM.

Lorsque le champ store_quote dans l'objet annotation de la requête est défini sur true, les valeurs originelles de l'anonymisation des données s'affichent dans le champ quote de l'enregistrement d'annotation. Exemple :

  • Si un élément DATE est anonymisé et que store_quote est défini sur true, les informations suivantes s'affichent dans l'enregistrement d'annotation:

    • La valeur de la date (par exemple 1980-12-05), affichée dans le champ quote
    • L'infoType DATE
    • L'emplacement de départ et de fin dans lesquels les données ont été trouvées. Ces emplacements utilisent un index basé sur zéro et sont tous deux inclus.
  • Si le champ store_quote est défini sur false, la date (1980-12-05) ne s'affiche pas dans l'enregistrement d'annotation et seules les informations suivantes s'affichent:

    • L'infoType DATE
    • L'emplacement de départ et de fin dans lesquels les données ont été trouvées. Ces emplacements utilisent un index basé sur zéro et sont tous deux inclus.

Annotations de données FHIR anonymisées

Cette section s'appuie sur les concepts expliqués dans la section Anonymiser des données FHIR à l'aide de l'API Cloud Healthcare.

Structure des enregistrements d'annotations

L'opération d'anonymisation crée un enregistrement d'annotation pour chaque ressource FHIR anonymisée. Chaque enregistrement d'annotation contient un objet textAnnotation qui comporte des informations sur les données anonymisées qui ont été inspectées et transformées. Pour qu'un champ anonymisé s'affiche dans l'enregistrement d'annotation, l'action INSPECT_AND_TRANSFORM Action doit lui avoir été appliquée.

Configurer des annotations de données FHIR anonymisées

Les exemples suivants utilisent l'anonymisation de données FHIR par défaut comme point de départ. Ils montrent comment anonymiser une ressource Patient à l'aide de la méthode FHIR par défaut et comment stocker des informations sur les données anonymisées dans un enregistrement d'annotation d'un nouveau magasin d'annotations. Dans ces exemples, le champ store_quote est défini sur true, ce qui signifie que l'enregistrement d'annotation généré contient les valeurs originelles des données anonymisées.

Le nouveau magasin d'annotations se trouve dans l'ensemble de données créé par l'opération d'anonymisation, mais vous pouvez également le créer dans un ensemble de données existant.

curl

curl -X POST \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    --data "{
      'destinationDataset': 'projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID',
      'config': {
        'fhir': {},
        'annotation': {
          'annotation_store_name': 'projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID',
          'store_quote': 'true'
        }
      }
    }" "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID:deidentify"

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID"
}

La réponse contient un nom d'opération. Vous pouvez suivre l'état de l'opération à l'aide de la méthode Operation get :

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID"

Si la requête aboutit, le serveur renvoie la réponse au format JSON. Une fois le processus d'anonymisation terminé, la réponse contient "done": true.

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1beta1.dataset.DatasetService.DeidentifyDataset",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME"
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.deidentify.DeidentifySummary",
    "successStoreCount": "1",
    "successResourceCount": "1"
  }
}

Après avoir vérifié la réussite de l'opération d'anonymisation, vous pouvez répertorier les magasins d'annotations de l'ensemble de données et vérifier si le magasin d'annotations a bien été créé au cours de l'opération:

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

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "annotationStores": [
    {
      "name": "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID"
    },
    {
      ...
    }
  ]
}

Utilisez la valeur ANNOTATION_STORE_ID pour répertorier les enregistrements du magasin d'annotations:

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations"

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "annotations": [
    "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/ANNOTATION_RECORD_ID",
    ...
  ]
}

Utilisez la valeur ANNOTATION_RECORD_ID pour afficher l'enregistrement de l'annotation:

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/ANNOTATION_RECORD_ID"

Si la requête aboutit, le serveur renvoie la réponse au format JSON.

L'objet textAnnotation contient des informations sur le texte sensible supprimé par l'opération d'anonymisation. Dans le champ details, vous pouvez constater que l'opération a recherché l'objet patient.text.div et trouvé quatre infoTypes, ainsi que leurs valeurs et les emplacements où ils ont été trouvés.

Lorsque vous utilisez l'anonymisation de données FHIR par défaut, les seules données inspectées et transformées sont celles de l'objet patient.text.div. Toutes les autres données anonymisées sont transformées sans être inspectées, car leur infoType a déjà été déclaré dans la ressource FHIR d'origine.

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/ANNOTATION_RECORD_ID",
  "annotationSource": {
    "cloudHealthcareSource": {
      "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Patient/PATIENT_ID"
    }
  },
  "textAnnotation": {
    "details": {
      "patient.text.div": {
        "findings": [
          {
            "infoType": "PERSON_NAME",
            "start": "42",
            "end": "54",
            "quote": "Smith, Darcy"
          },
          {
            "infoType": "PERSON_NAME",
            "start": "42",
            "end": "47",
            "quote": "Smith"
          },
          {
            "infoType": "PERSON_NAME",
            "start": "49",
            "end": "54",
            "quote": "Darcy"
          },
          {
            "infoType": "DATE",
            "start": "81",
            "end": "91",
            "quote": "1980-12-05"
          }
        ]
      }
    }
  }
}

Annotations de données DICOM anonymisées

Cette section s'appuie sur les concepts expliqués dans la section Anonymiser des données FHIR à l'aide de l'API Cloud Healthcare.

Structure des enregistrements d'annotations

L'opération d'anonymisation crée deux types d'enregistrements d'annotations pour les données DICOM anonymisées. Ces deux types d'enregistrements d'annotations sont les suivants:

  • Enregistrements d'annotations de texte: contiennent des métadonnées, telles que des balises DICOM, provenant des données anonymisées. Chaque enregistrement d'annotation de texte contient un objet textAnnotation comportant des informations sur les données anonymisées qui ont été inspectées et transformées. Pour qu'une balise anonymisée apparaisse dans l'enregistrement d'annotation, elle doit avoir été inspectée afin de détecter des données de santé protégées (PHI) en fonction de la configuration fournie dans le champ TagFilterProfile. Par exemple, les exemples de la section Configurer des annotations de données DICOM anonymisées utilisent la configuration DEIDENTIFY_TAG_CONTENTS.
  • Enregistrements d'annotations d'images: contiennent l'emplacement des informations sensibles dans des cadres DICOM individuels. Chaque enregistrement d'annotation d'image contient un objet ImageAnnotation comportant les coordonnées des informations sensibles trouvées.

L'opération d'anonymisation crée des enregistrements d'annotations pour chaque cadre dans une instance DICOM. Par exemple, si une instance DICOM comporte trois cadres, l'opération d'anonymisation crée les enregistrements d'annotation suivants:

  • Un enregistrement d'annotation de texte, contenant textAnnotation, pour les balises DICOM de l'instance DICOM.
  • Trois enregistrements d'annotations d'images, chacun contenant une annotation imageAnnotation, pour chacun des trois cadres. Chaque enregistrement d'annotation d'image contient un champ frame_index pour indiquer le cadre auquel l'enregistrement correspond.

Les quatre enregistrements d'annotations ont tous la même valeur cloudHealthcareSource.name, qui correspond au chemin d'accès à l'instance DICOM au format suivant: projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID.

Configurer des annotations de données DICOM anonymisées

Les exemples suivants montre utilisent une approche permettant de combiner l'anonymisation des balises et le masquage de texte incrusté comme point de départ. Ils montrent comment anonymiser une instance DICOM en masquant le texte incrusté dans une image, et comment inspecter et transformer le texte sensible. Ces exemples montrent également comment stocker des informations sur les données anonymisées dans un enregistrement d'annotation d'un nouveau magasin d'annotations. Dans ces exemples, le champ store_quote est défini sur true, ce qui signifie que l'enregistrement d'annotation généré contient les valeurs originelles des données anonymisées.

Le nouveau magasin d'annotations se trouve dans l'ensemble de données créé par l'opération d'anonymisation, mais vous pouvez également le créer dans un ensemble de données existant.

curl

curl -X POST \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    --data "{
      'destinationDataset': 'projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID',
      'config': {
        'dicom': {
          'filterProfile': 'DEIDENTIFY_TAG_CONTENTS'
        },
        'image': {
          'textRedactionMode': 'REDACT_ALL_TEXT'
        },
        'annotation': {
          'annotation_store_name': 'projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID',
          'store_quote': 'true'
        }
      }
    }" "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID:deidentify"

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID"
}

La réponse contient un nom d'opération. Vous pouvez suivre l'état de l'opération à l'aide de la méthode Operation get :

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID"

Si la requête aboutit, le serveur renvoie la réponse au format JSON. Une fois le processus d'anonymisation terminé, la réponse contient "done": true.

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1beta1.dataset.DatasetService.DeidentifyDataset",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME"
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.deidentify.DeidentifySummary",
    "successStoreCount": "1",
    "successResourceCount": "1"
  }
}

Après avoir vérifié la réussite de l'opération d'anonymisation, vous pouvez répertorier les magasins d'annotations de l'ensemble de données et vérifier si le magasin d'annotations a bien été créé au cours de l'opération:

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

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "annotationStores": [
    {
      "name": "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID"
    },
    {
      ...
    }
  ]
}

Utilisez la valeur ANNOTATION_STORE_ID pour répertorier les enregistrements du magasin d'annotations:

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations"

Si la requête aboutit, le serveur renvoie la réponse au format JSON :

{
  "annotations": [
    "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/TEXT_ANNOTATION_RECORD_ID",
    "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/IMAGE_ANNOTATION_RECORD_ID",
    ...
  ]
}

Vous pouvez constater que deux enregistrements d'annotation ont été créés: un enregistrement d'annotation de texte et un enregistrement d'annotation d'image.

Tout d'abord, utilisez la valeur TEXT_ANNOTATION_RECORD_ID pour afficher l'enregistrement d'annotation de texte :

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/TEXT_ANNOTATION_RECORD_ID"

Si la requête aboutit, le serveur renvoie la réponse au format JSON.

L'objet textAnnotation contient des informations sur le texte sensible supprimé lors de l'opération d'anonymisation. Dans le champ details, vous pouvez constater que l'opération a fourni une liste de tags DICOM. Lorsqu'une balise DICOM est trouvée, ses informations sont fournies dans l'objet findings, qui indique l'infoType, la valeur de l'infoType et les emplacements où les valeurs ont été trouvées.

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/TEXT_ANNOTATION_RECORD_ID",
  "annotationSource": {
    "cloudHealthcareSource": {
      "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID"
    }
  },
  "textAnnotation": {
    "details": {
      "00080070": {},
      "00080090": {
        "findings": [
          {
            "infoType": "PERSON_NAME",
            "end": "8",
            "quote": "John Doe"
          }
        ]
      },
      "00081090": {},
      "00100010": {
        "findings": [
          {
            "infoType": "PERSON_NAME",
            "end": "11",
            "quote": "Ann Johnson"
          }
        ]
      },
      "00100020": {},
      "00100030": {
        "findings": [
          {
            "infoType": "DATE",
            "end": "8",
            "quote": "19880812"
          }
        ]
      },
      "00020013": {
        "findings": [
          {
            "infoType": "LOCATION",
            "end": "5",
            "quote": "OFFIS"
          }
        ]
      },
      "00080020": {
        "findings": [
          {
            "infoType": "DATE",
            "end": "8",
            "quote": "20110909"
          }
        ]
      }
    }
  }
}

Utilisez ensuite la valeur IMAGE_ANNOTATION_RECORD_ID pour afficher l'enregistrement d'annotation d'image:

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/REGION/datasets/DATASET_ID/annotationStores/ANNOTATION_STORE_ID//annotations/IMAGE_ANNOTATION_RECORD_ID"

Si la requête aboutit, le serveur renvoie la réponse au format JSON.

Dans l'objet imageAnnotation, il existe plusieurs sommets vertices, chacun contenant quatre points X/Y qui relient les emplacements où l'opération d'anonymisation a détecté des données d'image sensibles et du texte incrusté.

{
  "name": "projects/PROJECT_ID/locations/REGION/datasets/DESTINATION_DATASET_ID/annotationStores/ANNOTATION_STORE_ID/annotations/IMAGE_ANNOTATION_RECORD_ID",
  "annotationSource": {
    "cloudHealthcareSource": {
      "name": "projects/PROJECT_ID/locations/REGION/datasets/SOURCE_DATASET_ID/dicomStores/DICOM_STORE_ID/dicomWeb/studies/STUDY_UID/series/SERIES_UID/instances/INSTANCE_UID"
    }
  },
  "imageAnnotation": {
    "boundingPolys": [
      {
        "vertices": [
          {
            "x": 439,
            "y": 919
          },
          {
            "x": 495,
            "y": 919
          },
          {
            "x": 495,
            "y": 970
          },
          {
            "x": 439,
            "y": 970
          }
        ]
      },
      {
        "vertices": [
          {
            "x": 493,
            "y": 919
          },
          {
            "x": 610,
            "y": 919
          },
          {
            "x": 610,
            "y": 972
          },
          {
            "x": 493,
            "y": 972
          }
        ]
      },
      {
        "vertices": [
        ...
        ]
      },
      ...
    ]
  }
}