Skip to content

Commit

Permalink
Vendor the deid-redactor library (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
craigatron authored Aug 1, 2024
1 parent eb64294 commit 9cbc434
Show file tree
Hide file tree
Showing 13 changed files with 1,367 additions and 2 deletions.
4 changes: 2 additions & 2 deletions import/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dependencies {
compile 'com.google.guava:guava:28.1-jre'
compile 'com.google.auth:google-auth-library-oauth2-http'
compile "com.google.api-client:google-api-client"

compile "com.beust:jcommander:1.72"
compile "org.dcm4che:dcm4che-core:5.31.2"
compile "org.dcm4che:dcm4che-net:5.31.2"
Expand All @@ -77,7 +77,7 @@ dependencies {
compile project(":dicom_util")
compile project(":util")

compile "com.github.red1408:deid-redactor:233908ca"
compile project(":deid-redactor")

compile "com.google.cloud:google-cloud-storage:1.98.0"
compile 'com.google.cloud:google-cloud-nio:0.116.0-alpha'
Expand Down
4 changes: 4 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ include ":import"
include ":util"
include ':dicom_util'
include ':jai-imageio-jpeg2000'
include ':deid-redactor'
include ':deid-redactor-examples'
project(':jai-imageio-jpeg2000').projectDir = new File('third_party/jai-imageio-jpeg2000')
project(':deid-redactor').projectDir = new File('third_party/deid-redactor/lib')
project(':deid-redactor-examples').projectDir = new File('third_party/deid-redactor/examples/tag_remover')
51 changes: 51 additions & 0 deletions third_party/deid-redactor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# DICOM Redactor Library

The DICOM Redactor Library is a Java library designed to redact sensitive data contained in DICOM tags. This is an offline library which does not communicate with Google Cloud.

## Getting Started

### Building

The DICOM redactor library can be built using [Gradle](https://gradle.org/). Please refer to these [instructions](https://gradle.org/install/) to build Gradle for your system.

To build the library and examples:

```shell
cd redactor
./gradlew build
```

### Running unit tests

```shell
cd redactor
./gradlew test
```

## Configuration

The library's redaction is primarily configured using [protobuf](https://developers.google.com/protocol-buffers/). The configuration is similar to the [DicomConfig](https://cloud.google.com/healthcare/docs/reference/rpc/google.cloud.healthcare.v1beta1/deidentify#dicomconfig) for the deidentify operation in Google's Cloud Healthcare API (although the predefined filter profiles differ).

The user can configure which tags to redact/remove in one of 3 ways:

1. keep_list - a list of DICOM tags to keep untouched. Other tags are removed.
2. remove_list - a list of DICOM tags to remove. Other tags are kept untouched.
3. filter_profile - a predefined profile that will keep and remove particular tags.

See the full configuration [proto](lib/src/main/proto/DicomConfig.proto) for more info.

To view the sepcific tags removed for a certain profile, see the relevant [textproto](lib/src/main/resource/chc_basic.textproto).

## UID Regeneration

Regardless of the configuration, several UIDs will always be regenerated: SOPInstanceUID, StudyInstanceUID, SeriesInstanceUID, and MediaStorageSOPInstanceUID.
By default, these will be regenerated using the [UUID Derived UID](http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_B.2.html) method. Optionally, when constucting a redactor, the user can specify their own prefix to use for the regenerated UIDs.

## Sample script

A [command line utility](examples) for redacting tags using the library has been included. To run:

```shell
cd redactor
./gradlew examples:tag_remover:run --args='-i in.dcm -o out.dcm -t PatientName'
```
46 changes: 46 additions & 0 deletions third_party/deid-redactor/examples/tag_remover/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id 'java'
id 'application'
}

repositories {
google()
jcenter()
mavenCentral()
maven { url 'http://www.dcm4che.org/maven2/' }
}

buildDir = "/tmp/gradle_build/redactor/examples"

dependencies {
implementation project(':deid-redactor')
implementation 'commons-cli:commons-cli:1.4'

testImplementation 'junit:junit:4.12'
testImplementation 'com.google.truth:truth:0.39'
testImplementation 'commons-io:commons-io:2.6'
}

mainClassName = 'com.google.cloud.healthcare.deid.remover.TagRemover'

jar {
manifest {
attributes 'Main-Class': mainClassName
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2019 Google LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.healthcare.deid.remover;

import com.google.cloud.healthcare.deid.redactor.DicomRedactor;
import com.google.cloud.healthcare.deid.redactor.protos.DicomConfigProtos.DicomConfig;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
* TagRemover is a command line utility that removes tags from DICOM files.
*/
final class TagRemover {

public static void main(String[] args) throws Exception {
Options options = new Options();
Option input = new Option("i", "input", true, "input DICOM file path");
input.setRequired(true);
options.addOption(input);
Option output = new Option("o", "output", true, "output DICOM file path");
output.setRequired(true);
options.addOption(output);
Option tags = new Option("t", "tags", true, "DICOM tags to redact");
tags.setRequired(true);
tags.setArgs(Option.UNLIMITED_VALUES);
options.addOption(tags);

CommandLineParser parser = new DefaultParser();
HelpFormatter formatter = new HelpFormatter();
CommandLine cmd;

try {
cmd = parser.parse(options, args);
} catch (ParseException e) {
System.out.println(e.getMessage());
formatter.printHelp("tagremove", options);
return;
}

InputStream is =
new BufferedInputStream(new FileInputStream(new File(cmd.getOptionValue("input"))));
OutputStream os =
new BufferedOutputStream(new FileOutputStream(new File(cmd.getOptionValue("output"))));

List<String> tagList = Arrays.asList(cmd.getOptionValues("tags"));
DicomConfig config = DicomConfig.newBuilder().setRemoveList(
DicomConfig.TagFilterList.newBuilder().addAllTags(tagList)).build();

DicomRedactor redactor = new DicomRedactor(config);
redactor.redact(is, os);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2019 Google LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.healthcare.deid.remover;

import java.io.File;
import org.apache.commons.io.FileUtils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Test basic DICOM tag removal. */
@RunWith(JUnit4.class)
public final class TagRemoverTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void basicRedaction() throws Exception {
File expectedFile = new File(
TagRemoverTest.class.getClassLoader().getResource("basic-redacted.dcm").getFile());
File inFile = new File(
TagRemoverTest.class.getClassLoader().getResource("basic.dcm").getFile());
FileUtils.copyFileToDirectory(inFile, folder.getRoot());
String inPath = folder.getRoot() + "/basic.dcm";
String outPath = folder.getRoot() + "/basic-redacted.dcm";
String[] args = new String[]{"-i", inPath, "-o", outPath, "-t", "PatientName", "00081080"};
TagRemover.main(args);
Assert.assertTrue(FileUtils.contentEquals(expectedFile, new File(outPath)));
}
}
Binary file not shown.
Binary file not shown.
54 changes: 54 additions & 0 deletions third_party/deid-redactor/lib/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id 'java-library'
id 'com.google.protobuf' version "0.8.13"
id 'maven'
}

repositories {
google()
jcenter()
mavenCentral()
maven { url 'http://www.dcm4che.org/maven2/' }
}

group = "com.google.cloud.healthcare.deid"
archivesBaseName = "redactor"
version = "0.1.0"

buildDir = "/tmp/gradle_build/redactor"

dependencies {
implementation "org.dcm4che:dcm4che-core:3.3.8"
implementation "org.dcm4che:dcm4che-imageio:3.3.8"

protobuf 'com.google.protobuf:protobuf-java:3.7.1'

api "com.google.api.grpc:proto-google-common-protos:1.16.0"

testImplementation 'junit:junit:4.12'
testImplementation 'com.google.truth:truth:0.39'
testImplementation 'commons-io:commons-io:2.6'
}

protobuf {
generatedFilesBaseDir = "/tmp/gradle_build/redactor/gen"
protoc {
artifact = "com.google.protobuf:protoc:3.7.1";
}
}
Loading

0 comments on commit 9cbc434

Please sign in to comment.