private static GrayscaleImageGraphic CreateOverlayImageGraphic(OverlayPlane overlayPlaneIod, byte[] overlayData)
        {
            Point origin = (overlayPlaneIod.OverlayOrigin ?? new Point(1, 1)) - new Size(1, 1);
            int   rows   = overlayPlaneIod.OverlayRows;
            int   cols   = overlayPlaneIod.OverlayColumns;

            if (overlayData == null || overlayData.Length == 0)
            {
                return(null);
            }

            GrayscaleImageGraphic imageGraphic = new GrayscaleImageGraphic(
                rows, cols,        // the reported overlay dimensions
                8,                 // bits allocated is always 8
                8,                 // overlays always have bit depth of 1, but we upconverted the data
                7,                 // the high bit is now 7 after upconverting
                false, false,      // overlays aren't signed and don't get inverted
                1, 0,              // overlays have no rescale
                overlayData);      // the upconverted overlay data

            imageGraphic.SpatialTransform.TranslationX = origin.X;
            imageGraphic.SpatialTransform.TranslationY = origin.Y;

            return(imageGraphic);
        }
Example #2
0
        public void ChangeSyntax(TransferSyntax syntax)
        {
            try
            {
                if (!_dicomFile.TransferSyntax.Encapsulated)
                {
                    // Check if Overlay is embedded in pixels
                    OverlayPlaneModuleIod overlayIod = new OverlayPlaneModuleIod(_dicomFile.DataSet);
                    for (int i = 0; i < 16; i++)
                    {
                        if (overlayIod.HasOverlayPlane(i))
                        {
                            OverlayPlane overlay = overlayIod[i];
                            if (overlay.OverlayData == null)
                            {
                                DicomUncompressedPixelData pd = new DicomUncompressedPixelData(_dicomFile);
                                overlay.ConvertEmbeddedOverlay(pd);
                            }
                        }
                    }
                }
                else if (syntax.Encapsulated)
                {
                    // Must decompress first.
                    _dicomFile.ChangeTransferSyntax(TransferSyntax.ExplicitVrLittleEndian);
                }

                _dicomFile.ChangeTransferSyntax(syntax);
            }
            catch (Exception e)
            {
                Platform.Log(LogLevel.Error, e, "Unexpected exception compressing/decompressing DICOM file");
            }
        }
        public void SetupMRWithOverlay(DicomAttributeCollection theSet)
        {
            SetupMR(theSet);

            OverlayPlaneModuleIod      overlayIod = new OverlayPlaneModuleIod(theSet);
            DicomUncompressedPixelData pd         = new DicomUncompressedPixelData(theSet);
            OverlayPlane overlay = overlayIod[0];

            // Embedded overlays are retired in dicom, just doing it for testing purposes
            theSet[DicomTags.OverlayBitPosition].SetInt32(0, pd.HighBit + 1);
            overlay.OverlayBitsAllocated = 1;
            overlay.OverlayColumns       = pd.ImageWidth;
            overlay.OverlayRows          = pd.ImageHeight;
            overlay.OverlayOrigin        = new Point(0, 0);
            overlay.OverlayType          = OverlayType.R;
        }
Example #4
0
        public void RleOverlayTest()
        {
            DicomFile file = new DicomFile("RleCodecOverlayTest.dcm");

            SetupMRWithOverlay(file.DataSet);

            SetupMetaInfo(file);

            // Save a copy
            file.Save();


            // Load the file into new DicomFile object
            DicomFile newFile = new DicomFile(file.Filename);

            newFile.Load();

            OverlayPlaneModuleIod overlayIod = new OverlayPlaneModuleIod(newFile.DataSet);
            OverlayPlane          overlay    = overlayIod[0];

            // SHould be no OverlayData tag
            Assert.IsNull(overlay.OverlayData, "Overlay should be in pixel data");

            // Overlay should be extracted out of pixel data here
            newFile.ChangeTransferSyntax(TransferSyntax.RleLossless);

            Assert.IsNotNull(overlay.OverlayData, "Overlay Data is not null");

            newFile.Save();

            // Load a new copy
            newFile = new DicomFile(file.Filename);

            newFile.Load();

            newFile.ChangeTransferSyntax(TransferSyntax.ExplicitVrLittleEndian);

            newFile.Filename = "Output" + file.Filename;
            newFile.Save();

            List <DicomAttributeComparisonResult> results = new List <DicomAttributeComparisonResult>();
            bool compare = file.DataSet.Equals(newFile.DataSet, ref results);

            // Shouldn't be the same, OverlayData tag should have been added
            Assert.IsFalse(compare, results.Count > 0 ? CollectionUtils.FirstElement(results).Details : string.Empty);
        }
Example #5
0
        /// <summary>
        /// Determines if the overlay data for this plane is embedded in the pixel data.
        /// </summary>
        /// <remarks>
        /// We cannot use <see cref="OverlayPlane.IsEmbedded"/> because the PixelData attribute is not in the dataset since we stream it separately.
        /// </remarks>
        private static bool IsOverlayEmbedded(OverlayPlane overlayPlane)
        {
            IDicomAttributeProvider provider = overlayPlane.DicomAttributeProvider;

            // OverlayData exists => not embedded
            DicomAttribute overlayData = provider[overlayPlane.TagOffset + DicomTags.OverlayData];

            if (overlayData.IsEmpty || overlayData.IsNull)
            {
                // embedded => BitsAllocated={8|16}, OverlayBitPosition in [0, BitsAllocated)
                int overlayBitPosition = provider[overlayPlane.TagOffset + DicomTags.OverlayBitPosition].GetInt32(0, -1);
                int bitsAllocated      = provider[DicomTags.BitsAllocated].GetInt32(0, 0);
                if (overlayBitPosition >= 0 && overlayBitPosition < bitsAllocated && (bitsAllocated == 8 || bitsAllocated == 16))
                {
                    // embedded => OverlayBitPosition in (HighBit, BitsAllocated) or [0, HighBit - BitsStored + 1)
                    int highBit    = provider[DicomTags.HighBit].GetInt32(0, 0);
                    int bitsStored = provider[DicomTags.BitsStored].GetInt32(0, 0);
                    return(overlayBitPosition > highBit || overlayBitPosition < highBit - bitsStored + 1);
                }
            }
            return(false);
        }
Example #6
0
            protected override byte[] CreateNormalizedOverlayData(int overlayGroupNumber, int overlayFrameNumber)
            {
                int frameIndex   = overlayFrameNumber - 1;
                int overlayIndex = overlayGroupNumber - 1;

                byte[] overlayData = null;

                OverlayPlaneModuleIod opmi = new OverlayPlaneModuleIod(this.Parent);

                if (opmi.HasOverlayPlane(overlayIndex))
                {
                    OverlayPlane overlayPlane = opmi[overlayIndex];

                    if (_overlayData[overlayIndex] == null)
                    {
                        if (IsOverlayEmbedded(overlayPlane))
                        {
                            this.GetNormalizedPixelData();
                        }
                        else
                        {
                            int bitOffset;
                            overlayPlane.TryComputeOverlayDataBitOffset(frameIndex, out bitOffset);

                            OverlayData od = new OverlayData(bitOffset,
                                                             overlayPlane.OverlayRows,
                                                             overlayPlane.OverlayColumns,
                                                             overlayPlane.IsBigEndianOW,
                                                             overlayPlane.OverlayData);
                            _overlayData[overlayIndex] = od.Unpack();
                        }
                    }
                    overlayData = _overlayData[overlayIndex];
                }

                return(overlayData);
            }
        /// <summary>
        /// Constructs an <see cref="OverlayPlaneGraphic"/> for a single or multi-frame overlay plane using a pre-processed overlay pixel data buffer.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The <paramref name="overlayPixelData"/> parameter allows for the specification of an alternate source of overlay pixel data, such
        /// as the unpacked contents of <see cref="DicomTags.OverlayData"/> or the extracted, inflated overlay pixels of <see cref="DicomTags.PixelData"/>.
        /// Although the format should be 8-bits per pixel, every pixel should either be 0 or 255. This will allow pixel interpolation algorithms
        /// sufficient range to produce a pleasant image. (If the data was either 0 or 1, regardless of the bit-depth, most interpolation algorithms
        /// will interpolate 0s for everything in between!)
        /// </para>
        /// </remarks>
        /// <param name="overlayPlaneIod">The IOD object containing properties of the overlay plane.</param>
        /// <param name="overlayPixelData">The overlay pixel data in 8-bits per pixel format, with each pixel being either 0 or 255.</param>
        /// <param name="frameIndex">The overlay frame index (0-based). Single-frame overlays should specify 0.</param>
        /// <param name="source">A value identifying the source of the overlay plane.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="overlayPixelData"/> is NULL or 0-length.</exception>
        public OverlayPlaneGraphic(OverlayPlane overlayPlaneIod, byte[] overlayPixelData, int frameIndex, OverlayPlaneSource source)
        {
            Platform.CheckNonNegative(frameIndex, "frameIndex");
            _frameIndex  = frameIndex;
            _index       = overlayPlaneIod.Index;
            _label       = overlayPlaneIod.OverlayLabel;
            _description = overlayPlaneIod.OverlayDescription;
            _type        = overlayPlaneIod.OverlayType;
            _subtype     = (OverlayPlaneSubtype)overlayPlaneIod.OverlaySubtype;
            _source      = source;

            GrayscaleImageGraphic overlayImageGraphic = CreateOverlayImageGraphic(overlayPlaneIod, overlayPixelData);

            if (overlayImageGraphic != null)
            {
                _overlayGraphic = overlayImageGraphic;
                this.Color      = System.Drawing.Color.PeachPuff;
                base.Graphics.Add(overlayImageGraphic);
            }

            if (string.IsNullOrEmpty(overlayPlaneIod.OverlayLabel))
            {
                if (overlayPlaneIod.IsMultiFrame)
                {
                    base.Name = string.Format(SR.FormatDefaultMultiFrameOverlayGraphicName, _source, _index, frameIndex);
                }
                else
                {
                    base.Name = string.Format(SR.FormatDefaultSingleFrameOverlayGraphicName, _source, _index, frameIndex);
                }
            }
            else
            {
                base.Name = overlayPlaneIod.OverlayLabel;
            }
        }
		/// <summary>
		/// Constructs an <see cref="OverlayPlaneGraphic"/> for a single or multi-frame overlay plane using a pre-processed overlay pixel data buffer.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The <paramref name="overlayPixelData"/> parameter allows for the specification of an alternate source of overlay pixel data, such
		/// as the unpacked contents of <see cref="DicomTags.OverlayData"/> or the extracted, inflated overlay pixels of <see cref="DicomTags.PixelData"/>.
		/// Although the format should be 8-bits per pixel, every pixel should either be 0 or 255. This will allow pixel interpolation algorithms
		/// sufficient range to produce a pleasant image. (If the data was either 0 or 1, regardless of the bit-depth, most interpolation algorithms
		/// will interpolate 0s for everything in between!)
		/// </para>
		/// </remarks>
		/// <param name="overlayPlaneIod">The IOD object containing properties of the overlay plane.</param>
		/// <param name="overlayPixelData">The overlay pixel data in 8-bits per pixel format, with each pixel being either 0 or 255.</param>
		/// <param name="frameIndex">The overlay frame index (0-based). Single-frame overlays should specify 0.</param>
		/// <param name="source">A value identifying the source of the overlay plane.</param>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="overlayPixelData"/> is NULL or 0-length.</exception>
		public OverlayPlaneGraphic(OverlayPlane overlayPlaneIod, byte[] overlayPixelData, int frameIndex, OverlayPlaneSource source)
		{
			Platform.CheckNonNegative(frameIndex, "frameIndex");
			_frameIndex = frameIndex;
			_index = overlayPlaneIod.Index;
			_label = overlayPlaneIod.OverlayLabel;
			_description = overlayPlaneIod.OverlayDescription;
			_type = overlayPlaneIod.OverlayType;
			_subtype = (OverlayPlaneSubtype) overlayPlaneIod.OverlaySubtype;
			_source = source;

			GrayscaleImageGraphic overlayImageGraphic = CreateOverlayImageGraphic(overlayPlaneIod, overlayPixelData);
			if (overlayImageGraphic != null)
			{
				_overlayGraphic = overlayImageGraphic;
				this.Color = System.Drawing.Color.PeachPuff;
				base.Graphics.Add(overlayImageGraphic);
			}

			if (string.IsNullOrEmpty(overlayPlaneIod.OverlayLabel))
			{
				if (overlayPlaneIod.IsMultiFrame)
					base.Name = string.Format(SR.FormatDefaultMultiFrameOverlayGraphicName, _source, _index, frameIndex);
				else
					base.Name = string.Format(SR.FormatDefaultSingleFrameOverlayGraphicName, _source, _index, frameIndex);
			}
			else
			{
				base.Name = overlayPlaneIod.OverlayLabel;
			}
		}
		/// <summary>
		/// Constructs an <see cref="OverlayPlaneGraphic"/> for a single-frame overlay plane using a pre-processed overlay pixel data buffer.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This overload should only be used for single-frame overlay planes. Multi-frame overlay planes should process the overlay data
		/// into separate buffers and then construct individual graphics using <see cref="OverlayPlaneGraphic(OverlayPlane, byte[], int, OverlayPlaneSource)"/>.
		/// </para>
		/// <para>
		/// The <paramref name="overlayPixelData"/> parameter allows for the specification of an alternate source of overlay pixel data, such
		/// as the unpacked contents of <see cref="DicomTags.OverlayData"/> or the extracted, inflated overlay pixels of <see cref="DicomTags.PixelData"/>.
		/// Although the format should be 8-bits per pixel, every pixel should either be 0 or 255. This will allow pixel interpolation algorithms
		/// sufficient range to produce a pleasant image. (If the data was either 0 or 1, regardless of the bit-depth, most interpolation algorithms
		/// will interpolate 0s for everything in between!)
		/// </para>
		/// </remarks>
		/// <param name="overlayPlaneIod">The IOD object containing properties of the overlay plane.</param>
		/// <param name="overlayPixelData">The overlay pixel data in 8-bits per pixel format, with each pixel being either 0 or 255.</param>
		/// <param name="source">A value identifying the source of the overlay plane.</param>
		/// <exception cref="ArgumentNullException">Thrown if <paramref name="overlayPixelData"/> is NULL or 0-length.</exception>
		public OverlayPlaneGraphic(OverlayPlane overlayPlaneIod, byte[] overlayPixelData, OverlayPlaneSource source) : this(overlayPlaneIod, overlayPixelData, 0, source) {}
Example #10
0
		private static GrayscaleImageGraphic CreateOverlayImageGraphic(OverlayPlane overlayPlaneIod, byte[] overlayData)
		{
			Point origin = (overlayPlaneIod.OverlayOrigin ?? new Point(1, 1)) - new Size(1, 1);
			int rows = overlayPlaneIod.OverlayRows;
			int cols = overlayPlaneIod.OverlayColumns;

			if (overlayData == null || overlayData.Length == 0)
				return null;

			GrayscaleImageGraphic imageGraphic = new GrayscaleImageGraphic(
				rows, cols, // the reported overlay dimensions
				8, // bits allocated is always 8
				8, // overlays always have bit depth of 1, but we upconverted the data
				7, // the high bit is now 7 after upconverting
				false, false, // overlays aren't signed and don't get inverted
				1, 0, // overlays have no rescale
				overlayData); // the upconverted overlay data

			imageGraphic.SpatialTransform.TranslationX = origin.X;
			imageGraphic.SpatialTransform.TranslationY = origin.Y;

			return imageGraphic;
		}
Example #11
0
        public void TestConversion_MultiframeEmbedded()
        {
            const bool bigEndian = false;

            // these parameters should be kept prime numbers so that we can exercise the overlay handling for rows/frames that cross byte boundaries
            const int rows = 97;
            const int columns = 101;
            const int frames = 7;

            const int overlayIndex = 0;

            var overlayData = new bool[rows*columns*frames];
            for (int i = 0; i < frames; i++)
            {
                overlayData[(i * rows*columns) + 1] = true;
                overlayData[(i * rows * columns) + 10] = true;
                overlayData[(i * rows * columns) + 100] = true;
                overlayData[(i * rows * columns) + 225] = true;
                overlayData[(i * rows * columns) + 1000] = true;
                overlayData[(i * rows * columns) + 1001] = true;
                overlayData[(i * rows * columns) + (rows * columns) - 1] = true;
            }

            var dataset = new DicomAttributeCollection();
            SetImage(dataset, new byte[rows * columns * 2 * frames], rows, columns, frames, 16, 12, 11, false);
            SetOverlay(dataset, overlayIndex, overlayData, OverlayType.G, new Point(1, 1), 12, bigEndian);

            DicomUncompressedPixelData pd = new DicomUncompressedPixelData(dataset);

            OverlayPlane overlayPlane = new OverlayPlane(overlayIndex, dataset);
            Assert.IsTrue(overlayPlane.ExtractEmbeddedOverlay(pd), "Failed to convert to Non-Embedded overlay plane.");

            
            int actualOverlayFrame;

            Assert.IsFalse(overlayPlane.TryGetRelevantOverlayFrame(-1, frames, out actualOverlayFrame), "Should not any matching overlay frame for image frame #-1");
            Assert.IsFalse(overlayPlane.TryGetRelevantOverlayFrame(0, frames, out actualOverlayFrame), "Should not any matching overlay frame for image frame #0");
            Assert.IsFalse(overlayPlane.TryGetRelevantOverlayFrame(8, frames, out actualOverlayFrame), "Should not any matching overlay frame for image frame #8");

            // all valid image frame inputs should map 1-to-1 with the same numbered overlay frame

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(1, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #1");
            Assert.AreEqual(1, actualOverlayFrame, "Wrong overlay frame matched to image frame #1");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(2, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #2");
            Assert.AreEqual(2, actualOverlayFrame, "Wrong overlay frame matched to image frame #2");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(3, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #3");
            Assert.AreEqual(3, actualOverlayFrame, "Wrong overlay frame matched to image frame #3");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(4, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #4");
            Assert.AreEqual(4, actualOverlayFrame, "Wrong overlay frame matched to image frame #4");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(5, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #5");
            Assert.AreEqual(5, actualOverlayFrame, "Wrong overlay frame matched to image frame #5");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(6, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #6");
            Assert.AreEqual(6, actualOverlayFrame, "Wrong overlay frame matched to image frame #6");

            Assert.IsTrue(overlayPlane.TryGetRelevantOverlayFrame(7, frames, out actualOverlayFrame), "Should be able to match an overlay frame to image frame #7");
            Assert.AreEqual(7, actualOverlayFrame, "Wrong overlay frame matched to image frame #7");
        }
 /// <summary>
 /// Constructs an <see cref="OverlayPlaneGraphic"/> for a single-frame overlay plane using a pre-processed overlay pixel data buffer.
 /// </summary>
 /// <remarks>
 /// <para>
 /// This overload should only be used for single-frame overlay planes. Multi-frame overlay planes should process the overlay data
 /// into separate buffers and then construct individual graphics using <see cref="OverlayPlaneGraphic(OverlayPlane, byte[], int, OverlayPlaneSource)"/>.
 /// </para>
 /// <para>
 /// The <paramref name="overlayPixelData"/> parameter allows for the specification of an alternate source of overlay pixel data, such
 /// as the unpacked contents of <see cref="DicomTags.OverlayData"/> or the extracted, inflated overlay pixels of <see cref="DicomTags.PixelData"/>.
 /// Although the format should be 8-bits per pixel, every pixel should either be 0 or 255. This will allow pixel interpolation algorithms
 /// sufficient range to produce a pleasant image. (If the data was either 0 or 1, regardless of the bit-depth, most interpolation algorithms
 /// will interpolate 0s for everything in between!)
 /// </para>
 /// </remarks>
 /// <param name="overlayPlaneIod">The IOD object containing properties of the overlay plane.</param>
 /// <param name="overlayPixelData">The overlay pixel data in 8-bits per pixel format, with each pixel being either 0 or 255.</param>
 /// <param name="source">A value identifying the source of the overlay plane.</param>
 /// <exception cref="ArgumentNullException">Thrown if <paramref name="overlayPixelData"/> is NULL or 0-length.</exception>
 public OverlayPlaneGraphic(OverlayPlane overlayPlaneIod, byte[] overlayPixelData, OverlayPlaneSource source) : this(overlayPlaneIod, overlayPixelData, 0, source)
 {
 }
        protected void SerializeOverlayPlane(OverlayPlaneModuleIod overlayPlaneModule, out IOverlayMapping overlayMapping, DicomPresentationImageCollection <T> images)
        {
            // Doesn't support multiframe or whatever case it is when we get more than one image serialized to one state
            List <OverlayPlaneGraphic> visibleOverlays = new List <OverlayPlaneGraphic>();

            foreach (T image in images)
            {
                DicomGraphicsPlane dicomGraphics = DicomGraphicsPlane.GetDicomGraphicsPlane(image, false);
                if (dicomGraphics != null)
                {
                    // identify visible bitmap shutter if exists
                    OverlayPlaneGraphic bitmapShutter = dicomGraphics.Shutters.ActiveShutter as OverlayPlaneGraphic;
                    if (bitmapShutter != null)
                    {
                        visibleOverlays.Add(bitmapShutter);
                    }

                    // identify any visible overlays
                    visibleOverlays.AddRange(((IEnumerable <ILayer>)dicomGraphics.Layers).Where(l => l.Visible).SelectMany(l => l.Graphics).OfType <OverlayPlaneGraphic>().Where(g => g.Visible));
                }
            }

            OverlayMapping overlayMap = new OverlayMapping();
            Queue <OverlayPlaneGraphic> overlaysToRemap = new Queue <OverlayPlaneGraphic>();

            // user and presentation state overlays are high priority items to remap
            foreach (OverlayPlaneGraphic overlay in CollectionUtils.Select(visibleOverlays, delegate(OverlayPlaneGraphic t) { return(t.Source != OverlayPlaneSource.Image); }))
            {
                overlaysToRemap.Enqueue(overlay);
            }
            foreach (OverlayPlaneGraphic overlay in CollectionUtils.Select(visibleOverlays, delegate(OverlayPlaneGraphic t) { return(t.Source == OverlayPlaneSource.Image); }))
            {
                if (overlayMap[overlay.Index] == null)
                {
                    overlayMap[overlay.Index] = overlay;
                }
                else
                {
                    overlaysToRemap.Enqueue(overlay);                     // image overlays are lower priority items to remap, since they will be included in the header anyway
                }
            }

            // seed the overlays to remap into the remaining available overlay groups
            for (int n = 0; n < 16 && overlaysToRemap.Count > 0; n++)
            {
                if (overlayMap[n] == null)
                {
                    overlayMap[n] = overlaysToRemap.Dequeue();
                }
            }

            // serialize the overlays
            for (int n = 0; n < 16; n++)
            {
                OverlayPlaneGraphic overlay = overlayMap[n];
                if (overlay != null)
                {
                    if (overlay.Source != OverlayPlaneSource.Image || overlay.Index != n)
                    {
                        // only record this overlay in the presentation state if it is being remapped to another group or is not already in the image.
                        OverlayPlane overlayIod = overlayPlaneModule[n];
                        overlayIod.OverlayData          = overlay.CreateOverlayData(overlayIod.IsBigEndianOW).Raw;
                        overlayIod.OverlayBitPosition   = 0;
                        overlayIod.OverlayBitsAllocated = 1;
                        overlayIod.OverlayColumns       = overlay.Columns;
                        overlayIod.OverlayDescription   = overlay.Description;
                        overlayIod.OverlayLabel         = overlay.Label;
                        overlayIod.OverlayOrigin        = Point.Round(overlay.Origin);
                        overlayIod.OverlayRows          = overlay.Rows;
                        overlayIod.OverlaySubtype       = overlay.Subtype;
                        overlayIod.OverlayType          = overlay.Type;
                        overlayIod.RoiArea = null;
                        overlayIod.RoiMean = null;
                        overlayIod.RoiStandardDeviation = null;
                    }
                    else
                    {
                        overlayPlaneModule.Delete(n);
                    }
                }
            }

            if (overlaysToRemap.Count > 0)
            {
                Platform.Log(LogLevel.Warn, "Attempt to serialize presentation state with more than 16 visible overlays - some information may be lost.");
            }

            overlayMapping = overlayMap;
        }