public DicomFile GetDataset(ZipPool pool = null)
        {
            if (IsZipReference(FullPath))
            {
                var bits = FullPath.Split('!');

                var zip = pool != null?pool.OpenRead(bits[0]) : ZipFile.Open(bits[0], ZipArchiveMode.Read);

                try
                {
                    var entry = zip.GetEntry(bits[1]);

                    if (!IsDicomReference(bits[1]))
                    {
                        throw new AmbiguousFilePathResolutionException("Path provided '" + FullPath + "' was to a zip file but not to a dicom file entry");
                    }

                    var buffer = ByteStreamHelper.ReadFully(entry.Open());

                    //todo: when GH-627 goes live we can use FileReadOption  https://github.com/fo-dicom/fo-dicom/blob/GH-627/DICOM/DicomFile.cs
                    //using (var memoryStream = new MemoryStream(buffer))
                    var memoryStream = new MemoryStream(buffer);

                    return(DicomFile.Open(memoryStream));
                }
                finally
                {
                    if (pool == null)
                    {
                        zip.Dispose();
                    }
                }
            }

            if (!IsDicomReference(FullPath))
            {
                throw new AmbiguousFilePathResolutionException("Path provided '" + FullPath + "' was not to either an entry in a zip file or to a dicom file");
            }

            return(DicomFile.Open(FullPath));
        }
Beispiel #2
0
        public DicomFile GetDataset(ZipPool pool = null)
        {
            if (IsZipReference(FullPath))
            {
                var bits = FullPath.Split('!');

                var zip = pool != null?pool.OpenRead(bits[0]) : ZipFile.Open(bits[0], ZipArchiveMode.Read);

                try
                {
                    var entry = zip.GetEntry(bits[1]);

                    if (entry == null)
                    {
                        //Maybe user has formatted it dodgy
                        //e.g. \2015\3\18\2.25.177481563701402448825228719253578992342.dcm
                        string adjusted = bits[1].TrimStart('\\', '/');

                        //if that doesn't work
                        if ((entry = zip.GetEntry(adjusted)) == null)
                        {
                            //try normalizing the slashes
                            adjusted = adjusted.Replace('\\', '/');

                            //nope we just cannot get a legit path in this zip
                            if ((entry = zip.GetEntry(adjusted)) == null)
                            {
                                throw new AmbiguousFilePathResolutionException($"Could not find path '{bits[1]}' within zip archive '{bits[0]}'");
                            }
                        }

                        //we fixed it to something that actually exists so update our state that we don't make the same mistake again
                        FullPath = bits[0] + '!' + adjusted;
                    }

                    if (!IsDicomReference(bits[1]))
                    {
                        throw new AmbiguousFilePathResolutionException("Path provided '" + FullPath + "' was to a zip file but not to a dicom file entry");
                    }

                    var buffer = ByteStreamHelper.ReadFully(entry.Open());

                    //todo: when GH-627 goes live we can use FileReadOption  https://github.com/fo-dicom/fo-dicom/blob/GH-627/DICOM/DicomFile.cs
                    //using (var memoryStream = new MemoryStream(buffer))
                    var memoryStream = new MemoryStream(buffer);

                    return(DicomFile.Open(memoryStream));
                }
                finally
                {
                    if (pool == null)
                    {
                        zip.Dispose();
                    }
                }
            }

            if (!IsDicomReference(FullPath))
            {
                throw new AmbiguousFilePathResolutionException("Path provided '" + FullPath + "' was not to either an entry in a zip file or to a dicom file");
            }

            return(DicomFile.Open(FullPath));
        }
Beispiel #3
0
        public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken)
        {
            //Things we ignore, Lookups, SupportingSql etc
            if (_extractCommand == null)
            {
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Ignoring non dataset command "));
                return(toProcess);
            }

            //if it isn't a dicom dataset don't process it
            if (!toProcess.Columns.Contains(RelativeArchiveColumnName))
            {
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, "Dataset " + _extractCommand.DatasetBundle.DataSet + " did not contain field '" + RelativeArchiveColumnName + "' so we will not attempt to extract images"));
                return(toProcess);
            }

            if (_putter == null)
            {
                _putter = (IPutDicomFilesInExtractionDirectories)  new ObjectConstructor().Construct(PutterType);
            }

            var projectNumber = _extractCommand.Configuration.Project.ProjectNumber.Value;

            var mappingServer        = new MappingRepository(UIDMappingServer);
            var destinationDirectory = new DirectoryInfo(Path.Combine(_extractCommand.GetExtractionDirectory().FullName, "Images"));

            var releaseCol = _extractCommand.QueryBuilder.SelectColumns.Select(c => c.IColumn).Single(c => c.IsExtractionIdentifier);

            // See: ftp://medical.nema.org/medical/dicom/2011/11_15pu.pdf

            var flags = DicomAnonymizer.SecurityProfileOptions.BasicProfile |
                        DicomAnonymizer.SecurityProfileOptions.CleanStructdCont |
                        DicomAnonymizer.SecurityProfileOptions.CleanDesc |
                        DicomAnonymizer.SecurityProfileOptions.RetainUIDs;

            if (RetainDates)
            {
                flags |= DicomAnonymizer.SecurityProfileOptions.RetainLongFullDates;
            }

            var profile = DicomAnonymizer.SecurityProfile.LoadProfile(null, flags);

            var anonymiser = new DicomAnonymizer(profile);

            using (var pool = new ZipPool())
            {
                _sw.Start();

                foreach (DataRow row in toProcess.Rows)
                {
                    if (_errors > 0 && _errors > ErrorThreshold)
                    {
                        throw new Exception($"Number of errors reported ({_errors}) reached the threshold ({ErrorThreshold})");
                    }

                    cancellationToken.ThrowIfAbortRequested();

                    var path = new AmbiguousFilePath(ArchiveRootIfAny, (string)row[RelativeArchiveColumnName]);

                    DicomFile dicomFile;

                    try
                    {
                        dicomFile = path.GetDataset(pool);
                    }
                    catch (Exception e)
                    {
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, $"Failed to get image at path '{path.FullPath}'", e));
                        _errors++;
                        continue;
                    }

                    //get the new patient ID
                    var releaseId = row[releaseCol.GetRuntimeName()].ToString();

                    DicomDataset ds;

                    try
                    {
                        ds = anonymiser.Anonymize(dicomFile.Dataset);
                    }
                    catch (Exception e)
                    {
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, $"Failed to anonymize image at path '{path.FullPath}'", e));
                        _errors++;
                        continue;
                    }

                    //now we want to explicitly use our own release Id regardless of what FoDicom said
                    ds.AddOrUpdate(DicomTag.PatientID, releaseId);

                    //rewrite the UIDs
                    foreach (var kvp in UIDMapping.SupportedTags)
                    {
                        if (!ds.Contains(kvp.Key))
                        {
                            continue;
                        }

                        var value = ds.GetValue <string>(kvp.Key, 0);

                        //if it has a value for this UID
                        if (value == null)
                        {
                            continue;
                        }
                        var releaseValue = mappingServer.GetOrAllocateMapping(value, projectNumber, kvp.Value);

                        //change value in dataset
                        ds.AddOrUpdate(kvp.Key, releaseValue);

                        //and change value in DataTable
                        if (toProcess.Columns.Contains(kvp.Key.DictionaryEntry.Keyword))
                        {
                            row[kvp.Key.DictionaryEntry.Keyword] = releaseValue;
                        }
                    }

                    var newPath = _putter.WriteOutDataset(destinationDirectory, releaseId, ds);
                    row[RelativeArchiveColumnName] = newPath;

                    _anonymisedImagesCount++;

                    listener.OnProgress(this, new ProgressEventArgs("Writing ANO images", new ProgressMeasurement(_anonymisedImagesCount, ProgressType.Records), _sw.Elapsed));
                }

                _sw.Stop();
            }

            return(toProcess);
        }