using AutoMapper; using AutoMapper.QueryableExtensions; using Dicom; using Dicom.Imaging.Codec; using IRaCIS.Core.Application.Contracts.Dicom; using IRaCIS.Core.Application.Contracts.Dicom.DTO; using IRaCIS.Core.Domain.Interfaces; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Share; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IRaCIS.Core.Application.Dicom { public class DicomArchiveService : IDicomArchiveService { private readonly IDicomStudyRepository _studyRepository; private readonly IDicomSeriesRepository _seriesRepository; private readonly IDicomInstanceRepository _instanceRepository; private readonly IImageLabelRepository _imageLabelRepository; private readonly IKeyInstanceRepository _keyInstanceRepository; private readonly IMapper _mapper; private readonly IHostEnvironment _hostEnvironment; private static string _fileStorePath = string.Empty; private readonly ILogger _logger; public DicomArchiveService(IDicomStudyRepository studyRepository, IDicomSeriesRepository seriesRepository, IDicomInstanceRepository instanceRepository, IImageLabelRepository imageLabelRepository, IKeyInstanceRepository keyInstanceRepository, IHostEnvironment hostEnvironment, IConfiguration config, ILogger logger, IMapper mapper) { _studyRepository = studyRepository; _seriesRepository = seriesRepository; _instanceRepository = instanceRepository; _imageLabelRepository = imageLabelRepository; _keyInstanceRepository = keyInstanceRepository; _mapper = mapper; _logger = logger; _hostEnvironment = hostEnvironment; _fileStorePath = config.GetValue("DicomFileArchivedPath"); //Directory.GetParent(_hostEnvironment.ContentRootPath).FullName; //TranscoderManager.SetImplementation(Efferent.Native.Codec.NativeTranscoderManager.Instance); } public async Task ArchiveDicomStreamAsync(Stream dicomStream, DicomTrialSiteSubjectInfo addtionalInfo) { try { //TranscoderManager.SetImplementation(Efferent.Native.Codec.NativeTranscoderManager.Instance); DicomFile dicomFile = await DicomFile.OpenAsync(dicomStream, Encoding.Default); DicomDataset dataset = dicomFile.Dataset; ////按照配置文件 匿名化 //foreach (var anonymizeItem in SystemConfig.AnonymizeTagList) //{ // if (anonymizeItem.Enable) // { // ushort group = Convert.ToUInt16(anonymizeItem.Group, 16); // ushort element = Convert.ToUInt16(anonymizeItem.Element, 16); // dataset.AddOrUpdate(new DicomTag(group, element), anonymizeItem.ReplaceValue); // } //} if (SystemConfig.AddClinicalInfo) //是否需要写入临床信息 { //Dicom 文件中写入临床信息 dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, addtionalInfo.TrialCode); //Trial dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolName, addtionalInfo.TrialIndication); //indication dataset.AddOrUpdate(DicomTag.ClinicalTrialSponsorName, addtionalInfo.Sponsor);//sponsor dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, addtionalInfo.SiteCode); //SiteId dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteName, addtionalInfo.SiteName);//SiteName dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, addtionalInfo.SubjectCode + " " + addtionalInfo.SubjectSex);//SubjectId dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, addtionalInfo.SubjectVisitNum.ToString()); // TimePoint dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointDescription, addtionalInfo.SubjectVisitVisitName + " " + addtionalInfo.SubjectVisitSVUPDES); } DicomStudy dicomStudy = CreateDicomStudy(dataset, addtionalInfo); DicomSeries dicomSeries = CreateDicomSeries(dataset, dicomStudy); DicomInstance dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries); string filePath = CreateInstanceFilePath(dicomStudy, dicomSeries.Id, dicomInstance.Id); //await dicomFile.SaveAsync(filePath); var samplesPerPixel = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, string.Empty); var photometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty); if (samplesPerPixel == "1" && (photometricInterpretation.ToUpper() == "MONOCHROME2" || photometricInterpretation.ToUpper() == "MONOCHROME1"))//MONOCHROME2 { if (dataset.InternalTransferSyntax.IsEncapsulated) await dicomFile.SaveAsync(filePath); else await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(filePath); } else { if (dataset.InternalTransferSyntax.IsEncapsulated) await dicomFile.SaveAsync(filePath); else await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(filePath); //RLELossless } //try //{ // //var file = dicomFile.Clone(dicomFile.Dataset.InternalTransferSyntax); // DicomTranscoder transcoder = new DicomTranscoder(dicomFile.Dataset.InternalTransferSyntax, // DicomTransferSyntax.JPEGLSLossless); // await transcoder.Transcode(dicomFile).SaveAsync(filePath); //} //catch (Exception ee) //{ // throw; //} await _studyRepository.AddOrUpdateAsync(dicomStudy); await _seriesRepository.AddOrUpdateAsync(dicomSeries); await _instanceRepository.AddOrUpdateAsync(dicomInstance); //_studyRepository.SaveChanges(); return _mapper.Map(dicomInstance); } catch (Exception ex) { _logger.LogError(ex.Message); throw; } } public ICollection GetArchivedStudyList(List archivedStudyIds) { return _studyRepository.Find(s => archivedStudyIds.Contains(s.Id)).ProjectTo(_mapper.ConfigurationProvider).ToList(); } public string GetStudyPreview(Guid studyId) { DicomInstance dicomInstance = _instanceRepository .GetAll().FirstOrDefault(s => s.StudyId == studyId); if (dicomInstance != null) { DicomStudy dicomStudy = _studyRepository.FindSingleOrDefault(s => s.Id == dicomInstance.StudyId); if (dicomStudy != null) { return GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString()); } } return string.Empty; } public DicomStudyDTO GetStudyItem(Guid studyId) { return _mapper.Map(_studyRepository.FindSingleOrDefault(s => s.Id == studyId)); } public IEnumerable GetSeriesList(Guid studyId) { return _seriesRepository.Find(s => s.StudyId == studyId).OrderBy(s => s.SeriesNumber). ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) .ProjectTo(_mapper.ConfigurationProvider); } public IEnumerable GetSeriesWithLabelList(Guid studyId, string tpCode) { //return _seriesRepository.Find(s => s.StudyId == studyId).OrderBy(s => s.SeriesNumber). // ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) //.Sele var seriesList = _seriesRepository.Find(s => s.StudyId == studyId).OrderBy(s => s.SeriesNumber). ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) .ProjectTo(_mapper.ConfigurationProvider).ToList(); bool hasKeyInstance = false; var SeriesIdList = _imageLabelRepository.Find(u => u.TpCode == tpCode).Select(s => s.SeriesId).Distinct().ToList(); var instanceIdList = _imageLabelRepository.Find(u => u.TpCode == tpCode).Select(s => s.InstanceId).Distinct().ToList(); foreach (var item in seriesList) { if (SeriesIdList.Contains(item.Id)) { item.HasLabel = true; hasKeyInstance = true; } else item.HasLabel = false; } if (hasKeyInstance) { seriesList.Add(new DicomSeriesWithLabelDTO { KeySeries = true, Id = SeriesIdList[0], InstanceCount = instanceIdList.Count, HasLabel = true, Modality = seriesList[0].Modality, Description = "Key Series" }); } var idList = _instanceRepository.Find(s => s.StudyId == studyId).ToList();//.GroupBy(u => u.SeriesId); foreach (var item in seriesList) { if (item.KeySeries) { item.InstanceList = instanceIdList; } else { item.InstanceList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime).Select(u => u.Id).ToList(); } } return seriesList; } public string GetSeriesPreview(Guid seriesId) { DicomInstance dicomInstance = _instanceRepository .GetAll().FirstOrDefault(s => s.SeriesId == seriesId); if (dicomInstance != null) { DicomStudy dicomStudy = _studyRepository.FindSingleOrDefault(s => s.Id == dicomInstance.StudyId); if (dicomStudy != null) { return GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString()); } } return string.Empty;//new HttpResponseMessage(HttpStatusCode.NotFound); } public IEnumerable GetInstanceList(Guid seriesId) { return _instanceRepository.Find(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber). ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime) .ProjectTo(_mapper.ConfigurationProvider); } public IEnumerable GetInstanceIdList(Guid seriesId, string tpCode, bool? key) { if (key != null && key.HasValue && key.Value) { return _keyInstanceRepository.Find(s => s.TpCode == tpCode) .Select(t => t.InstanceId).Distinct(); } else return _instanceRepository.Find(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber) .Select(t => t.Id); } public string GetInstancePreview(Guid instanceId) { DicomInstance dicomInstance = _instanceRepository .GetAll().FirstOrDefault(s => s.Id == instanceId); if (dicomInstance != null) { DicomStudy dicomStudy = _studyRepository.FindSingleOrDefault(s => s.Id == dicomInstance.StudyId); if (dicomStudy != null) { return GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString()); } } return string.Empty;//new HttpResponseMessage(HttpStatusCode.NotFound); } public string GetInstanceContent(Guid instanceId) { DicomInstance dicomInstance = _instanceRepository .GetAll().FirstOrDefault(s => s.Id == instanceId); if (dicomInstance != null) { DicomStudy dicomStudy = _studyRepository.FindSingleOrDefault(s => s.Id == dicomInstance.StudyId); if (dicomStudy != null) { string path = string.Empty; if (dicomInstance.Anonymize) //被匿名化 { path = GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id + ".Anonymize"); } else path = GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString()); return path; } } return string.Empty; } #region private DicomStudy CreateDicomStudy(DicomDataset dataset, DicomTrialSiteSubjectInfo addtionalInfo) { string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString()); DicomStudy dicomStudy = _studyRepository.GetAll().FirstOrDefault(u => u.Id == studyId); //if (dicomStudy == null) //{ // dicomStudy = _studyRepository.GetAll().FirstOrDefault(s => s.StudyInstanceUid == studyInstanceUid); //} if (dicomStudy != null) return dicomStudy; dicomStudy = new DicomStudy { Id = studyId, StudyInstanceUid = studyInstanceUid, StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, DateTime.Now).TimeOfDay),//dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime), Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty), InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty), PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty), PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty), PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty), BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty), AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty), PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty), AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty), AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), SiteId = addtionalInfo.SiteId, TrialId = addtionalInfo.TrialId, SubjectId = addtionalInfo.SubjectId, SubjectVisitId = addtionalInfo.SubjectVisitId, IsDoubleReview = addtionalInfo.IsDoubleReview, Comment = addtionalInfo.Comment, SeriesCount = 0, InstanceCount = 0 }; #region Setting Code var last = _studyRepository.GetAll().Where(s => s.TrialId == addtionalInfo.TrialId).OrderByDescending(c => c.StudyCode).FirstOrDefault(); if (last != null) { var len = last.StudyCode.Length; if (len > 5 && int.TryParse(last.StudyCode.Substring(len - 5, 5), out var num)) { dicomStudy.StudyCode = "ST" + (++num).ToString().PadLeft(5, '0'); } else { return null; } } else { dicomStudy.StudyCode = "ST" + 1.ToString().PadLeft(5, '0'); } #endregion return dicomStudy; } private DicomSeries CreateDicomSeries(DicomDataset dataset, DicomStudy dicomStudy) { string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID); Guid seriesId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, seriesInstanceUid, dicomStudy.TrialId.ToString()); DicomSeries dicomSeries = null; dicomSeries = _seriesRepository.FindSingleOrDefault(t => t.Id == seriesId); if (dicomSeries != null) return dicomSeries; dicomSeries = new DicomSeries { Id = seriesId, StudyId = dicomStudy.Id, StudyInstanceUid = dicomStudy.StudyInstanceUid, SeriesInstanceUid = seriesInstanceUid, SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1), SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay), // dataset.GetDateTime(DicomTag.SeriesDate, DicomTag.SeriesTime), Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty), SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty), ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty), BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty), ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty), ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), SiteId = dicomStudy.SiteId, TrialId = dicomStudy.TrialId, SubjectId = dicomStudy.SubjectId, SubjectVisitId = dicomStudy.SubjectVisitId, InstanceCount = 0 }; ++dicomStudy.SeriesCount; return dicomSeries; } private DicomInstance CreateDicomInstance(DicomDataset dataset, DicomStudy dicomStudy, DicomSeries dicomSeries) { string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID); Guid instanceId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, dicomSeries.SeriesInstanceUid, sopInstanceUid, dicomStudy.TrialId.ToString()); DicomInstance dicomInstance = null; dicomInstance = _instanceRepository.FindSingleOrDefault(t => t.Id == instanceId); if (dicomInstance != null) return dicomInstance; dicomInstance = new DicomInstance { Id = instanceId, StudyId = dicomStudy.Id, SeriesId = dicomSeries.Id, SiteId = dicomStudy.SiteId, TrialId = dicomStudy.TrialId, SubjectId = dicomStudy.SubjectId, SubjectVisitId = dicomStudy.SubjectVisitId, StudyInstanceUid = dicomStudy.StudyInstanceUid, SeriesInstanceUid = dicomSeries.SeriesInstanceUid, SopInstanceUid = sopInstanceUid, InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1), InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, DateTime.Now).TimeOfDay), //dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime) CPIStatus = false, ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0), ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0), SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0), SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0), PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty), ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty), WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty), WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty), }; ++dicomStudy.InstanceCount; ++dicomSeries.InstanceCount; return dicomInstance; } #endregion private string CreateInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, Guid instanceId) { //string path = string.Empty; //if (SystemConfig.Share) //{ // WNetAddConnectionHelper.Connect(); // path = Path.Combine("W:\\IRC_DICOM_ARCHIVED", dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(), // dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString()); //} //else path = Path.Combine(_fileStorePath, dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(), // dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString()); var path = Path.Combine(_fileStorePath, dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(), dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString()); try { //_logger.LogInformation("检查文件夹是否存在:" + Directory.Exists(path)); if (!Directory.Exists(path)) { //_logger.LogInformation("不存在,创建文件夹:" + path); Directory.CreateDirectory(path); } //_logger.LogInformation("创建文件夹后,检查文件夹是否创建成功:" + Directory.Exists(path)); } catch (Exception ex) { //_logger.LogError("创建文件夹异常:" + ex.Message); throw; } return Path.Combine(path, instanceId.ToString() + ".dcm"); } private string GetInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, string instanceId) { return Path.Combine(_fileStorePath, dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(), dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString(), instanceId.ToString() + ".dcm"); } private void RemoveStudyDirectory(DicomStudy dicomStudy) { string path = Path.Combine(_fileStorePath, dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(), dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString()); if (Directory.Exists(path)) Directory.Delete(path, true); } public IEnumerable GetImageLabel(string tpCode) { return _imageLabelRepository.Find(s => s.TpCode == tpCode) .ProjectTo(_mapper.ConfigurationProvider); } public bool SaveImageLabelList(ImageLabelCommand imageLabelCommand) { var success = _imageLabelRepository.Delete(u => u.TpCode == imageLabelCommand.TpCode); _keyInstanceRepository.Delete(u => u.TpCode == imageLabelCommand.TpCode); if (imageLabelCommand.ImageLabelList.Count == 0) { return true; } else { foreach (var label in imageLabelCommand.ImageLabelList) { _imageLabelRepository.Add(new ImageLabel { TpCode = imageLabelCommand.TpCode, StudyId = label.StudyId, SeriesId = label.SeriesId, InstanceId = label.InstanceId, LabelValue = label.LabelValue }); _keyInstanceRepository.Add(new KeyInstance { TpCode = imageLabelCommand.TpCode, SeriesId = label.SeriesId, InstanceId = label.InstanceId, }); } success = _imageLabelRepository.SaveChanges(); return success; } } } }