public void TestKeyImages(int numberOfFrames, int numberOfMultiframeKeyImages, int numberOfSingleFrameKeyImages, bool doSplitting) { Assert.IsTrue(numberOfFrames == 0 || numberOfFrames > 1); Assert.IsTrue(numberOfMultiframeKeyImages <= numberOfFrames); const int numberOfSeries = 1; int instancesPerSeries = numberOfSingleFrameKeyImages + ((numberOfFrames > 0) ? 1 : 0); Assert.IsTrue(instancesPerSeries > 0); List <TestDataSource> dataSources = CreateMRStudyDataSources(numberOfSeries, instancesPerSeries, "1.2.3"); if (numberOfFrames > 0) { TestDataSource multiFrameDataSource = dataSources[0]; DicomAttributeCollection oldDataSet = multiFrameDataSource.File.DataSet; DicomAttributeCollection newDataSet = new DicomAttributeCollection(); DicomFile newFile = new DicomFile("", new DicomAttributeCollection(), newDataSet); //Yes this is the world's crappiest hack. base.SetupMultiframeXA(newDataSet, 512, 512, (uint)numberOfFrames); //because of an exception that gets thrown from the DateTimeParser newDataSet[DicomTags.StudyDate].SetNullValue(); newDataSet[DicomTags.StudyTime].SetNullValue(); newDataSet[DicomTags.SeriesDate].SetNullValue(); newDataSet[DicomTags.SeriesTime].SetNullValue(); newDataSet[DicomTags.ReferencedStudySequence].SetEmptyValue(); newDataSet[DicomTags.Modality].SetStringValue("MR"); newDataSet[DicomTags.StudyInstanceUid].SetStringValue(oldDataSet[DicomTags.StudyInstanceUid].ToString()); newDataSet[DicomTags.SeriesInstanceUid].SetStringValue(oldDataSet[DicomTags.SeriesInstanceUid].ToString()); dataSources[0] = new TestDataSource(newFile); } StudyTree studyTree = CreateStudyTree(ConvertToSops(dataSources)); KeyImageSerializer serializer = new KeyImageSerializer(); Patient patient = studyTree.Patients[0]; Study study = patient.Studies[0]; Series sourceSeries = study.Series[0]; List <IDisplaySet> allDisplaySets = new List <IDisplaySet>(); BasicDisplaySetFactory factory = new BasicDisplaySetFactory(); factory.SetStudyTree(studyTree); List <IDisplaySet> displaySets = factory.CreateDisplaySets(sourceSeries); allDisplaySets.AddRange(displaySets); List <DicomFile> presentationStates = new List <DicomFile>(); int numberOfMultiframeKeyImagesCreated = 0; foreach (IDisplaySet displaySet in displaySets) { foreach (IPresentationImage image in displaySet.PresentationImages) { Frame frame = ((IImageSopProvider)image).Frame; if (frame.ParentImageSop.NumberOfFrames > 1) { if (numberOfMultiframeKeyImagesCreated >= numberOfMultiframeKeyImages) { continue; } ++numberOfMultiframeKeyImagesCreated; } DicomSoftcopyPresentationState presentationState = DicomSoftcopyPresentationState.Create(image); //because of an exception that gets thrown from the DateTimeParser presentationState.DicomFile.DataSet[DicomTags.StudyDate].SetNullValue(); presentationState.DicomFile.DataSet[DicomTags.StudyTime].SetNullValue(); presentationState.DicomFile.DataSet[DicomTags.SeriesDate].SetNullValue(); presentationState.DicomFile.DataSet[DicomTags.SeriesTime].SetNullValue(); presentationStates.Add(presentationState.DicomFile); serializer.AddImage(frame, presentationState); } } List <DicomFile> files = serializer.Serialize(); List <TestDataSource> keyImageDataSources = ConvertToDataSources(files); List <Sop> keyImageSops = ConvertToSops(keyImageDataSources); keyImageSops.AddRange(ConvertToSops(ConvertToDataSources(presentationStates))); foreach (Sop sop in keyImageSops) { studyTree.AddSop(sop); } try { foreach (Series series in study.Series) { if (series.Modality != "KO") { continue; } List <IDisplaySet> keyImageDisplaySets; if (doSplitting) { factory.CreateSingleImageDisplaySets = true; keyImageDisplaySets = factory.CreateDisplaySets(series); if (keyImageDisplaySets.Count == 0) { factory.CreateSingleImageDisplaySets = false; keyImageDisplaySets = factory.CreateDisplaySets(series); } } else { keyImageDisplaySets = factory.CreateDisplaySets(series); } allDisplaySets.AddRange(keyImageDisplaySets); int numberOfKeyImages = numberOfMultiframeKeyImages + numberOfSingleFrameKeyImages; if (!doSplitting) { Assert.AreEqual(1, keyImageDisplaySets.Count, "There should be only one display set"); IDisplaySet keyImageDisplaySet = keyImageDisplaySets[0]; Assert.AreEqual(numberOfKeyImages, keyImageDisplaySet.PresentationImages.Count, "Expected {0} images", numberOfKeyImages); Assert.AreEqual(typeof(KOSelectionDocumentDisplaySetDescriptor), keyImageDisplaySet.Descriptor.GetType(), "Wrong display set descriptor type"); } else { Assert.AreEqual(numberOfKeyImages, keyImageDisplaySets.Count, "Expected {0} display sets", numberOfKeyImages); foreach (IDisplaySet keyImageDisplaySet in keyImageDisplaySets) { Assert.AreEqual(1, keyImageDisplaySet.PresentationImages.Count, "There should be only one presentation image"); IPresentationImage keyImage = keyImageDisplaySet.PresentationImages[0]; ImageSop sop = ((IImageSopProvider)keyImage).ImageSop; Assert.AreEqual(sourceSeries.SeriesInstanceUid, sop.SeriesInstanceUid, "Series Instance Uid is not that of the source series"); if (numberOfKeyImages == 1) { Assert.AreEqual(typeof(KOSelectionDocumentDisplaySetDescriptor), keyImageDisplaySet.Descriptor.GetType(), "Wrong display set descriptor type"); } else if (sop.NumberOfFrames > 1) { Assert.AreEqual(typeof(KOSelectionSingleFrameDisplaySetDescriptor), keyImageDisplaySet.Descriptor.GetType(), "Wrong display set descriptor type"); } else { Assert.AreEqual(typeof(KOSelectionSingleImageDisplaySetDescriptor), keyImageDisplaySet.Descriptor.GetType(), "Wrong display set descriptor type"); } } } } } finally { foreach (IDisplaySet displaySet in allDisplaySets) { displaySet.Dispose(); } studyTree.Dispose(); } }
public static void AddKeyImage(IPresentationImage image) { Platform.CheckForNullReference(image, "image"); Platform.CheckForNullReference(image.ImageViewer, "image.ImageViewer"); // TODO (CR Phoenix5 - Med): Clinical as well if (!PermissionsHelper.IsInRole(AuthorityTokens.KeyImages)) { throw new PolicyException(SR.ExceptionCreateKeyImagePermissionDenied); } var sopProvider = image as IImageSopProvider; if (sopProvider == null) { throw new ArgumentException("The image must be an IImageSopProvider.", "image"); } IDisplaySet displaySet = null; foreach (var set in image.ImageViewer.LogicalWorkspace.ImageSets) { if (set.Descriptor.Equals(image.ParentDisplaySet.ParentImageSet.Descriptor)) { foreach (var d in set.DisplaySets) { var displaySetDescriptor = d.Descriptor as KeyImageDisplaySetDescriptor; if (displaySetDescriptor != null && displaySetDescriptor.SourceStudy.StudyInstanceUid.Equals(sopProvider.Sop.StudyInstanceUid)) { displaySet = d; break; } } break; } } if (displaySet == null) { var displaySetDescriptor = new KeyImageDisplaySetDescriptor(new StudyIdentifier(sopProvider.ImageSop)); displaySet = new DisplaySet(displaySetDescriptor); bool displaySetAdded = false; foreach (var imageSet in image.ImageViewer.LogicalWorkspace.ImageSets) { if (imageSet.Descriptor.Equals(image.ParentDisplaySet.ParentImageSet.Descriptor)) { imageSet.DisplaySets.Add(displaySet); displaySetAdded = true; break; } } if (!displaySetAdded) { throw new ApplicationException(SR.MessageCreateKeyImageFailed); } } var presentationImage = image.CreateFreshCopy(); var presentationState = DicomSoftcopyPresentationState.Create(image); var basicImage = presentationImage as BasicPresentationImage; if (basicImage != null) { basicImage.PresentationState = presentationState; } displaySet.PresentationImages.Add(presentationImage); foreach (var imageBox in image.ImageViewer.PhysicalWorkspace.ImageBoxes) { if (imageBox.DisplaySet != null && imageBox.DisplaySet.Descriptor.Uid == displaySet.Descriptor.Uid) { var physicalImage = presentationImage.CreateFreshCopy(); presentationState = DicomSoftcopyPresentationState.Create(image); basicImage = physicalImage as BasicPresentationImage; if (basicImage != null) { basicImage.PresentationState = presentationState; } imageBox.DisplaySet.PresentationImages.Add(physicalImage); imageBox.Draw(); } } }
/// <summary> /// Creates all the SOP instances associated with the key object selection and the content presentation states. /// </summary> public IDictionary <IStudySource, List <DicomFile> > CreateSopInstances(NextSeriesNumberDelegate nextSeriesNumberDelegate) { if (!HasChanges || !Items.Any()) { return(new Dictionary <IStudySource, List <DicomFile> >(0)); } // update the author field Author = GetUserName(); // the series index ensures consistent series level data because we only create one KO series and one PR series per study var studyIndex = new Dictionary <string, StudyInfo>(); var secondaryCaptureImageFactory = new SecondaryCaptureImageFactory(nextSeriesNumberDelegate); var framePresentationStates = new List <KeyValuePair <KeyImageReference, PresentationStateReference> >(); // create presentation states for the images in the clipboard var presentationStates = new List <DicomSoftcopyPresentationState>(); foreach (var item in Items.Where(i => i.Item is IPresentationImage)) { var image = (IPresentationImage)item.Item; // if the item is a placeholder image (e.g. because source study wasn't available), simply reserialize the original sop references if (image is KeyObjectPlaceholderImage) { // because source study wasn't available, we don't have enough information to create the identical KO for that study // and thus an entry in the study index table is not needed var ko = (KeyObjectPlaceholderImage)image; framePresentationStates.Add(new KeyValuePair <KeyImageReference, PresentationStateReference>(ko.KeyImageReference, ko.PresentationStateReference)); continue; } var provider = image as IImageSopProvider; if (provider == null) { continue; } StudyInfo studyInfo; var studyInstanceUid = provider.ImageSop.StudyInstanceUid; if (!studyIndex.TryGetValue(studyInstanceUid, out studyInfo)) { studyIndex.Add(studyInstanceUid, studyInfo = new StudyInfo(provider, nextSeriesNumberDelegate)); // keep the previous series number if the one we know about is the same study as this new document // otherwise, pre-allocate a series number for the KO now (ensures the number will be lower than any SC and PR series) if (_parentStudyInstanceUid == studyInstanceUid && _seriesNumber.HasValue) { studyInfo.KeyObjectSeriesNumber = _seriesNumber.Value; } else { studyInfo.AllocateKeyObjectSeriesNumber(); } } // if the item doesn't have changes and the presentation state is DICOM, simply reserialize the original sop references if (!item.HasChanges() && image is IDicomPresentationImage) { var dicomPresentationState = ((IDicomPresentationImage)image).PresentationState as DicomSoftcopyPresentationState; framePresentationStates.Add(new KeyValuePair <KeyImageReference, PresentationStateReference>(provider.Frame, dicomPresentationState)); continue; } // if the image is not a permanent stored instance (i.e. it was dynamically generated), create a secondary capture from it if (!provider.Sop.DataSource.IsStored) { image = secondaryCaptureImageFactory.CreateSecondaryCapture(image); provider = (IImageSopProvider)image; } var presentationState = DicomSoftcopyPresentationState.IsSupported(image) ? DicomSoftcopyPresentationState.Create (image, ps => { ps.PresentationSeriesInstanceUid = studyInfo.PresentationSeriesUid; ps.PresentationSeriesNumber = studyInfo.PresentationSeriesNumber; ps.PresentationSeriesDateTime = studyInfo.PresentationSeriesDateTime; ps.PresentationInstanceNumber = studyInfo.GetNextPresentationInstanceNumber(); ps.SourceAETitle = provider.ImageSop.DataSource[DicomTags.SourceApplicationEntityTitle].ToString(); }) : null; if (presentationState != null) { presentationStates.Add(presentationState); } framePresentationStates.Add(new KeyValuePair <KeyImageReference, PresentationStateReference>(provider.Frame, presentationState)); } // serialize the key image document var serializer = new KeyImageSerializer(); serializer.Author = Author; serializer.Description = Description; serializer.DocumentTitle = DocumentTitle; serializer.SeriesDescription = SeriesDescription; foreach (var presentationFrame in framePresentationStates) { serializer.AddImage(presentationFrame.Key, presentationFrame.Value); } // collect all the SOP instances that were created (SC, PR and KO) var documents = new List <DicomFile>(); documents.AddRange(serializer.Serialize(koSeries => { var uid = koSeries.StudyInstanceUid; if (studyIndex.ContainsKey(uid)) { koSeries.SeriesDateTime = studyIndex[uid].KeyObjectSeriesDateTime; koSeries.SeriesNumber = studyIndex[uid].KeyObjectSeriesNumber; koSeries.SeriesInstanceUid = studyIndex[uid].KeyObjectSeriesUid; return(studyIndex[uid].DataSource); } return(null); } )); documents.AddRange(secondaryCaptureImageFactory.Files); documents.AddRange(presentationStates.Select(ps => ps.DicomFile)); // return the created instances grouped by study (and thus study origin/source) return(documents.GroupBy(f => (IStudySource)studyIndex[f.DataSet[DicomTags.StudyInstanceUid].ToString()]).ToDictionary(g => g.Key, g => g.ToList())); }
public IPresentationImage CreateSecondaryCapture(IPresentationImage image) { var imageSopProvider = image as IImageSopProvider; if (imageSopProvider == null) { const string msg = "image must implement IImageSopProvider"; throw new ArgumentException(msg, "image"); } SeriesInfo seriesInfo; var seriesKey = MakeSeriesKey(imageSopProvider.Frame.StudyInstanceUid, imageSopProvider.Sop.Modality); if (!_seriesInfo.TryGetValue(seriesKey, out seriesInfo)) { _seriesInfo[seriesKey] = seriesInfo = new SeriesInfo(_nextSeriesNumberDelegate.Invoke(imageSopProvider.Frame.StudyInstanceUid)); } var dcf = CreatePrototypeFile(imageSopProvider.Sop.DataSource); FillGeneralSeriesModule(dcf.DataSet, imageSopProvider.Frame, seriesInfo); FillScEquipmentModule(dcf.DataSet, Manufacturer, ManufacturersModelName, SoftwareVersions); FillFrameOfReferenceModule(dcf.DataSet, imageSopProvider.Frame); FillGeneralImageModule(dcf.DataSet, imageSopProvider.Frame, seriesInfo); FillScImageModule(dcf.DataSet, imageSopProvider.Frame); FillImagePlaneModule(dcf.DataSet, imageSopProvider.Frame); FillSopCommonModule(dcf.DataSet, SopClass.SecondaryCaptureImageStorageUid); FillAuxiliaryImageData(dcf.DataSet, imageSopProvider.Frame); if (image is GrayscalePresentationImage) { FillModalityLutModule(dcf.DataSet, imageSopProvider.Frame); FillVoiLutModule(dcf.DataSet, imageSopProvider.Frame); // create image pixel last - this method may need to override some attributes set previously CreateImagePixelModuleGrayscale(dcf.DataSet, imageSopProvider.Frame); } else if (image is ColorPresentationImage) { // create image pixel last - this method may need to override some attributes set previously CreateImagePixelModuleColor(dcf.DataSet, imageSopProvider.Frame); } else { // create image pixel last - this method may need to override some attributes set previously CreateImagePixelModuleRasterRgb(dcf.DataSet, image); } dcf.MediaStorageSopClassUid = dcf.DataSet[DicomTags.SopClassUid].ToString(); dcf.MediaStorageSopInstanceUid = dcf.DataSet[DicomTags.SopInstanceUid].ToString(); _files.Add(dcf); using (var sop = new ImageSop(new LocalSopDataSource(dcf))) { var secondaryCapture = PresentationImageFactory.Create(sop).Single(); try { var presentationState = DicomSoftcopyPresentationState.IsSupported(image) ? DicomSoftcopyPresentationState.Create(image) : null; if (presentationState != null) { presentationState.DeserializeOptions |= DicomSoftcopyPresentationStateDeserializeOptions.IgnoreImageRelationship; presentationState.Deserialize(secondaryCapture); // override the spatial transform of the secondary capture because the presentation state doesn't save exact parameters var sourceTransform = image as ISpatialTransformProvider; var targetTransform = secondaryCapture as ISpatialTransformProvider; if (sourceTransform != null && targetTransform != null) { targetTransform.SpatialTransform.CenterOfRotationXY = sourceTransform.SpatialTransform.CenterOfRotationXY; targetTransform.SpatialTransform.FlipX = sourceTransform.SpatialTransform.FlipX; targetTransform.SpatialTransform.FlipY = sourceTransform.SpatialTransform.FlipY; targetTransform.SpatialTransform.RotationXY = sourceTransform.SpatialTransform.RotationXY; targetTransform.SpatialTransform.Scale = sourceTransform.SpatialTransform.Scale; targetTransform.SpatialTransform.TranslationX = sourceTransform.SpatialTransform.TranslationX; targetTransform.SpatialTransform.TranslationY = sourceTransform.SpatialTransform.TranslationY; var sourceImageTransform = sourceTransform as IImageSpatialTransform; var targetImageTransform = targetTransform as IImageSpatialTransform; if (sourceImageTransform != null && targetImageTransform != null) { targetImageTransform.ScaleToFit = sourceImageTransform.ScaleToFit; } } } // force a render to update the client rectangle and scaling of the image secondaryCapture.RenderImage(image.ClientRectangle).Dispose(); } catch (Exception ex) { Platform.Log(LogLevel.Warn, ex, "An error has occurred while deserializing the image presentation state."); } return(secondaryCapture); } }