Skip to content

Commit

Permalink
Migrate from BadMedicine to SynthEHR (#222)
Browse files Browse the repository at this point in the history
* migrate to useing synthehr
---------

Co-authored-by: James A Sutherland <[email protected]>
  • Loading branch information
JFriel and jas88 authored May 16, 2024
1 parent d8c8354 commit 9c18d17
Show file tree
Hide file tree
Showing 12 changed files with 41 additions and 35 deletions.
Empty file added .gitmodules
Empty file.
2 changes: 1 addition & 1 deletion BadDicom/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using BadMedicine;
using BadMedicine.Dicom;
using CommandLine;
using System;
Expand All @@ -19,6 +18,7 @@
using FAnsi.Implementations.Oracle;
using FAnsi.Implementations.PostgreSql;
using YamlDotNet.Serialization;
using SynthEHR;

namespace BadDicom;

Expand Down
29 changes: 15 additions & 14 deletions BadMedicine.Dicom.Tests/DicomDataGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using CsvHelper;
using SynthEHR;

namespace BadMedicine.Dicom.Tests;

Expand All @@ -14,7 +15,7 @@ public class DicomDataGeneratorTests
public void Test_CreatingOnDisk_OneFile()
{
var r = new Random(500);
using var generator = new DicomDataGenerator(r, TestContext.CurrentContext.WorkDirectory) {Layout = FileSystemLayout.StudyUID, MaximumImages = 1};
using var generator = new DicomDataGenerator(r, TestContext.CurrentContext.WorkDirectory) { Layout = FileSystemLayout.StudyUID, MaximumImages = 1 };


var person = new Person(r);
Expand Down Expand Up @@ -72,10 +73,10 @@ public void Test_CreatingInMemory_ModalityCT()
{
var r = new Random(23);
var person = new Person(r);
using var generator = new DicomDataGenerator(r,new string(TestContext.CurrentContext.WorkDirectory),"CT") {NoPixels = true};
using var generator = new DicomDataGenerator(r, new string(TestContext.CurrentContext.WorkDirectory), "CT") { NoPixels = true };

//generate 100 images
for(var i = 0 ; i < 100 ; i++)
for (var i = 0; i < 100; i++)
{
//all should be CT because we said CT only
var ds = generator.GenerateTestDataset(person, r);
Expand All @@ -89,7 +90,7 @@ public void Test_Anonymise()
var r = new Random(23);
var person = new Person(r);

using var generator = new DicomDataGenerator(r,new string(TestContext.CurrentContext.WorkDirectory),"CT");
using var generator = new DicomDataGenerator(r, new string(TestContext.CurrentContext.WorkDirectory), "CT");

// without anonymisation (default) we get the normal patient ID
var ds = generator.GenerateTestDataset(person, r);
Expand Down Expand Up @@ -118,24 +119,24 @@ public void Test_CreatingInMemory_Modality_CTAndMR()
var r = new Random(23);
var person = new Person(r);

using var generator = new DicomDataGenerator(r,new string(TestContext.CurrentContext.WorkDirectory),"CT","MR");
using var generator = new DicomDataGenerator(r, new string(TestContext.CurrentContext.WorkDirectory), "CT", "MR");

//generate 100 images
for(var i = 0 ; i < 100 ; i++)
for (var i = 0; i < 100; i++)
{
//all should be CT because we said CT only
var ds = generator.GenerateTestDataset(person, r);
var modality = ds.GetSingleValue<string>(DicomTag.Modality);

Assert.That(modality is "CT" or "MR",$"Unexpected modality {modality}");
Assert.That(modality is "CT" or "MR", $"Unexpected modality {modality}");
}
}

[Test]
public void TestFail_CreatingInMemory_Modality_Unknown()
{
var r = new Random(23);
Assert.Throws<ArgumentException>(()=>_=new DicomDataGenerator(r,new string(TestContext.CurrentContext.WorkDirectory),"LOLZ"));
Assert.Throws<ArgumentException>(() => _ = new DicomDataGenerator(r, new string(TestContext.CurrentContext.WorkDirectory), "LOLZ"));

}

Expand All @@ -144,37 +145,37 @@ public void Test_CsvOption()
{
var r = new Random(500);

var outputDir = new DirectoryInfo(Path.Combine(TestContext.CurrentContext.WorkDirectory,nameof(Test_CsvOption)));
var outputDir = new DirectoryInfo(Path.Combine(TestContext.CurrentContext.WorkDirectory, nameof(Test_CsvOption)));
if (outputDir.Exists)
outputDir.Delete(true);
outputDir.Create();

var people = new PersonCollection();
people.GeneratePeople(100,r);
people.GeneratePeople(100, r);

using (var generator = new DicomDataGenerator(r,outputDir.FullName, "CT"))
using (var generator = new DicomDataGenerator(r, outputDir.FullName, "CT"))
{
generator.Csv = true;
generator.NoPixels = true;
generator.MaximumImages = 500;

generator.GenerateTestDataFile(people,new FileInfo(Path.Combine(outputDir.FullName,"index.csv")),500);
generator.GenerateTestDataFile(people, new FileInfo(Path.Combine(outputDir.FullName, "index.csv")), 500);
}

//3 csv files + index.csv (the default one
Assert.That(outputDir.GetFiles(), Has.Length.EqualTo(4));

foreach (var f in outputDir.GetFiles())
{
using var reader = new CsvReader(new StreamReader(f.FullName),CultureInfo.CurrentCulture);
using var reader = new CsvReader(new StreamReader(f.FullName), CultureInfo.CurrentCulture);
var rowcount = 0;

//confirms that the CSV is intact (no dodgy commas, unquoted newlines etc)
while (reader.Read())
rowcount++;

//should be 1 row per image + 1 for header
if(f.Name == DicomDataGenerator.ImageCsvFilename)
if (f.Name == DicomDataGenerator.ImageCsvFilename)
Assert.That(rowcount, Is.EqualTo(501));
}
}
Expand Down
1 change: 1 addition & 0 deletions BadMedicine.Dicom.Tests/StudyTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using FellowOakDicom;
using NUnit.Framework;
using SynthEHR;
using System;

namespace BadMedicine.Dicom.Tests;
Expand Down
4 changes: 2 additions & 2 deletions BadMedicine.Dicom/BadMedicine.Dicom.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand Down Expand Up @@ -44,7 +44,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="HIC.BadMedicine" Version="1.2.1" />
<PackageReference Include="HIC.SynthEHR" Version="2.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.3" />
</ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions BadMedicine.Dicom/DicomDataGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using BadMedicine.Datasets;
using SynthEHR.Datasets;
using FellowOakDicom;
using System;
using System.IO;
Expand All @@ -7,6 +7,7 @@
using System.Globalization;
using System.Runtime.InteropServices;
using CsvHelper;
using SynthEHR;

namespace BadMedicine.Dicom;

Expand Down Expand Up @@ -225,7 +226,7 @@ public DicomDataGenerator(Random r, string? outputDir, params string[] modalitie
}

/// <summary>
/// Returns headers for the inventory file produced during <see cref="GenerateTestDataset(BadMedicine.Person,System.Random)"/>
/// Returns headers for the inventory file produced during <see cref="GenerateTestDataset(SynthEHR.Person,System.Random)"/>
/// </summary>
/// <returns></returns>
protected override string[] GetHeaders()
Expand Down
3 changes: 2 additions & 1 deletion BadMedicine.Dicom/DicomDataGeneratorStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
using System;
using System.Collections.Generic;
using System.Data;
using BadMedicine.Datasets;
using SynthEHR.Datasets;
using SynthEHR;

namespace BadMedicine.Dicom;

Expand Down
17 changes: 9 additions & 8 deletions BadMedicine.Dicom/Series.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FellowOakDicom;
using SynthEHR;
using System;
using System.Collections;
using System.Collections.Generic;
Expand All @@ -16,19 +17,19 @@ public class Series : IEnumerable<DicomDataset>
/// <summary>
/// The unique identifier for this series
/// </summary>
public DicomUID SeriesUID {get; }
public DicomUID SeriesUID { get; }

/// <summary>
/// The Dicom Study this series is a part of
/// </summary>
public Study Study{get; }
public Study Study { get; }

/// <summary>
/// All dicom images generated for this series. These can be
/// written out to file by other processes and do not yet exist
/// on disk.
/// </summary>
public IReadOnlyList<DicomDataset> Datasets{get; }
public IReadOnlyList<DicomDataset> Datasets { get; }

private readonly List<DicomDataset> _datasets = new();

Expand All @@ -43,13 +44,13 @@ public class Series : IEnumerable<DicomDataset>
/// Value to use for the <see cref="DicomTag.Modality"/> when writing
/// out to dicom datasets
/// </summary>
public string Modality {get; }
public string Modality { get; }

/// <summary>
/// Value to use for the <see cref="DicomTag.ImageType"/> when writing
/// out to dicom datasets
/// </summary>
public string ImageType {get; }
public string ImageType { get; }

/// <summary>
/// Date to use for the <see cref="DicomTag.SeriesDate"/> when writing
Expand Down Expand Up @@ -98,11 +99,11 @@ internal Series(Study study, Person person, string modality, string imageType, i
SeriesDate = study.StudyDate;
SeriesTime = study.StudyTime;

SeriesDescription = part?.SeriesDescription;
SeriesDescription = part?.SeriesDescription;
BodyPartExamined = part?.BodyPartExamined;

for (var i =0 ; i<imageCount;i++)
_datasets.Add(Study.Parent.GenerateTestDataset(person,this));
for (var i = 0; i < imageCount; i++)
_datasets.Add(Study.Parent.GenerateTestDataset(person, this));

Datasets = new ReadOnlyCollection<DicomDataset>(_datasets);
}
Expand Down
1 change: 1 addition & 0 deletions BadMedicine.Dicom/Study.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FellowOakDicom;
using SynthEHR;
using System;
using System.Collections;
using System.Collections.Generic;
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

...
* Replace BadMedicine v1.2.1 with SynthEHR v2.0.0

## [0.1.0] - 2024-03-20

Expand Down
2 changes: 1 addition & 1 deletion Packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

| Package | Source Code | License | Purpose | Additional Risk Assessment |
| ------- | ------------| ------- | ------- | -------------------------- |
| HIC.BadMedicine | [GitHub](https://github.com/SMI/BadMedicine) | [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.html) | Handles generating baseline random data (patient date of birth, CHI numberts etc)| |
| HIC.SynthEHR | [GitHub](https://github.com/HICServices/SynthEHR) | [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.html) | Handles generating baseline random data (patient date of birth, CHI numberts etc)| |
| fo-dicom | [GitHub](https://github.com/fo-dicom/fo-dicom) |[MS-PL](https://opensource.org/licenses/MS-PL) | Handles reading/writing dicom tags from dicom datasets | |
| fo-dicom.Imaging.ImageSharp | [GitHub](https://github.com/fo-dicom/fo-dicom) |[MS-PL](https://opensource.org/licenses/MS-PL) | Handles imaging aspects with fo-dicom | |
| SixLabors.ImageSharp | [GitHub](https://github.com/SixLabors/ImageSharp) | [Apache 2.0](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) | Platform-independent replacement for legacy Windows-only System.Drawing.Common | |
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ There are a number of public sources of Dicom clinical images e.g. [TCIA ](https
- Do not represent the breadth of Modalities/Tags found in a live clinical [PACS](https://en.wikipedia.org/wiki/Picture_archiving_and_communication_system).
- Take up a lot of space

BadMedicine.Dicom generates dicom images on demand based on an anonymous aggregate model of tag data found in scottish medical imaging. It is an extension of [BadMedicine](https://github.com/SMI/BadMedicine) which generates traditional EHR records.
BadMedicine.Dicom generates dicom images on demand based on an anonymous aggregate model of tag data found in scottish medical imaging. It is an extension of [SynthEHR](https://github.com/SMI/SynthEHR) which generates traditional EHR records.

## Usage

Expand Down Expand Up @@ -51,18 +51,18 @@ Database:
# Contains the table schema (which dicom tags to use for which tables)
Template: CT.it
# Database to create/use on the server
DatabaseName: BadMedicineTestData
DatabaseName: SynthEHRTestData
# Setting this deduplicates study/series level schemas (works only if tables do not already exist on server)
MakeDistinct: true
```
## EHR Datasets
If you want to generate EHR datasets with a shared patient pool with the dicom data (e.g. for doing linkage) you can provided the -s (seed) and use the main [BadMedicine](https://github.com/SMI/BadMedicine) application.
If you want to generate EHR datasets with a shared patient pool with the dicom data (e.g. for doing linkage) you can provided the -s (seed) and use the main [SynthEHR](https://github.com/SMI/SynthEHR) application.
```
BadDicom.exe c:\temp\testdicoms 12 10 -s 100
BadMedicine.exe c:\temp\testdicoms 12 100 -s 100
SynthEHR.exe c:\temp\testdicoms 12 100 -s 100
```
_Generates a pool of 12 patients and 10 Studies (for random patients) then 100 rows of data for each EHR dataset_

Expand Down Expand Up @@ -120,7 +120,7 @@ $ sudo yum install libc6-devel libgdiplus

# Tag Data

Basic random patient information (age, CHI etc) are generated by [BadMedicine](https://github.com/SMI/BadMedicine)
Basic random patient information (age, CHI etc) are generated by [SynthEHR](https://github.com/SMI/SynthEHR)

The following tags are populated in dicom files generated:

Expand Down

0 comments on commit 9c18d17

Please sign in to comment.