예제 #1
0
        protected static IPresentationImage GetImage(ImageKey key)
        {
            string filename = string.Format(_testImagePathFormat, key.ToString().ToLower());

            try
            {
                LocalSopDataSource dataSource = new LocalSopDataSource(filename);
                ImageSop           imageSop   = new ImageSop(dataSource);
                IPresentationImage theOne     = null;
                foreach (IPresentationImage image in PresentationImageFactory.Create(imageSop))
                {
                    if (theOne == null)
                    {
                        theOne = image;
                        continue;
                    }
                    image.Dispose();
                }
                imageSop.Dispose();
                return(theOne);
            }
            catch (Exception ex)
            {
                throw new FileNotFoundException("Unable to load requested test image. Please check that the assembly has been built.", filename, ex);
            }
        }
예제 #2
0
		private static IEnumerable<IClipboardItem> CreateClipboardItems(StudyTree studyTree, Sop keyObjectSelectionDocument)
		{
			Platform.CheckTrue(keyObjectSelectionDocument.SopClassUid == SopClass.KeyObjectSelectionDocumentStorageUid, "SOP Class must be Key Object Selection Document Storage");

			var dummyContext = new KeyImageInformation(); // just need an instance of creating items
			var factory = new PresentationImageFactory(studyTree);
			foreach (var image in factory.CreateImages(keyObjectSelectionDocument))
			{
				var presentationStateInstanceUid = string.Empty;

				// set the deserialize interactive flag on the presentation state
				var dicomPresentationImage = image as IDicomPresentationImage;
				if (dicomPresentationImage != null)
				{
					var presentationState = dicomPresentationImage.PresentationState as DicomSoftcopyPresentationState;
					if (presentationState != null)
					{
						presentationState.DeserializeOptions |= DicomSoftcopyPresentationStateDeserializeOptions.InteractiveAnnotations;
						presentationStateInstanceUid = presentationState.PresentationSopInstanceUid;
					}
				}

				var item = dummyContext.CreateKeyImageItem(image, true);
				item.AssignSourceInfo(Guid.NewGuid(), keyObjectSelectionDocument.SopInstanceUid, presentationStateInstanceUid);
				yield return item;
			}
		}
예제 #3
0
            private static bool IsSupportedGantryTilt(IList <IFrameReference> frames)
            {
                try
                {
                    using (IPresentationImage firstImage = PresentationImageFactory.Create(frames[0].Frame))
                    {
                        using (IPresentationImage lastImage = PresentationImageFactory.Create(frames[frames.Count - 1].Frame))
                        {
                            // neither of these should return null since we already checked for image orientation and position (patient)
                            DicomImagePlane firstImagePlane = DicomImagePlane.FromImage(firstImage);
                            DicomImagePlane lastImagePlane  = DicomImagePlane.FromImage(lastImage);

                            Vector3D stackZ = lastImagePlane.PositionPatientTopLeft - firstImagePlane.PositionPatientTopLeft;
                            Vector3D imageX = firstImagePlane.PositionPatientTopRight - firstImagePlane.PositionPatientTopLeft;

                            if (!stackZ.IsOrthogonalTo(imageX, _gantryTiltTolerance))
                            {
                                // this is a gantry slew (gantry tilt about Y axis)
                                return(false);
                            }
                        }
                    }
                    return(true);
                }
                catch (Exception ex)
                {
                    Platform.Log(LogLevel.Debug, ex, "Unexpected exception encountered while checking for supported gantry tilts");
                    return(false);
                }
            }
        public void TestAgentCodeSequence()
        {
            var agent1 = new CodeSequenceMacro {
                CodeMeaning = "Contrastinol", CodeValue = "123", CodingSchemeDesignator = "ABC"
            };
            var agent2 = new CodeSequenceMacro {
                CodeMeaning = "Bolusinate", CodeValue = "456", CodingSchemeDesignator = "DEF"
            };

            var dataset = TestDataSource.CreateImageSopDataSource();

            dataset[DicomTags.ContrastBolusAgent].SetStringValue(@"Contrastinol and Bolusinate");
            dataset[DicomTags.ContrastBolusAgentSequence].AddSequenceItem(agent1.DicomSequenceItem);

            using (var sop = (ImageSop)Sop.Create(dataset))
                using (var images = new DisposableList <IPresentationImage>(PresentationImageFactory.Create(sop)))
                {
                    Assert.AreEqual(@"Contrastinol", _annotationItems[_cbAgent].GetAnnotationText(images[0]));

                    dataset[DicomTags.ContrastBolusAgentSequence].AddSequenceItem(agent2.DicomSequenceItem);
                    Assert.AreEqual(@"Contrastinol\Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[0]));

                    agent1.CodeMeaning = string.Empty;
                    Assert.AreEqual(@"123 (ABC)\Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[0]));

                    agent2.CodeMeaning = string.Empty;
                    Assert.AreEqual(@"Contrastinol and Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[0]));

                    dataset[DicomTags.ContrastBolusAgent].SetNullValue();
                    Assert.AreEqual(@"123 (ABC)\456 (DEF)", _annotationItems[_cbAgent].GetAnnotationText(images[0]));
                }
        }
예제 #5
0
        private static IEnumerable <IClipboardItem> CreateClipboardItems(StudyTree studyTree, Sop keyObjectSelectionDocument)
        {
            Platform.CheckTrue(keyObjectSelectionDocument.SopClassUid == SopClass.KeyObjectSelectionDocumentStorageUid, "SOP Class must be Key Object Selection Document Storage");

            var dummyContext = new KeyImageInformation();             // just need an instance of creating items
            var factory      = new PresentationImageFactory(studyTree);

            foreach (var image in factory.CreateImages(keyObjectSelectionDocument))
            {
                var presentationStateInstanceUid = string.Empty;

                // set the deserialize interactive flag on the presentation state
                var dicomPresentationImage = image as IDicomPresentationImage;
                if (dicomPresentationImage != null)
                {
                    var presentationState = dicomPresentationImage.PresentationState as DicomSoftcopyPresentationState;
                    if (presentationState != null)
                    {
                        presentationState.DeserializeOptions |= DicomSoftcopyPresentationStateDeserializeOptions.InteractiveAnnotations;
                        presentationStateInstanceUid          = presentationState.PresentationSopInstanceUid;
                    }
                }

                var item = dummyContext.CreateKeyImageItem(image, true);
                item.AssignSourceInfo(Guid.NewGuid(), keyObjectSelectionDocument.SopInstanceUid, presentationStateInstanceUid);
                yield return(item);
            }
        }
        private static void TestVolume(bool signed, VolumeFunction f, IEnumerable <IVolumeSlicerParams> slicerParams, string testName, ImageKernelFunction imageKernel, VolumeKernelFunction volumeKernel)
        {
            const int      FULL_SCALE         = 65535;
            VolumeFunction normalizedFunction = f.Normalize(100);

            using (Volume volume = normalizedFunction.CreateVolume(100, signed))
            {
                float offset = signed ? -32768 : 0;
                foreach (IVolumeSlicerParams slicing in slicerParams)
                {
                    List <double> list = new List <double>();
                    using (VolumeSlicer slicer = new VolumeSlicer(volume, slicing, DicomUid.GenerateUid().UID))
                    {
                        foreach (ISopDataSource slice in slicer.CreateSlices())
                        {
                            using (ImageSop imageSop = new ImageSop(slice))
                            {
                                foreach (IPresentationImage image in PresentationImageFactory.Create(imageSop))
                                {
                                    IImageSopProvider     imageSopProvider     = (IImageSopProvider)image;
                                    IImageGraphicProvider imageGraphicProvider = (IImageGraphicProvider)image;
                                    DicomImagePlane       dip = DicomImagePlane.FromImage(image);

                                    for (int y = 1; y < imageSopProvider.Frame.Rows - 1; y++)
                                    {
                                        for (int x = 1; x < imageSopProvider.Frame.Columns - 1; x++)
                                        {
                                            // pixels on the extreme sides of the volume tend to have more interpolation error due to MPR padding values
                                            Vector3D vector = dip.ConvertToPatient(new PointF(x, y));                                             // +new Vector3D(-0.5f, -0.5f, 0);
                                            if (Between(vector.X, 1, 98) && Between(vector.Y, 1, 98) && Between(vector.Z, 1, 98))
                                            {
                                                float expected = volumeKernel.Invoke(normalizedFunction, vector.X, vector.Y, vector.Z) + offset;
                                                float actual   = imageKernel.Invoke(imageGraphicProvider.ImageGraphic.PixelData, x, y);
                                                list.Add(Math.Abs(expected - actual));
                                            }
                                        }
                                    }

                                    image.Dispose();
                                }
                            }
                            slice.Dispose();
                        }
                    }

                    Statistics stats = new Statistics(list);
                    Trace.WriteLine(string.Format("Testing {0}", testName));
                    Trace.WriteLine(string.Format("\tFunction/Slicing: {0} / {1}", normalizedFunction.Name, slicing.Description));
                    Trace.WriteLine(string.Format("\t       Pixel Rep: {0}", signed ? "signed" : "unsigned"));
                    Trace.WriteLine(string.Format("\t Voxels Compared: {0}", list.Count));
                    Trace.WriteLine(string.Format("\t      Mean Delta: {0:f2} ({1:p2} of full scale)", stats.Mean, stats.Mean / FULL_SCALE));
                    Trace.WriteLine(string.Format("\t    StdDev Delta: {0:f2} ({1:p2} of full scale)", stats.StandardDeviation, stats.StandardDeviation / FULL_SCALE));
                    Assert.Less(stats.Mean, FULL_SCALE * 0.05, "Mean delta exceeds 5% of full scale ({0})", FULL_SCALE);
                    Assert.Less(stats.StandardDeviation, FULL_SCALE * 0.05, "StdDev delta exceeds 5% of full scale ({0})", FULL_SCALE);
                }
            }
        }
예제 #7
0
 private void FillPresentationImages()
 {
     foreach (MprSliceSop sop in _sliceSet.SliceSops)
     {
         foreach (IPresentationImage image in PresentationImageFactory.Create(sop))
         {
             base.PresentationImages.Add(image);
         }
     }
 }
        public void TestNil()
        {
            var dataset = TestDataSource.CreateImageSopDataSource();

            using (var sop = (ImageSop)Sop.Create(dataset))
                using (var images = new DisposableList <IPresentationImage>(PresentationImageFactory.Create(sop)))
                {
                    Assert.IsEmpty(_annotationItems[_cbAgent].GetAnnotationText(images[0]));
                }
        }
예제 #9
0
        public Bitmap GetBitmap(int n)
        {
            IPresentationImage presentationImage =
                PresentationImageFactory.Create(ImageSop.Frames[n]);

            int width  = imageSop.Frames[n].Columns;
            int height = imageSop.Frames[n].Rows;

            return(presentationImage.DrawToBitmap(width, height));
        }
예제 #10
0
 private static DisposableList <IPresentationImage> CreateImages(DicomFile dicomFile)
 {
     using (var dataSource = new LocalSopDataSource(dicomFile))
     {
         using (var sop = new ImageSop(dataSource))
         {
             return(new DisposableList <IPresentationImage>(PresentationImageFactory.Create(sop)));
         }
     }
 }
예제 #11
0
        protected static void ValidateVolumeSlicePoints(Volumes.Volume volume, IVolumeSlicerParams slicerParams, IList <KnownSample> expectedPoints,
                                                        double xAxialGantryTilt, double yAxialGantryTilt, bool gantryTiltInDegrees)
        {
            if (gantryTiltInDegrees)
            {
                xAxialGantryTilt *= Math.PI / 180;
                yAxialGantryTilt *= Math.PI / 180;
            }

            Trace.WriteLine(string.Format("Using slice plane: {0}", slicerParams.Description));
            using (VolumeSlicer slicer = new VolumeSlicer(volume, slicerParams))
            {
                foreach (ISopDataSource slice in slicer.CreateSliceSops())
                {
                    using (ImageSop imageSop = new ImageSop(slice))
                    {
                        foreach (IPresentationImage image in PresentationImageFactory.Create(imageSop))
                        {
                            IImageGraphicProvider imageGraphicProvider = (IImageGraphicProvider)image;
                            DicomImagePlane       dip = DicomImagePlane.FromImage(image);

                            foreach (KnownSample sample in expectedPoints)
                            {
                                Vector3D patientPoint = sample.Point;
                                if (xAxialGantryTilt != 0 && yAxialGantryTilt == 0)
                                {
                                    float cos = (float)Math.Cos(xAxialGantryTilt);
                                    float sin = (float)Math.Sin(xAxialGantryTilt);
                                    patientPoint = new Vector3D(patientPoint.X,
                                                                patientPoint.Y * cos + (xAxialGantryTilt > 0 ? 100 * sin : 0),
                                                                patientPoint.Z / cos - patientPoint.Y * sin - (xAxialGantryTilt > 0 ? 100 * sin * sin / cos : 0));
                                }
                                else if (yAxialGantryTilt != 0)
                                {
                                    Assert.Fail("Unit test not designed to work with gantry tilts about Y (i.e. slew)");
                                }

                                Vector3D slicedPoint = dip.ConvertToImagePlane(patientPoint);
                                if (slicedPoint.Z > -0.5 && slicedPoint.Z < 0.5)
                                {
                                    int actual = imageGraphicProvider.ImageGraphic.PixelData.GetPixel((int)slicedPoint.X, (int)slicedPoint.Y);
                                    Trace.WriteLine(string.Format("Sample {0} @{1} (SLICE: {2}; PATIENT: {3})", actual, FormatVector(sample.Point), FormatVector(slicedPoint), FormatVector(patientPoint)));
                                    Assert.AreEqual(sample.Value, actual, "Wrong colour sample @{0}", sample.Point);
                                }
                            }

                            image.Dispose();
                        }
                    }
                    slice.Dispose();
                }
            }
        }
        public void TestAgentDescriptorOnly()
        {
            var dataset = TestDataSource.CreateImageSopDataSource();

            dataset[DicomTags.ContrastBolusAgent].SetStringValue(@"Contrastinol and Bolusinate");

            using (var sop = (ImageSop)Sop.Create(dataset))
                using (var images = new DisposableList <IPresentationImage>(PresentationImageFactory.Create(sop)))
                {
                    Assert.AreEqual(@"Contrastinol and Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[0]));
                }
        }
예제 #13
0
        private static IPresentationImage CreateResultImage(Bitmap bitmap, ColorMode colorMode, string filePath)
        {
            DicomFile file = new DicomFile();

            file.DataSet[DicomTags.BitsAllocated].SetInt32(0, 8);
            file.DataSet[DicomTags.BitsStored].SetInt32(0, 8);
            file.DataSet[DicomTags.HighBit].SetInt32(0, 7);
            file.DataSet.RemoveAttribute(DicomTags.WindowCenter);
            file.DataSet.RemoveAttribute(DicomTags.WindowWidth);
            file.DataSet[DicomTags.PixelSpacing].SetInt32(0, 0);
            file.DataSet[DicomTags.RescaleIntercept].SetInt32(0, 0);
            file.DataSet[DicomTags.RescaleSlope].SetInt32(0, 1);
            file.DataSet[DicomTags.Rows].SetInt32(0, bitmap.Height);
            file.DataSet[DicomTags.Columns].SetInt32(0, bitmap.Width);
            file.DataSet[DicomTags.PixelRepresentation].SetInt32(0, 0);
            file.DataSet[DicomTags.NumberOfFrames].SetInt32(0, 1);
            file.DataSet[DicomTags.PhotometricInterpretation].SetStringValue("MONOCHROME2");
            file.DataSet[DicomTags.SopClassUid].SetStringValue("1.2.840.10008.5.1.4.1.1.7");
            file.DataSet[DicomTags.Modality].SetStringValue("OT");
            file.DataSet[DicomTags.TransferSyntaxUid].SetStringValue("1.2.840.10008.1.2");
            file.DataSet[DicomTags.StudyId].SetStringValue("1");
            file.DataSet[DicomTags.StudyInstanceUid].SetStringValue("1.2.276.0.7230010.3.1.2.2866517296.296.1377417571.2");
            file.DataSet[DicomTags.SeriesNumber].SetInt32(0, 1);
            file.DataSet[DicomTags.SamplesPerPixel].SetInt32(0, 1);
            file.DataSet[DicomTags.SeriesInstanceUid].SetStringValue(" 1.2.276.0.7230010.3.1.3.2866517296.296.1377417571.3");
            file.DataSet[DicomTags.SopInstanceUid].SetStringValue(DicomUid.GenerateUid().UID);
            file.DataSet[DicomTags.PixelData].Values = null;
            byte[] pixelData = BitMapUtility.GetBitmap(bitmap, colorMode);
            file.DataSet[DicomTags.PixelData].Values = pixelData;
            file.MediaStorageSopClassUid             = file.DataSet[DicomTags.SopClassUid];
            file.MediaStorageSopInstanceUid          = file.DataSet[DicomTags.SopInstanceUid];
            string fileName = string.Format("{0}\\{1}", filePath, DateTime.Now.Ticks.ToString());

            file.Save(fileName);
            var dataSource = new LocalSopDataSource(file);
            Sop dstSop     = Sop.Create(dataSource);
            IPresentationImage presentation = (PresentationImageFactory.Create((ImageSop)dstSop))[0];

            if (presentation is IAnnotationLayoutProvider)
            {
                foreach (AnnotationBox box in ((IAnnotationLayoutProvider)presentation).AnnotationLayout.AnnotationBoxes)
                {
                    box.Visible = false;
                }
            }

            return(presentation);
        }
        private static DisplaySet CreateDisplaySet(IEnumerable <ImageSop> sops)
        {
            var descriptor = new XDisplaySetDescriptor(CollectionUtils.FirstElement(sops).DataSource);
            var displaySet = new DisplaySet(descriptor);

            foreach (var sop in sops)
            {
                foreach (var image in PresentationImageFactory.Create(sop))
                {
                    if (image is IVoiLutProvider)
                    {
                        ((IVoiLutProvider)image).VoiLutManager.InstallVoiLut(new IdentityVoiLinearLut());
                    }
                    displaySet.PresentationImages.Add(image);
                }
            }
            return(displaySet);
        }
예제 #15
0
    private static Bitmap DrawDefaultFrame(Frame f)
    {
        var presentationImage = PresentationImageFactory.Create(f);
        var bitmap            = presentationImage.DrawToBitmap(f.Columns, f.Rows);

        try
        {
            var bmp = presentationImage.DrawToBitmap(f.Columns, f.Rows);
            if (f.Columns == f.Rows)
            {
                if (f.Columns < 512)
                {
                    bmp = ResizeBitmap(bmp, 512, 512);
                }
            }
            return(bmp);
        }
        catch { return(null); }
    }
예제 #16
0
 /// <summary>
 /// Gets the initial VOI LUT for the source frames (base or overlay). This is NOT the LUT used on the fusion image! See <see cref="InstallVoiLut"/>.
 /// </summary>
 private static IVoiLut GetInitialVoiLut(Frame frame)
 {
     if (frame != null)
     {
         using (var image = PresentationImageFactory.Create(frame))
         {
             var voiLut = InitialVoiLutProvider.Instance.GetLut(image);
             if (voiLut == null && image is IImageGraphicProvider)
             {
                 var pixelData = ((IImageGraphicProvider)image).ImageGraphic.PixelData;
                 if (pixelData is GrayscalePixelData)
                 {
                     voiLut = new MinMaxPixelCalculatedLinearLut((GrayscalePixelData)pixelData);
                 }
             }
             return(voiLut);
         }
     }
     return(null);
 }
예제 #17
0
        public DicomElement(string filePath)
        {
            this.FilePath = filePath;

            DicomFile = new DicomFile(filePath);
            DicomFile.Load();

            ImageSop = new ClearCanvas.ImageViewer.StudyManagement.LocalImageSop(filePath);

            PresentationImage =
                PresentationImageFactory.Create(ImageSop.Frames[1]);

            int width  = ImageSop.Frames[1].Columns;
            int height = ImageSop.Frames[1].Rows;

            this.Bitmap = PresentationImage.DrawToBitmap(width, height);
            if (DicomFile.DataSet[DicomTags.InstanceNumber] == null)
            {
                throw new Exception("Tag 'Instance Number' not found!");
            }
            this.InstanceNumber = DicomFile.DataSet[DicomTags.InstanceNumber].GetInt32(0, 1);
        }
예제 #18
0
    private static Bitmap DrawLutFrame(Frame f, double ww, double wc)
    {
        IPresentationImage pres     = PresentationImageFactory.Create(f);
        IVoiLutProvider    provider = ((IVoiLutProvider)pres);
        IVoiLutManager     manager  = provider.VoiLutManager;
        var linearLut = manager.VoiLut as IVoiLutLinear;

        if (linearLut != null)
        {
            var standardLut = linearLut as IBasicVoiLutLinear;

            if (standardLut == null)
            {
                var installLut = new BasicVoiLutLinear(ww, wc);

                manager.InstallVoiLut(installLut);
            }
            else
            {
                standardLut.WindowWidth  = ww;
                standardLut.WindowCenter = wc;
            }
            provider.Draw();
        }
        try
        {
            var bmp = pres.DrawToBitmap(f.Columns, f.Rows);
            if (f.Columns == f.Rows)
            {
                if (f.Columns < 512)
                {
                    bmp = ResizeBitmap(bmp, 512, 512);
                }
            }
            return(bmp);
        }
        catch { return(null); }
    }
        public void TestEnhancedAgentCodeSequence()
        {
            var agent1 = new CodeSequenceMacro {
                CodeMeaning = "Contrastinol", CodeValue = "123", CodingSchemeDesignator = "ABC"
            };
            var agent2 = new CodeSequenceMacro {
                CodeMeaning = "Bolusinate", CodeValue = "456", CodingSchemeDesignator = "DEF"
            };
            var agent3 = new CodeSequenceMacro {
                CodeMeaning = "Dilithium", CodeValue = "789", CodingSchemeDesignator = "GHI"
            };

            var usageFrame1 = new ContrastBolusUsageFunctionalGroup
            {
                ContrastBolusUsageSequence = new[]
                {
                    new ContrastBolusUsageSequenceItem {
                        ContrastBolusAgentNumber = 1
                    },
                    new ContrastBolusUsageSequenceItem {
                        ContrastBolusAgentNumber = 3
                    }
                }
            };
            var usageFrame2 = new ContrastBolusUsageFunctionalGroup
            {
                ContrastBolusUsageSequence = new[]
                {
                    new ContrastBolusUsageSequenceItem {
                        ContrastBolusAgentNumber = 2
                    }
                }
            };
            var usageFrame3 = new ContrastBolusUsageFunctionalGroup(new DicomSequenceItem())
            {
                ContrastBolusUsageSequence = new[]
                {
                    new ContrastBolusUsageSequenceItem {
                        ContrastBolusAgentNumber = 999
                    },
                    new ContrastBolusUsageSequenceItem {
                        ContrastBolusAgentNumber = 2
                    }
                }
            };
            var usageFrame4 = new ContrastBolusUsageFunctionalGroup(new DicomSequenceItem());

            var dataset = TestDataSource.CreateImageSopDataSource(4);

            dataset[DicomTags.ContrastBolusAgent].SetStringValue(@"Contrastinol and Bolusinate");
            dataset[DicomTags.ContrastBolusAgentSequence].AddSequenceItem(agent1.DicomSequenceItem);
            dataset[DicomTags.ContrastBolusAgentSequence].AddSequenceItem(agent2.DicomSequenceItem);
            dataset[DicomTags.ContrastBolusAgentSequence].AddSequenceItem(agent3.DicomSequenceItem);
            dataset[DicomTags.PerFrameFunctionalGroupsSequence].AddSequenceItem(usageFrame1.DicomSequenceItem);
            dataset[DicomTags.PerFrameFunctionalGroupsSequence].AddSequenceItem(usageFrame2.DicomSequenceItem);
            dataset[DicomTags.PerFrameFunctionalGroupsSequence].AddSequenceItem(usageFrame3.DicomSequenceItem);
            dataset[DicomTags.PerFrameFunctionalGroupsSequence].AddSequenceItem(usageFrame4.DicomSequenceItem);

            agent1.DicomSequenceItem[DicomTags.ContrastBolusAgentNumber].SetInt32(0, 1);
            agent2.DicomSequenceItem[DicomTags.ContrastBolusAgentNumber].SetInt32(0, 2);
            agent3.DicomSequenceItem[DicomTags.ContrastBolusAgentNumber].SetInt32(0, 3);

            using (var sop = (ImageSop)Sop.Create(dataset))
                using (var images = new DisposableList <IPresentationImage>(PresentationImageFactory.Create(sop)))
                {
                    Assert.AreEqual(@"Contrastinol\Dilithium", _annotationItems[_cbAgent].GetAnnotationText(images[0]), "Frame 1");
                    Assert.AreEqual(@"Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[1]), "Frame 2");
                    Assert.AreEqual(@"Bolusinate", _annotationItems[_cbAgent].GetAnnotationText(images[2]), "Frame 3");
                    Assert.IsEmpty(_annotationItems[_cbAgent].GetAnnotationText(images[3]), "Frame 4");

                    agent1.CodeMeaning = string.Empty;
                    agent2.CodeMeaning = string.Empty;
                    Assert.AreEqual(@"123 (ABC)\Dilithium", _annotationItems[_cbAgent].GetAnnotationText(images[0]), "Frame 1");
                    Assert.AreEqual(@"456 (DEF)", _annotationItems[_cbAgent].GetAnnotationText(images[1]), "Frame 2");
                    Assert.AreEqual(@"456 (DEF)", _annotationItems[_cbAgent].GetAnnotationText(images[2]), "Frame 3");
                    Assert.IsEmpty(_annotationItems[_cbAgent].GetAnnotationText(images[3]), "Frame 4");

                    agent3.CodeMeaning = string.Empty;
                    Assert.AreEqual(@"123 (ABC)\789 (GHI)", _annotationItems[_cbAgent].GetAnnotationText(images[0]));
                    Assert.AreEqual(@"456 (DEF)", _annotationItems[_cbAgent].GetAnnotationText(images[1]), "Frame 2");
                    Assert.AreEqual(@"456 (DEF)", _annotationItems[_cbAgent].GetAnnotationText(images[2]), "Frame 3");
                    Assert.IsEmpty(_annotationItems[_cbAgent].GetAnnotationText(images[3]), "Frame 4");
                }
        }
        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);
            }
        }
        /// <summary>
        /// Reference implementation of the image fusion operator.
        /// </summary>
        private static IPresentationImage Fuse(IPresentationImage baseImage, IPresentationImage overlayImage, IColorMap colorMap, float opacity, bool thresholding)
        {
            Platform.CheckTrue(baseImage is IImageSopProvider, "baseImage must be a IImageSopProvider.");
            Platform.CheckTrue(overlayImage is IImageSopProvider, "overlayImage must be a IImageSopProvider.");
            Platform.CheckTrue(baseImage is IImageGraphicProvider, "baseImage must be a IImageGraphicProvider.");
            Platform.CheckTrue(overlayImage is IImageGraphicProvider, "overlayImage must be a IImageGraphicProvider.");

            var baseImageSopProvider        = (IImageSopProvider)baseImage;
            var baseImageGraphicProvider    = (IImageGraphicProvider)baseImage;
            var overlayImageSopProvider     = (IImageSopProvider)overlayImage;
            var overlayImageGraphicProvider = (IImageGraphicProvider)overlayImage;
            var rows      = baseImageSopProvider.Frame.Rows;
            var cols      = baseImageSopProvider.Frame.Columns;
            var pixelData = new byte[3 * rows * cols];

            colorMap.MinInputValue = ushort.MinValue;
            colorMap.MaxInputValue = ushort.MaxValue;

            // this here is the magic
            baseImageGraphicProvider.ImageGraphic.PixelData.ForEachPixel(
                (n, x, y, i) =>
            {
                // and this is why the base and overlay slices must be unsigned precisely coincident
                var patientLocation   = baseImageSopProvider.Frame.ImagePlaneHelper.ConvertToPatient(new PointF(x, y));
                var overlayCoordinate = overlayImageSopProvider.Frame.ImagePlaneHelper.ConvertToImagePlane(patientLocation);
                var baseValue         = (ushort)baseImageGraphicProvider.ImageGraphic.PixelData.GetPixel(i);
                var overlayValue      = overlayImageGraphicProvider.ImageGraphic.PixelData.GetPixel((int)overlayCoordinate.X, (int)overlayCoordinate.Y);

                // the fusion operator: output = underlyingGrey*(1-alpha) + overlayingColour*(alpha) (see DICOM 2009 PS 3.4 N.2.4.3)
                var compositeColor   = ToRgbVectorFromGrey(baseValue) * (1 - opacity) + ToRgbVector(colorMap[overlayValue]) * opacity;
                pixelData[3 * n]     = (byte)compositeColor.X;
                pixelData[3 * n + 1] = (byte)compositeColor.Y;
                pixelData[3 * n + 2] = (byte)compositeColor.Z;
            });

            var dicomFile = new DicomFile();
            var dataset   = dicomFile.DataSet;

            dataset[DicomTags.PatientId].SetStringValue(baseImageSopProvider.ImageSop.PatientId);
            dataset[DicomTags.PatientsName].SetStringValue(baseImageSopProvider.ImageSop.PatientsName);
            dataset[DicomTags.StudyId].SetStringValue(baseImageSopProvider.ImageSop.StudyId);
            dataset[DicomTags.StudyInstanceUid].SetStringValue(baseImageSopProvider.ImageSop.StudyInstanceUid);
            dataset[DicomTags.SeriesDescription].SetStringValue(baseImageSopProvider.ImageSop.SeriesDescription);
            dataset[DicomTags.SeriesNumber].SetInt32(0, 9001);
            dataset[DicomTags.SeriesInstanceUid].SetStringValue(DicomUid.GenerateUid().UID);
            dataset[DicomTags.SopInstanceUid].SetStringValue(DicomUid.GenerateUid().UID);
            dataset[DicomTags.SopClassUid].SetStringValue(ModalityConverter.ToSopClassUid(Modality.SC));
            dataset[DicomTags.Modality].SetStringValue("SC");
            dataset[DicomTags.FrameOfReferenceUid].SetStringValue(baseImageSopProvider.Frame.FrameOfReferenceUid);
            dataset[DicomTags.ImageOrientationPatient].SetStringValue(baseImageSopProvider.Frame.ImageOrientationPatient.ToString());
            dataset[DicomTags.ImagePositionPatient].SetStringValue(baseImageSopProvider.Frame.ImagePositionPatient.ToString());
            dataset[DicomTags.PixelSpacing].SetStringValue(baseImageSopProvider.Frame.PixelSpacing.ToString());
            dataset[DicomTags.PhotometricInterpretation].SetStringValue("RGB");
            dataset[DicomTags.SamplesPerPixel].SetInt32(0, 3);
            dataset[DicomTags.BitsStored].SetInt32(0, 8);
            dataset[DicomTags.BitsAllocated].SetInt32(0, 8);
            dataset[DicomTags.HighBit].SetInt32(0, 7);
            dataset[DicomTags.PixelRepresentation].SetInt32(0, 0);
            dataset[DicomTags.Rows].SetInt32(0, rows);
            dataset[DicomTags.Columns].SetInt32(0, cols);
            dataset[DicomTags.PixelData].Values  = pixelData;
            dicomFile.MediaStorageSopClassUid    = dataset[DicomTags.SopClassUid];
            dicomFile.MediaStorageSopInstanceUid = dataset[DicomTags.SopInstanceUid];

            using (var sopDataSource = new XSopDataSource(dicomFile))
            {
                return(PresentationImageFactory.Create(new ImageSop(sopDataSource))[0]);
            }
        }