Skip to content

Commit eada50c

Browse files
committed
fix(image-sets-normalization): fix sorting of files in series
1 parent eac5f09 commit eada50c

File tree

6 files changed

+216
-130
lines changed

6 files changed

+216
-130
lines changed

packages/dicom/gdcm/SortSpatially.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#ifndef SORT_SPATIALLY_H
19+
#define SORT_SPATIALLY_H
20+
21+
#include <string>
22+
#include <unordered_set>
23+
24+
#include "gdcmSerieHelper.h"
25+
26+
class CustomSerieHelper : public gdcm::SerieHelper
27+
{
28+
public:
29+
void AddFileName(std::string const &fileName)
30+
{
31+
SerieHelper::AddFileName(fileName);
32+
}
33+
};
34+
35+
using FileNamesContainer = std::vector<std::string>;
36+
37+
FileNamesContainer sortSpatially(std::vector<std::string> unsortedSerieFileNames)
38+
{
39+
std::unique_ptr<CustomSerieHelper> serieHelper(new CustomSerieHelper());
40+
for (const std::string &fileName : unsortedSerieFileNames)
41+
{
42+
serieHelper->AddFileName(fileName);
43+
}
44+
serieHelper->SetUseSeriesDetails(true);
45+
// Add the default restrictions to refine the file set into multiple series.
46+
serieHelper->CreateDefaultUniqueSeriesIdentifier();
47+
using SeriesIdContainer = std::vector<std::string>;
48+
SeriesIdContainer seriesUIDs;
49+
// Accessing the first serie found (assume there is at least one)
50+
gdcm::FileList *flist = serieHelper->GetFirstSingleSerieUIDFileSet();
51+
while (flist)
52+
{
53+
if (!flist->empty()) // make sure we have at least one serie
54+
{
55+
gdcm::File *file = (*flist)[0]; // for example take the first one
56+
57+
// Create its unique series ID
58+
const std::string id(serieHelper->CreateUniqueSeriesIdentifier(file));
59+
60+
seriesUIDs.push_back(id);
61+
}
62+
flist = serieHelper->GetNextSingleSerieUIDFileSet();
63+
}
64+
65+
FileNamesContainer fileNames;
66+
flist = serieHelper->GetFirstSingleSerieUIDFileSet();
67+
const std::string serie = seriesUIDs[0];
68+
bool found = false;
69+
while (flist && !found)
70+
{
71+
if (!flist->empty()) // make sure we have at least one serie
72+
{
73+
gdcm::File *file = (*flist)[0]; // for example take the first one
74+
const std::string id(serieHelper->CreateUniqueSeriesIdentifier(file));
75+
if (id == serie)
76+
{
77+
found = true; // we found a match
78+
break;
79+
}
80+
}
81+
flist = serieHelper->GetNextSingleSerieUIDFileSet();
82+
}
83+
serieHelper->OrderFileList(flist);
84+
85+
gdcm::FileList::iterator it;
86+
for (it = flist->begin(); it != flist->end(); ++it)
87+
{
88+
gdcm::FileWithName *header = *it;
89+
fileNames.push_back(header->filename);
90+
}
91+
return fileNames;
92+
}
93+
94+
#endif // SORT_SPATIALLY_H

packages/dicom/gdcm/image-sets-normalization.cxx

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@
1515
* limitations under the License.
1616
*
1717
*=========================================================================*/
18+
#include <unordered_set>
19+
20+
#include "rapidjson/document.h"
21+
#include "rapidjson/stringbuffer.h"
22+
#include "rapidjson/writer.h"
23+
1824
#include "itkPipeline.h"
1925
#include "itkOutputTextStream.h"
2026

2127
#include "gdcmDiscriminateVolume.h"
2228
#include "DICOMTagReader.h"
2329
#include "Tags.h"
24-
25-
#include "rapidjson/document.h"
26-
#include "rapidjson/stringbuffer.h"
27-
#include "rapidjson/writer.h"
28-
29-
#include <unordered_set>
30+
#include "SortSpatially.h"
3031

3132
const std::string STUDY_INSTANCE_UID = "0020|000D";
3233
const std::string SERIES_INSTANCE_UID = "0020|000e";
3334

3435
using File = std::string;
3536
using TagValues = itk::DICOMTagReader::TagMapType;
36-
using FileToTags = std::unordered_map<File, TagValues>;
3737

3838
rapidjson::Value mapToJsonObj(const TagValues &tags, rapidjson::Document::AllocatorType &allocator)
3939
{
@@ -118,24 +118,23 @@ bool compareTags(const TagValues &tags1, const TagValues &tags2, const Tags &tag
118118
return true;
119119
}
120120

121+
bool isSameVolume(const TagValues &tags1, const TagValues &tags2)
122+
{
123+
// TODO check cosines
124+
return compareTags(tags1, tags2, {SERIES_INSTANCE_UID});
125+
}
126+
121127
Volumes groupByVolume(const DicomFiles &dicomFiles)
122128
{
123129
Volumes volumes;
124130
for (const DicomFile &dicomFile : dicomFiles)
125131
{
126-
const auto &[file, tags] = dicomFile;
127-
Volume *matchingVolume = nullptr;
128-
for (Volume &volume : volumes)
129-
{
130-
const DicomFile fileInVolume = *volume.begin();
131-
const TagValues volumeTags = fileInVolume.second;
132-
if (compareTags(volumeTags, tags, {SERIES_INSTANCE_UID}))
133-
{
134-
matchingVolume = &volume;
135-
break;
136-
}
137-
}
138-
if (matchingVolume)
132+
const auto tags = dicomFile.second;
133+
auto matchingVolume = std::find_if(volumes.begin(), volumes.end(), [&tags](const Volume &volume)
134+
{
135+
return isSameVolume(volume.begin()->second, tags); });
136+
137+
if (matchingVolume != volumes.end())
139138
{
140139
matchingVolume->push_back(dicomFile);
141140
}
@@ -154,17 +153,11 @@ ImageSets groupByImageSet(const Volumes &volumes)
154153
for (const Volume &volume : volumes)
155154
{
156155
const auto volumeTags = volume.begin()->second;
157-
Volumes *matchingImageSet = nullptr;
158-
for (Volumes &volumes : imageSets)
159-
{
156+
auto matchingImageSet = std::find_if(imageSets.begin(), imageSets.end(), [&volumeTags](const Volumes &volumes)
157+
{
160158
const TagValues imageSetTags = volumes.begin()->begin()->second;
161-
if (compareTags(imageSetTags, volumeTags, {STUDY_INSTANCE_UID}))
162-
{
163-
matchingImageSet = &volumes;
164-
break;
165-
}
166-
}
167-
if (matchingImageSet)
159+
return compareTags(imageSetTags, volumeTags, {STUDY_INSTANCE_UID}); });
160+
if (matchingImageSet != imageSets.end())
168161
{
169162
matchingImageSet->push_back(volume);
170163
}
@@ -177,6 +170,33 @@ ImageSets groupByImageSet(const Volumes &volumes)
177170
return imageSets;
178171
}
179172

173+
Volumes sortSpatially(Volumes &volumes)
174+
{
175+
Volumes sortedVolumes;
176+
for (Volume &volume : volumes)
177+
{
178+
std::vector<std::string> unsortedSerieFileNames;
179+
for (const DicomFile &dicomFile : volume)
180+
{
181+
unsortedSerieFileNames.push_back(dicomFile.first);
182+
}
183+
std::vector<std::string> sortedFileNames = sortSpatially(unsortedSerieFileNames);
184+
185+
Volume sorted;
186+
for (const auto &fileName : sortedFileNames)
187+
{
188+
const auto matchingDicomFile = std::find_if(volume.begin(), volume.end(), [&fileName](const DicomFile &dicomFile)
189+
{ return dicomFile.first == fileName; });
190+
if (matchingDicomFile != volume.end())
191+
{
192+
sorted.push_back(*matchingDicomFile);
193+
}
194+
}
195+
sortedVolumes.push_back(sorted);
196+
}
197+
return sortedVolumes;
198+
}
199+
180200
rapidjson::Document toJson(const ImageSets &imageSets)
181201
{
182202
rapidjson::Document imageSetsJson(rapidjson::kArrayType);
@@ -271,7 +291,8 @@ int main(int argc, char *argv[])
271291
ITK_WASM_PARSE(pipeline);
272292

273293
const DicomFiles dicomFiles = readTags(files);
274-
const Volumes volumes = groupByVolume(dicomFiles);
294+
Volumes volumes = groupByVolume(dicomFiles);
295+
volumes = sortSpatially(volumes);
275296
const ImageSets imageSets = groupByImageSet(volumes);
276297

277298
rapidjson::Document imageSetsJson = toJson(imageSets);

packages/dicom/gdcm/read-image-dicom-file-series.cxx

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include <fstream>
2121
#include <memory>
2222

23+
#include "rapidjson/document.h"
24+
#include "rapidjson/prettywriter.h"
25+
#include "rapidjson/ostreamwrapper.h"
26+
2327
#include "itkCommonEnums.h"
2428
#include "gdcmSerieHelper.h"
2529
#include "itkImageIOBase.h"
@@ -32,18 +36,7 @@
3236
#include "itkOutputImage.h"
3337
#include "itkOutputTextStream.h"
3438

35-
#include "rapidjson/document.h"
36-
#include "rapidjson/prettywriter.h"
37-
#include "rapidjson/ostreamwrapper.h"
38-
39-
class CustomSerieHelper: public gdcm::SerieHelper
40-
{
41-
public:
42-
void AddFileName(std::string const &fileName)
43-
{
44-
SerieHelper::AddFileName(fileName);
45-
}
46-
};
39+
#include "SortSpatially.h"
4740

4841
namespace itk
4942
{
@@ -210,60 +203,7 @@ int runPipeline(itk::wasm::Pipeline & pipeline, std::vector<std::string> & input
210203

211204
if (!singleSortedSeries)
212205
{
213-
std::unique_ptr<CustomSerieHelper> serieHelper(new CustomSerieHelper());
214-
for (const std::string & fileName: inputFileNames)
215-
{
216-
serieHelper->AddFileName(fileName);
217-
}
218-
serieHelper->SetUseSeriesDetails(true);
219-
// Add the default restrictions to refine the file set into multiple series.
220-
serieHelper->CreateDefaultUniqueSeriesIdentifier();
221-
using SeriesIdContainer = std::vector<std::string>;
222-
SeriesIdContainer seriesUIDs;
223-
// Accessing the first serie found (assume there is at least one)
224-
gdcm::FileList * flist = serieHelper->GetFirstSingleSerieUIDFileSet();
225-
while (flist)
226-
{
227-
if (!flist->empty()) // make sure we have at leat one serie
228-
{
229-
gdcm::File * file = (*flist)[0]; // for example take the first one
230-
231-
// Create its unique series ID
232-
const std::string id( serieHelper->CreateUniqueSeriesIdentifier(file));
233-
234-
seriesUIDs.push_back(id);
235-
}
236-
flist = serieHelper->GetNextSingleSerieUIDFileSet();
237-
}
238-
239-
using FileNamesContainer = std::vector<std::string>;
240-
FileNamesContainer fileNames;
241-
flist = serieHelper->GetFirstSingleSerieUIDFileSet();
242-
const std::string serie = seriesUIDs[0];
243-
bool found = false;
244-
while (flist && !found)
245-
{
246-
if (!flist->empty()) // make sure we have at leat one serie
247-
{
248-
gdcm::File * file = (*flist)[0]; // for example take the first one
249-
const std::string id( serieHelper->CreateUniqueSeriesIdentifier(file));
250-
if (id == serie)
251-
{
252-
found = true; // we found a match
253-
break;
254-
}
255-
}
256-
flist = serieHelper->GetNextSingleSerieUIDFileSet();
257-
}
258-
serieHelper->OrderFileList(flist);
259-
260-
gdcm::FileList::iterator it;
261-
for (it = flist->begin(); it != flist->end(); ++it)
262-
{
263-
gdcm::FileWithName * header = *it;
264-
fileNames.push_back(header->filename);
265-
}
266-
206+
std::vector<std::string> fileNames = sortSpatially(inputFileNames);
267207
reader->SetFileNames(fileNames);
268208
}
269209
else

0 commit comments

Comments
 (0)