/// <summary>
        /// Cloning constructor.
        /// </summary>
        protected DicomGrayscalePresentationImage(DicomGrayscalePresentationImage source, ICloningContext context)
            : base(source, context)
        {
            Frame frame = source.Frame;

            _frameReference = frame.CreateTransientReference();
            _dicomVoiLuts   = new DicomVoiLuts(this);
        }
        protected void DeserializeSoftcopyVoiLut(SoftcopyVoiLutModuleIod module, T image)
        {
            SoftcopyVoiLutModuleIod.SoftcopyVoiLutSequenceItem[] lutSequences = module.SoftcopyVoiLutSequence;
            if (lutSequences == null)
            {
                return;
            }

            DicomVoiLuts voiLuts = (DicomVoiLuts)image.DicomVoiLuts;

            voiLuts.ReinitializePresentationLuts(this.PresentationSopInstanceUid);

            foreach (SoftcopyVoiLutModuleIod.SoftcopyVoiLutSequenceItem lutSequence in lutSequences)
            {
                var dictionary = !DeserializeIgnoreImageRelationship ? new ImageSopInstanceReferenceDictionary(lutSequence.ReferencedImageSequence, true) : null;
                if (dictionary == null || dictionary.ReferencesFrame(image.ImageSop.SopInstanceUid, image.Frame.FrameNumber))
                {
                    if (lutSequence.CountWindows > 0)
                    {
                        double[] widths       = lutSequence.WindowWidth;
                        double[] centers      = lutSequence.WindowCenter;
                        int      countWindows = Math.Min(widths.Length, centers.Length);
                        string[] explanation  = lutSequence.WindowCenterWidthExplanation;
                        if (explanation == null || explanation.Length < countWindows)
                        {
                            explanation = new string[countWindows];
                        }

                        if (lutSequence.VoiLutFunction == VoiLutFunction.Sigmoid)
                        {
                            Platform.Log(LogLevel.Warn, "Sigmoid LUTs are not currently supported.");
                        }
                        else                         // default is linear
                        {
                            for (int n = 0; n < countWindows; n++)
                            {
                                voiLuts.AddPresentationLinearLut(widths[n], centers[n], explanation[n]);
                            }
                        }
                    }

                    if (lutSequence.CountDataLuts > 0)
                    {
                        foreach (VoiLutSequenceItem item in lutSequence.VoiLutSequence)
                        {
                            DataLutIod dl = DataLutIod.Create(item.DicomSequenceItem, item.DicomAttributeProvider[DicomTags.LutDescriptor] is DicomAttributeSS, false);
                            voiLuts.AddPresentationDataLut(new VoiDataLut(dl.FirstMappedPixelValue, dl.BitsPerEntry, dl.Data, dl.Explanation));
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of <see cref="DicomGrayscalePresentationImage"/>.
        /// </summary>
        /// <param name="frameReference">A <see cref="IFrameReference">reference</see> to the frame from which to construct the image.</param>
        public DicomGrayscalePresentationImage(IFrameReference frameReference)
            : base(frameReference.Frame.Rows,
                   frameReference.Frame.Columns,
                   frameReference.Frame.BitsAllocated,
                   frameReference.Frame.BitsStored,
                   frameReference.Frame.HighBit,
                   frameReference.Frame.PixelRepresentation != 0,
                   frameReference.Frame.PhotometricInterpretation == PhotometricInterpretation.Monochrome1,
                   frameReference.Frame.RescaleSlope,
                   frameReference.Frame.RescaleIntercept,
                   frameReference.Frame.NormalizedPixelSpacing.Column,
                   frameReference.Frame.NormalizedPixelSpacing.Row,
                   frameReference.Frame.PixelAspectRatio.Column,
                   frameReference.Frame.PixelAspectRatio.Row,
                   frameReference.Frame.GetNormalizedPixelData)
        {
            _frameReference        = frameReference;
            _dicomVoiLuts          = new DicomVoiLuts(this);
            base.PresentationState = PresentationState.DicomDefault;

            if (ImageSop.Modality == "MG")
            {
                // use a special image spatial transform for digital mammography
                CompositeImageGraphic.SpatialTransform = new MammographyImageSpatialTransform(CompositeImageGraphic, Frame.Rows, Frame.Columns, Frame.NormalizedPixelSpacing.Column, Frame.NormalizedPixelSpacing.Row, Frame.PixelAspectRatio.Column, Frame.PixelAspectRatio.Row, Frame.PatientOrientation, ImageSop.ImageLaterality);
            }

            if (ImageSop.Modality == "PT" && frameReference.Frame.IsSubnormalRescale)
            {
                // some PET images have such a small slope that all stored pixel values map to one single value post-modality LUT
                // we detect this condition here and apply the inverse of the modality LUT as a normalization function for VOI purposes
                // http://groups.google.com/group/comp.protocols.dicom/browse_thread/thread/8930b159cb2a8e73?pli=1
                ImageGraphic.NormalizationLut = new NormalizationLutLinear(frameReference.Frame.RescaleSlope, frameReference.Frame.RescaleIntercept);
            }

            Initialize();
        }