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
+ #include " itk_wasm_dcmtk_rt_study.h"
19
+
20
+ #include " dcmtk_rt_study_p.h"
21
+
22
+ #include " plmbase_config.h"
23
+ #include < stdlib.h>
24
+ #include < stdio.h>
25
+ #include " dcmtk_config.h"
26
+ #include " dcmtk/ofstd/ofstream.h"
27
+ #include " dcmtk/dcmdata/dctk.h"
28
+
29
+ #include " dcmtk_file.h"
30
+ #include " dcmtk_metadata.h"
31
+ #include " dcmtk_module.h"
32
+ #include " dcmtk_rt_study.h"
33
+ #include " dcmtk_rt_study_p.h"
34
+ #include " dcmtk_series.h"
35
+ #include " dcmtk_slice_data.h"
36
+ #include " dcmtk_util.h"
37
+ #include " file_util.h"
38
+ #include " logfile.h"
39
+ #include " metadata.h"
40
+ #include " plm_uid_prefix.h"
41
+ #include " plm_version.h"
42
+ #include " print_and_exit.h"
43
+ #include " rtss.h"
44
+ #include " rtss_contour.h"
45
+ #include " rtss_roi.h"
46
+ #include " string_util.h"
47
+
48
+ Itk_wasm_dcmtk_rt_study::Itk_wasm_dcmtk_rt_study ()
49
+ {
50
+ }
51
+
52
+ Itk_wasm_dcmtk_rt_study::~Itk_wasm_dcmtk_rt_study ()
53
+ {
54
+ }
55
+
56
+ void
57
+ Itk_wasm_dcmtk_rt_study::save_rtss (const char * fname, const ItkWasmRtStudyMetadata & metadata)
58
+ {
59
+ // Modified:
60
+ // rt_study.cxx Rt_study::save_dicom
61
+ this ->d_ptr ->rt_study_metadata ->generate_new_rtstruct_instance_uid ();
62
+
63
+ // Modified:
64
+ // rtds_dcmtk.cxx Rt_study::rtss_save
65
+
66
+ OFCondition ofc;
67
+ Rtss::Pointer& rtss = this ->d_ptr ->rtss ;
68
+ Metadata::Pointer rtstruct_metadata;
69
+ if (this ->d_ptr ->rt_study_metadata ) {
70
+ rtstruct_metadata = d_ptr->rt_study_metadata ->get_rtstruct_metadata ();
71
+ }
72
+
73
+ /* Prepare structure set with slice uids */
74
+ const Slice_list *slice_list = d_ptr->rt_study_metadata ->get_slice_list ();
75
+ rtss->apply_slice_list (slice_list);
76
+
77
+ /* Prepare dcmtk */
78
+ DcmFileFormat fileformat;
79
+ DcmDataset *dataset = fileformat.getDataset ();
80
+
81
+ /* Add entries for common modules */
82
+ Dcmtk_module::set_sop_common (dataset);
83
+
84
+ /* GCS FIX, remove below code, use common modules instead */
85
+
86
+ /* ----------------------------------------------------------------- */
87
+ /* Part 1 -- General header */
88
+ /* ----------------------------------------------------------------- */
89
+ dataset->putAndInsertOFStringArray (DCM_InstanceCreationDate,
90
+ d_ptr->rt_study_metadata ->get_study_date ());
91
+ dataset->putAndInsertOFStringArray (DCM_InstanceCreationTime,
92
+ d_ptr->rt_study_metadata ->get_study_time ());
93
+ dataset->putAndInsertOFStringArray (DCM_InstanceCreatorUID,
94
+ PlmUidPrefix::getInstance ().get ().c_str ());
95
+ dataset->putAndInsertString (DCM_SOPClassUID, UID_RTStructureSetStorage);
96
+ dcmtk_put (dataset, DCM_SOPInstanceUID,
97
+ d_ptr->rt_study_metadata ->get_rtstruct_instance_uid ());
98
+ dataset->putAndInsertOFStringArray (DCM_StudyDate,
99
+ d_ptr->rt_study_metadata ->get_study_date ());
100
+ dataset->putAndInsertOFStringArray (DCM_StudyTime,
101
+ d_ptr->rt_study_metadata ->get_study_time ());
102
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata,
103
+ DCM_StudyDescription, " " );
104
+
105
+ dataset->putAndInsertOFStringArray (DCM_AccessionNumber, " " );
106
+ dataset->putAndInsertOFStringArray (DCM_Modality, " RTSTRUCT" );
107
+ dataset->putAndInsertString (DCM_Manufacturer, metadata.manufacturer .c_str ());
108
+ dataset->putAndInsertString (DCM_InstitutionName, " " );
109
+ dataset->putAndInsertString (DCM_ReferringPhysicianName, " " );
110
+ dataset->putAndInsertString (DCM_StationName, " " );
111
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata,
112
+ DCM_SeriesDescription, " " );
113
+ dataset->putAndInsertString (DCM_ManufacturerModelName, metadata.manufacturerModelName .c_str ());
114
+
115
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata, DCM_PatientName, " " );
116
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata, DCM_PatientID, " " );
117
+ dataset->putAndInsertString (DCM_PatientBirthDate, " " );
118
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata, DCM_PatientSex, " O" );
119
+ dataset->putAndInsertString (DCM_SoftwareVersions,
120
+ PLASTIMATCH_VERSION_STRING);
121
+
122
+ dataset->putAndInsertString (DCM_StudyInstanceUID,
123
+ d_ptr->rt_study_metadata ->get_study_uid ().c_str ());
124
+ dataset->putAndInsertString (DCM_SeriesInstanceUID,
125
+ d_ptr->rt_study_metadata ->get_rtstruct_series_uid ());
126
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata, DCM_StudyID, " 10001" );
127
+ dcmtk_copy_from_metadata (dataset, rtstruct_metadata, DCM_SeriesNumber, " 1" );
128
+ dataset->putAndInsertString (DCM_InstanceNumber, " 1" );
129
+ dataset->putAndInsertString (DCM_StructureSetLabel, " AutoSS" );
130
+ dataset->putAndInsertString (DCM_StructureSetName, " AutoSS" );
131
+ dataset->putAndInsertOFStringArray (DCM_StructureSetDate,
132
+ d_ptr->rt_study_metadata ->get_study_date ());
133
+ dataset->putAndInsertOFStringArray (DCM_StructureSetTime,
134
+ d_ptr->rt_study_metadata ->get_study_time ());
135
+
136
+ /* ----------------------------------------------------------------- */
137
+ /* Part 2 -- UID's for CT series */
138
+ /* ----------------------------------------------------------------- */
139
+ DcmSequenceOfItems *rfor_seq = 0 ;
140
+ DcmItem *rfor_item = 0 ;
141
+ dataset->findOrCreateSequenceItem (
142
+ DCM_ReferencedFrameOfReferenceSequence, rfor_item, -2 );
143
+ rfor_item->putAndInsertString (DCM_FrameOfReferenceUID,
144
+ d_ptr->rt_study_metadata ->get_frame_of_reference_uid ().c_str ());
145
+ dataset->findAndGetSequence (
146
+ DCM_ReferencedFrameOfReferenceSequence, rfor_seq);
147
+ DcmItem *rtrstudy_item = 0 ;
148
+ rfor_item->findOrCreateSequenceItem (
149
+ DCM_RTReferencedStudySequence, rtrstudy_item, -2 );
150
+ rtrstudy_item->putAndInsertString (
151
+ DCM_ReferencedSOPClassUID,
152
+ UID_RETIRED_StudyComponentManagementSOPClass);
153
+ rtrstudy_item->putAndInsertString (
154
+ DCM_ReferencedSOPInstanceUID,
155
+ d_ptr->rt_study_metadata ->get_study_uid ().c_str ());
156
+ DcmItem *rtrseries_item = 0 ;
157
+ rtrstudy_item->findOrCreateSequenceItem (
158
+ DCM_RTReferencedSeriesSequence, rtrseries_item, -2 );
159
+ rtrseries_item->putAndInsertString (
160
+ DCM_SeriesInstanceUID, d_ptr->rt_study_metadata ->get_ct_series_uid ());
161
+
162
+ for (int k = 0 ; k < d_ptr->rt_study_metadata ->num_slices (); k++) {
163
+ DcmItem *ci_item = 0 ;
164
+ rtrseries_item->findOrCreateSequenceItem (
165
+ DCM_ContourImageSequence, ci_item, -2 );
166
+ ci_item->putAndInsertString (
167
+ DCM_ReferencedSOPClassUID, UID_CTImageStorage);
168
+ ci_item->putAndInsertString (
169
+ DCM_ReferencedSOPInstanceUID,
170
+ d_ptr->rt_study_metadata ->get_slice_uid (k));
171
+ }
172
+
173
+ /* ----------------------------------------------------------------- */
174
+ /* Part 3 -- Structure info */
175
+ /* ----------------------------------------------------------------- */
176
+ for (size_t i = 0 ; i < rtss->num_structures ; i++) {
177
+ DcmItem *ssroi_item = 0 ;
178
+ std::string tmp;
179
+ dataset->findOrCreateSequenceItem (
180
+ DCM_StructureSetROISequence, ssroi_item, -2 );
181
+ tmp = string_format (" %d" , rtss->slist [i]->id );
182
+ ssroi_item->putAndInsertString (DCM_ROINumber, tmp.c_str ());
183
+ ssroi_item->putAndInsertString (DCM_ReferencedFrameOfReferenceUID,
184
+ d_ptr->rt_study_metadata ->get_frame_of_reference_uid ().c_str ());
185
+ ssroi_item->putAndInsertString (DCM_ROIName,
186
+ rtss->slist [i]->name .c_str ());
187
+ if (metadata.roiMetadata .size () > i) {
188
+ ssroi_item->putAndInsertString (DCM_ROIGenerationAlgorithm,
189
+ metadata.roiMetadata [i].roiGenerationAlgorithm .c_str ());
190
+ ssroi_item->putAndInsertString (DCM_ROIDescription,
191
+ metadata.roiMetadata [i].roiDescription .c_str ());
192
+ }
193
+ else {
194
+ ssroi_item->putAndInsertString (DCM_ROIGenerationAlgorithm, " " );
195
+ }
196
+ }
197
+
198
+ /* ----------------------------------------------------------------- */
199
+ /* Part 4 -- Contour info */
200
+ /* ----------------------------------------------------------------- */
201
+ for (size_t i = 0 ; i < rtss->num_structures ; i++) {
202
+ Rtss_roi *curr_structure = rtss->slist [i];
203
+ DcmItem *roic_item = 0 ;
204
+ dataset->findOrCreateSequenceItem (
205
+ DCM_ROIContourSequence, roic_item, -2 );
206
+ std::string tmp = curr_structure->get_dcm_color_string ();
207
+ roic_item->putAndInsertString (DCM_ROIDisplayColor, tmp.c_str ());
208
+ for (size_t j = 0 ; j < curr_structure->num_contours ; j++) {
209
+ Rtss_contour *curr_contour = curr_structure->pslist [j];
210
+ if (curr_contour->num_vertices <= 0 ) continue ;
211
+
212
+ #if defined (commentout)
213
+ /* GCS 2013-07-02: DICOM standard allows contours without
214
+ an associated slice UID. Maybe this bug is now
215
+ fixed in XiO??? */
216
+ /* GE -> XiO transfer does not work if contour does not have
217
+ corresponding slice uid */
218
+ if (curr_contour->ct_slice_uid .empty ()) {
219
+ printf (" Warning: Omitting contour (%ld,%ld)\n " ,
220
+ (long ) i, (long ) j);
221
+ continue ;
222
+ }
223
+ #endif
224
+
225
+ /* Add item to ContourSequence */
226
+ DcmItem *c_item = 0 ;
227
+ roic_item->findOrCreateSequenceItem (
228
+ DCM_ContourSequence, c_item, -2 );
229
+
230
+ /* ContourImageSequence */
231
+ if (curr_contour->ct_slice_uid != " " ) {
232
+ DcmItem *ci_item = 0 ;
233
+ c_item->findOrCreateSequenceItem (
234
+ DCM_ContourImageSequence, ci_item, -2 );
235
+ ci_item->putAndInsertString (DCM_ReferencedSOPClassUID,
236
+ UID_CTImageStorage);
237
+ ci_item->putAndInsertString (DCM_ReferencedSOPInstanceUID,
238
+ curr_contour->ct_slice_uid .c_str ());
239
+ }
240
+
241
+ /* ContourGeometricType */
242
+ c_item->putAndInsertString (DCM_ContourGeometricType,
243
+ " CLOSED_PLANAR" );
244
+
245
+ /* NumberOfContourPoints */
246
+ tmp = string_format (" %d" , curr_contour->num_vertices );
247
+ c_item->putAndInsertString (DCM_NumberOfContourPoints, tmp.c_str ());
248
+
249
+ /* ContourData */
250
+ tmp = string_format (" %.8g\\ %.8g\\ %.8g" ,
251
+ curr_contour->x [0 ],
252
+ curr_contour->y [0 ],
253
+ curr_contour->z [0 ]);
254
+ for (size_t k = 1 ; k < curr_contour->num_vertices ; k++) {
255
+ std::string tmp2 = string_format (" \\ %.8g\\ %.8g\\ %.8g" ,
256
+ curr_contour->x [k],
257
+ curr_contour->y [k],
258
+ curr_contour->z [k]);
259
+ tmp += tmp2;
260
+ }
261
+ c_item->putAndInsertString (DCM_ContourData, tmp.c_str ());
262
+ }
263
+
264
+ tmp = string_format (" %d" , (int ) curr_structure->id );
265
+ roic_item->putAndInsertString (DCM_ReferencedROINumber, tmp.c_str ());
266
+ }
267
+
268
+ /* ----------------------------------------------------------------- */
269
+ /* Part 5 -- More structure info */
270
+ /* ----------------------------------------------------------------- */
271
+ for (size_t i = 0 ; i < rtss->num_structures ; i++) {
272
+ Rtss_roi *curr_structure = rtss->slist [i];
273
+ std::string tmp;
274
+
275
+ /* RTROIObservationsSequence */
276
+ DcmItem *rtroio_item = 0 ;
277
+ dataset->findOrCreateSequenceItem (
278
+ DCM_RTROIObservationsSequence, rtroio_item, -2 );
279
+
280
+ /* ObservationNumber */
281
+ tmp = string_format (" %d" , (int ) curr_structure->id );
282
+ rtroio_item->putAndInsertString (DCM_ObservationNumber, tmp.c_str ());
283
+ /* ReferencedROINumber */
284
+ rtroio_item->putAndInsertString (DCM_ReferencedROINumber, tmp.c_str ());
285
+ if (metadata.roiMetadata .size () > i) {
286
+ rtroio_item->putAndInsertString (DCM_RTROIInterpretedType,
287
+ metadata.roiMetadata [i].rtRoiInterpretedType .c_str ());
288
+ }
289
+ else {
290
+ rtroio_item->putAndInsertString (DCM_RTROIInterpretedType, " " );
291
+ }
292
+ /* ROIInterpreter */
293
+ rtroio_item->putAndInsertString (DCM_ROIInterpreter, " " );
294
+ // /* ROIPhysicalProperty */
295
+ if (rtss->slist [i]->rsp_value > -1.0 )
296
+ {
297
+ DcmItem *rsp_item = NULL ;
298
+ if (rtroio_item->findOrCreateSequenceItem (
299
+ DCM_ROIPhysicalPropertiesSequence, rsp_item, -2 ).good ())
300
+ {
301
+ rsp_item->putAndInsertString (DCM_ROIPhysicalProperty, " REL_STOP_RATIO" );
302
+ rsp_item->putAndInsertString (DCM_ROIPhysicalPropertyValue, std::to_string (rtss->slist [i]->rsp_value ).c_str ());
303
+ }
304
+ }
305
+ }
306
+
307
+ /* ----------------------------------------------------------------- */
308
+ /* Write the output file */
309
+ /* ----------------------------------------------------------------- */
310
+ make_parent_directories (fname);
311
+
312
+ ofc = fileformat.saveFile (fname, EXS_LittleEndianExplicit);
313
+ if (ofc.bad ()) {
314
+ print_and_exit (" Error: cannot write DICOM RTSTRUCT (%s)\n " ,
315
+ ofc.text ());
316
+ }
317
+ }
0 commit comments