public void Refresh() { var clock = new CodeClock(); clock.Start(); var currentProcess = Process.GetCurrentProcess(); currentProcess.Refresh(); ProcessVirtualMemoryBytes = currentProcess.VirtualMemorySize64; ProcessPrivateBytes = currentProcess.PrivateMemorySize64; ProcessWorkingSetBytes = currentProcess.WorkingSet64; GCTotalBytesAllocated = GC.GetTotalMemory(false); SystemFreeMemoryBytes = SystemResources.GetAvailableMemory(SizeUnits.Bytes); HighWaterMarkBytes = MemoryManagementSettings.Default.HighWatermarkMegaBytes*OneMegabyte; LowWaterMarkBytes = MemoryManagementSettings.Default.LowWatermarkMegaBytes*OneMegabyte; HeldMemoryToCollectPercent = MemoryManagementSettings.Default.HeldMemoryToCollectPercent/100.0; x64MinimumFreeSystemMemoryBytes = MemoryManagementSettings.Default.x64MinimumFreeSystemMemoryMegabytes*OneMegabyte; x64MaxMemoryUsagePercent = MemoryManagementSettings.Default.x64MaxMemoryUsagePercent/100.0; x64MaxMemoryToCollectBytes = MemoryManagementSettings.Default.x64MaxMemoryToCollectMegabytes*OneMegabyte; MemoryManagerLargeObjectBytesCount = MemoryManager.LargeObjectBytesCount; clock.Stop(); PerformanceReportBroker.PublishReport("Memory", "UpdateMemoryInfo", clock.Seconds); }
public static PaletteColorMap Create(IDicomAttributeProvider dataSource) { CodeClock clock = new CodeClock(); clock.Start(); PaletteColorLut paletteColorLut = PaletteColorLut.Create(dataSource); clock.Stop(); PerformanceReportBroker.PublishReport("PaletteColorMap", "Create(IDicomAttributeProvider)", clock.Seconds); return new PaletteColorMap(paletteColorLut); }
public static void Render( ImageGraphic imageGraphic, IntPtr pDstPixelData, int dstWidth, int dstBytesPerPixel, Rectangle clientRectangle) { if (clientRectangle.Width <= 0 || clientRectangle.Height <= 0) return; if (imageGraphic.SizeInBytes != imageGraphic.PixelData.Raw.Length) throw new InvalidOperationException(String.Format(SR.ExceptionIncorrectPixelDataSize, imageGraphic.SizeInBytes, imageGraphic.PixelData.Raw.Length)); #if DEBUG CodeClock clock = new CodeClock(); clock.Start(); #endif RectangleF srcViewableRectangle; Rectangle dstViewableRectangle; CalculateVisibleRectangles(imageGraphic, clientRectangle, out dstViewableRectangle, out srcViewableRectangle); var grayGraphic = imageGraphic as GrayscaleImageGraphic; ColorImageGraphic colorGraphic; if (grayGraphic != null) { RenderGrayscale( grayGraphic, srcViewableRectangle, dstViewableRectangle, pDstPixelData, dstWidth, dstBytesPerPixel); } else if (null != (colorGraphic = imageGraphic as ColorImageGraphic)) { RenderColor( colorGraphic, srcViewableRectangle, dstViewableRectangle, pDstPixelData, dstWidth, dstBytesPerPixel); } else { throw new Exception("Unknown ImageGraphic."); } #if DEBUG clock.Stop(); PerformanceReportBroker.PublishReport("ImageRenderer", "Render", clock.Seconds); #endif }
/// <summary> /// Draws an <see cref="ImageGraphic"/>. /// </summary> protected override void DrawImageGraphic(ImageGraphic imageGraphic) { CodeClock clock = new CodeClock(); clock.Start(); const int bytesPerPixel = 4; Surface.ImageBuffer.Graphics.Clear(Color.FromArgb(0x0, 0xFF, 0xFF, 0xFF)); BitmapData bitmapData = Surface.ImageBuffer.Bitmap.LockBits( new Rectangle(0, 0, Surface.ImageBuffer.Bitmap.Width, Surface.ImageBuffer.Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); try { ImageRenderer.Render(imageGraphic, bitmapData.Scan0, bitmapData.Width, bytesPerPixel, Surface.ClientRectangle); } finally { Surface.ImageBuffer.Bitmap.UnlockBits(bitmapData); } Surface.FinalBuffer.RenderImage(Surface.ImageBuffer); clock.Stop(); PerformanceReportBroker.PublishReport("GDIRenderer", "DrawImageGraphic", clock.Seconds); }
/// <summary> /// Traverses and renders the scene graph. /// </summary> protected override void Render() { CodeClock clock = new CodeClock(); clock.Start(); Surface.FinalBuffer.Graphics.Clear(Color.Black); base.Render(); clock.Stop(); PerformanceReportBroker.PublishReport("GDIRenderer", "Render", clock.Seconds); }
/// <summary> /// Converts colour pixel data to ARGB. /// </summary> protected static byte[] ToArgb(IDicomAttributeProvider dicomAttributeProvider, byte[] pixelData, PhotometricInterpretation photometricInterpretation) { #if DEBUG CodeClock clock = new CodeClock(); clock.Start(); #endif int rows = dicomAttributeProvider[DicomTags.Rows].GetInt32(0, 0); int columns = dicomAttributeProvider[DicomTags.Columns].GetInt32(0, 0); int sizeInBytes = rows*columns*4; byte[] argbPixelData = MemoryManager.Allocate<byte>(sizeInBytes); // Convert palette colour images to ARGB so we don't get interpolation artifacts // when rendering. if (photometricInterpretation == PhotometricInterpretation.PaletteColor) { int bitsAllocated = dicomAttributeProvider[DicomTags.BitsAllocated].GetInt32(0, 0); int pixelRepresentation = dicomAttributeProvider[DicomTags.PixelRepresentation].GetInt32(0, 0); ColorSpaceConverter.ToArgb( bitsAllocated, pixelRepresentation != 0 ? true : false, pixelData, argbPixelData, PaletteColorMap.Create(dicomAttributeProvider)); } // Convert RGB and YBR variants to ARGB else { int planarConfiguration = dicomAttributeProvider[DicomTags.PlanarConfiguration].GetInt32(0, 0); ColorSpaceConverter.ToArgb( photometricInterpretation, planarConfiguration, pixelData, argbPixelData); } #if DEBUG clock.Stop(); PerformanceReportBroker.PublishReport("DicomMessageSopDataSource", "ToArgb", clock.Seconds); #endif return argbPixelData; }
/// <summary> /// Called by the base class to create a new byte buffer containing normalized pixel data /// for this frame (8 or 16-bit grayscale, or 32-bit ARGB). /// </summary> /// <returns>A new byte buffer containing the normalized pixel data.</returns> protected override byte[] CreateNormalizedPixelData() { DicomMessageBase message = Parent.SourceMessage; #if DEBUG CodeClock clock = new CodeClock(); clock.Start(); #endif PhotometricInterpretation photometricInterpretation; byte[] rawPixelData = null; if (!message.TransferSyntax.Encapsulated) { DicomUncompressedPixelData pixelData = new DicomUncompressedPixelData(message); // DICOM library uses zero-based frame numbers MemoryManager.Execute(delegate { rawPixelData = pixelData.GetFrame(_frameIndex); }); ExtractOverlayFrames(rawPixelData, pixelData.BitsAllocated); photometricInterpretation = PhotometricInterpretation.FromCodeString(message.DataSet[DicomTags.PhotometricInterpretation]); } else if (DicomCodecRegistry.GetCodec(message.TransferSyntax) != null) { DicomCompressedPixelData pixelData = new DicomCompressedPixelData(message); string pi = null; MemoryManager.Execute(delegate { rawPixelData = pixelData.GetFrame(_frameIndex, out pi); }); photometricInterpretation = PhotometricInterpretation.FromCodeString(pi); } else throw new DicomCodecException("Unsupported transfer syntax"); if (photometricInterpretation.IsColor) rawPixelData = ToArgb(message.DataSet, rawPixelData, photometricInterpretation); else NormalizeGrayscalePixels(message.DataSet, rawPixelData); #if DEBUG clock.Stop(); PerformanceReportBroker.PublishReport("DicomMessageSopDataSource", "CreateFrameNormalizedPixelData", clock.Seconds); #endif return rawPixelData; }
private RetrievePixelDataResult TryClientRetrievePixelData(out Exception lastRetrieveException) { // retry parameters const int maxRetryCount = 10; const int retryTimeout = 1500; int retryDelay = 50; int retryCounter = 0; StreamingClient client = new StreamingClient(this.BaseUrl); RetrievePixelDataResult result = null; lastRetrieveException = null; CodeClock timeoutClock = new CodeClock(); timeoutClock.Start(); while (true) { try { if (retryCounter > 0) Platform.Log(LogLevel.Info, "Retrying retrieve pixel data for Sop '{0}' (Attempt #{1})", this.SopInstanceUid, retryCounter); CodeClock statsClock = new CodeClock(); statsClock.Start(); result = client.RetrievePixelData(this.AETitle, this.StudyInstanceUid, this.SeriesInstanceUid, this.SopInstanceUid, this.FrameNumber - 1); statsClock.Stop(); Platform.Log(LogLevel.Debug, "[Retrieve Info] Sop/Frame: {0}/{1}, Transfer Syntax: {2}, Bytes transferred: {3}, Elapsed (s): {4}, Retries: {5}", this.SopInstanceUid, this.FrameNumber, this.TransferSyntaxUid, result.MetaData.ContentLength, statsClock.Seconds, retryCounter); break; } catch (Exception ex) { lastRetrieveException = ex; timeoutClock.Stop(); if (timeoutClock.Seconds * 1000 >= retryTimeout || retryCounter>=maxRetryCount) { // log an alert that we are aborting (exception trace at debug level only) int elapsed = (int)(1000*timeoutClock.Seconds); Platform.Log(LogLevel.Warn, "Failed to retrieve pixel data for Sop '{0}'; Aborting after {1} attempts in {2} ms", this.SopInstanceUid, retryCounter, elapsed); Platform.Log(LogLevel.Debug, ex, "[Retrieve Fail-Abort] Sop/Frame: {0}/{1}, Retry Attempts: {2}, Elapsed: {3} ms", this.SopInstanceUid, this.FrameNumber - 1, retryCounter, elapsed); break; } timeoutClock.Start(); retryCounter++; // log the retry (exception trace at debug level only) Platform.Log(LogLevel.Warn, "Failed to retrieve pixel data for Sop '{0}'; Retrying in {1} ms", this.SopInstanceUid, retryDelay); Platform.Log(LogLevel.Debug, ex, "[Retrieve Fail-Retry] Sop/Frame: {0}/{1}, Retry in: {2} ms", this.SopInstanceUid, this.FrameNumber - 1, retryDelay); MemoryManager.Collect(retryDelay); retryDelay *= 2; if (retryDelay > MaxRetryDelay) retryDelay = MaxRetryDelay; // cap it to avoid overflow, which will cause exception when calling MemoryManager.Collect() } } return result; }
private static float ExecutePerformanceTest(PerformanceTestCallback callback) { var values = new[] {-100300.43245325f, -1, -float.Epsilon, 0, float.Epsilon, 1, 90952.343542f}; var mod = values.Length; var permutations = (int) Math.Pow(mod, 8); var cc = new CodeClock(); cc.Start(); for (int n = 0; n < permutations; n++) { callback.Invoke( new PointF(values[(n)%mod], values[(n/mod/mod/mod/mod/mod/mod/mod)%mod]), new PointF(values[(n/mod/mod)%mod], values[(n/mod/mod/mod/mod/mod)%mod]), new PointF(values[(n/mod/mod/mod/mod)%mod], values[(n/mod/mod/mod)%mod]), new PointF(values[(n/mod/mod/mod/mod/mod/mod)%mod], values[(n/mod)%mod]) ); } cc.Stop(); return cc.Seconds*1000000/permutations; }
public RetrievePixelDataResult RetrievePixelData(string serverAE, string studyInstanceUID, string seriesInstanceUID, string sopInstanceUid, int frame) { try { CodeClock clock = new CodeClock(); clock.Start(); FrameStreamingResultMetaData result = new FrameStreamingResultMetaData(); StringBuilder url = new StringBuilder(); if (_baseUri.ToString().EndsWith("/")) { url.AppendFormat("{0}{1}", _baseUri, serverAE); } else { url.AppendFormat("{0}/{1}", _baseUri, serverAE); } url.AppendFormat("?requesttype=WADO&studyUID={0}&seriesUID={1}&objectUID={2}", studyInstanceUID, seriesInstanceUID, sopInstanceUid); url.AppendFormat("&frameNumber={0}", frame); url.AppendFormat("&contentType={0}", HttpUtility.HtmlEncode("application/clearcanvas")); result.Speed.Start(); HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url.ToString()); request.Accept = "application/dicom,application/clearcanvas,image/jpeg"; request.Timeout = (int) TimeSpan.FromSeconds(StreamingSettings.Default.ClientTimeoutSeconds).TotalMilliseconds; request.KeepAlive = false; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode != HttpStatusCode.OK) { throw new StreamingClientException(response.StatusCode, HttpUtility.HtmlDecode(response.StatusDescription)); } Stream responseStream = response.GetResponseStream(); BinaryReader reader = new BinaryReader(responseStream); byte[] buffer = reader.ReadBytes((int) response.ContentLength); reader.Close(); responseStream.Close(); response.Close(); result.Speed.SetData(buffer.Length); result.Speed.End(); result.ResponseMimeType = response.ContentType; result.Status = response.StatusCode; result.StatusDescription = response.StatusDescription; result.Uri = response.ResponseUri; result.ContentLength = buffer.Length; result.IsLast = (response.Headers["IsLast"] != null && bool.Parse(response.Headers["IsLast"])); clock.Stop(); PerformanceReportBroker.PublishReport("Streaming", "RetrievePixelData", clock.Seconds); RetrievePixelDataResult pixelDataResult; if (response.Headers["Compressed"] != null && bool.Parse(response.Headers["Compressed"])) pixelDataResult = new RetrievePixelDataResult(CreateCompressedPixelData(response, buffer), result); else pixelDataResult = new RetrievePixelDataResult(buffer, result); return pixelDataResult; } catch (WebException ex) { if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response is HttpWebResponse) { HttpWebResponse response = (HttpWebResponse) ex.Response; throw new StreamingClientException(response.StatusCode, HttpUtility.HtmlDecode(response.StatusDescription)); } throw new StreamingClientException(StreamingClientExceptionType.Network, ex); } }
/// <summary> /// Calculates the Minimum and Maximum pixel values from the pixel data efficiently, using unsafe code. /// </summary> /// <param name="minPixelValue">Returns the minimum pixel value.</param> /// <param name="maxPixelValue">Returns the maximum pixel value.</param> unsafe public void CalculateMinMaxPixelValue(out int minPixelValue, out int maxPixelValue) { byte[] pixelData = GetPixelData(); #if DEBUG CodeClock clock = new CodeClock(); clock.Start(); #endif if (_isSigned) { if (_bitsAllocated == 8) { fixed (byte* ptr = pixelData) { byte* pixel = (byte*)ptr; byte signMask = (byte)(1 << (_bitsStored - 1)); sbyte max, min; max = sbyte.MinValue; min = sbyte.MaxValue; for (int i = 0; i < _rows * _columns; ++i) { sbyte result; if (0 == ((*pixel) & signMask)) { result = (sbyte)(*pixel); } else { byte inverted = (byte)(~(*pixel)); // Need to mask out the bits greater above the high bit, since they're irrelevant byte mask = (byte)(byte.MaxValue >> (_bitsAllocated - _bitsStored)); byte maskedInverted = (byte)(inverted & mask); result = (sbyte)(-(maskedInverted + 1)); } if (result > max) max = result; else if (result < min) min = result; ++pixel; } maxPixelValue = (int)max; minPixelValue = (int)min; } } else { fixed (byte* ptr = pixelData) { UInt16* pixel = (UInt16*)ptr; UInt16 signMask = (UInt16)(1 << (_bitsStored - 1)); Int16 max, min; max = Int16.MinValue; min = Int16.MaxValue; for (int i = 0; i < _rows * _columns; ++i) { Int16 result; if (0 == ((*pixel) & signMask)) { result = (Int16)(*pixel); } else { UInt16 inverted = (UInt16)(~(*pixel)); // Need to mask out the bits greater above the high bit, since they're irrelevant UInt16 mask = (UInt16)(UInt16.MaxValue >> (_bitsAllocated - _bitsStored)); UInt16 maskedInverted = (UInt16)(inverted & mask); result = (Int16)(-(maskedInverted + 1)); } if (result > max) max = result; else if (result < min) min = result; ++pixel; } maxPixelValue = (int)max; minPixelValue = (int)min; } } } else { if (_bitsAllocated == 8) { fixed (byte* ptr = pixelData) { byte* pixel = ptr; byte max, min; max = min = *pixel; for (int i = 1; i < _rows * _columns; ++i) { if (*pixel > max) max = *pixel; else if (*pixel < min) min = *pixel; ++pixel; } maxPixelValue = (int)max; minPixelValue = (int)min; } } else { fixed (byte* ptr = pixelData) { UInt16* pixel = (UInt16*)ptr; UInt16 max, min; max = min = *pixel; for (int i = 1; i < _rows * _columns; ++i) { if (*pixel > max) max = *pixel; else if (*pixel < min) min = *pixel; ++pixel; } maxPixelValue = (int)max; minPixelValue = (int)min; } } } #if DEBUG clock.Stop(); Trace.WriteLine(String.Format("Min/Max pixel value calculation took {0:F3} seconds (rows = {1}, columns = {2})", clock.Seconds, _rows, _columns)); #endif }
private ImageViewerComponent LoadAndOpenStudies() { var codeClock = new CodeClock(); codeClock.Start(); var viewer = CreateViewer(LoadPriors); var desktopWindow = DesktopWindow ?? Application.ActiveDesktopWindow; try { viewer.LoadStudies(_studiesToOpen); } catch(InUseLoadStudyException) { if (!HandleStudyInUseError(viewer)) { viewer.Dispose(); return null; } } catch(LoadMultipleStudiesException ex) { // Note: although there may be other errors, we only need to handle exceptions caused by study being processed. // Other errors will (probably) happen again when all studies are reloaded and will be handled in HandleStudyInUseError. if (ex.InUseCount > 0) { if (!HandleStudyInUseError(viewer)) { viewer.Dispose(); return null; } } else { ExceptionHandler.Report(ex, SR.MessageFailedToOpenStudy, desktopWindow); } } catch (Exception e) { ExceptionHandler.Report(e, SR.MessageFailedToOpenStudy, desktopWindow); } if (!AnySopsLoaded(viewer) && !AllowEmptyViewer) { viewer.Dispose(); return null; } var args = new LaunchImageViewerArgs(WindowBehaviour) {Title = Title}; ImageViewerComponent.Launch(viewer, args); codeClock.Stop(); // note: the time will be skewed if the workstation prompts users for action Platform.Log(LogLevel.Debug, string.Format("TTFI: {0}", codeClock)); return viewer; }
public override void Collect(MemoryCollectionArgs collectionArgs) { _largeObjectEnumerator = collectionArgs.LargeObjectContainers.GetEnumerator(); _regenerationCost = RegenerationCost.Low; //TODO (Time Review): Use Environment.TickCount? _collectionStartTime = DateTime.Now; _timeSinceLastCollection = _collectionStartTime - _lastCollectionTime; TimeSpan thirtySeconds = TimeSpan.FromSeconds(30); if (_timeSinceLastCollection < thirtySeconds) { Platform.Log(LogLevel.Debug, "Time since last collection is less than 30 seconds; adjusting to 30 seconds."); _timeSinceLastCollection = thirtySeconds; } _maxTimeSinceLastAccess = _timeSinceLastCollection; _maxTimeSinceLastAccessDecrement = TimeSpan.FromSeconds(_timeSinceLastCollection.TotalSeconds / 3); _totalNumberOfCollections = 0; _totalBytesCollected = 0; _totalLargeObjectsCollected = 0; _totalContainersUnloaded = 0; try { CodeClock clock = new CodeClock(); clock.Start(); Collect(); clock.Stop(); PerformanceReportBroker.PublishReport("Memory", "Collect", clock.Seconds); } catch (Exception e) { Platform.Log(LogLevel.Warn, e, "Default memory management strategy failed to collect."); } finally { DateTime collectionEndTime = DateTime.Now; if (_totalContainersUnloaded > 0) _lastCollectionTime = collectionEndTime; _largeObjectEnumerator = null; TimeSpan totalElapsed = collectionEndTime - _collectionStartTime; MemoryCollectedEventArgs finalArgs = new MemoryCollectedEventArgs( _totalContainersUnloaded, _totalLargeObjectsCollected, _totalBytesCollected, totalElapsed, true); if ( _totalNumberOfCollections != 0 || _totalBytesCollected != 0 || _totalLargeObjectsCollected != 0 ||_totalContainersUnloaded != 0) Platform.Log(LogLevel.Info, "Large object collection summary: freed {0} MB in {1} seconds and {2} iterations, Total Containers: {3}, Total Large Objects: {4}", _totalBytesCollected/(float)OneMegabyte, totalElapsed.TotalSeconds, _totalNumberOfCollections, _totalContainersUnloaded, _totalLargeObjectsCollected); OnMemoryCollected(finalArgs); } }
private void CollectGarbage() { CodeClock clock = new CodeClock(); clock.Start(); Platform.Log(LogLevel.Debug, "Performing garbage collection."); GC.Collect(); clock.Stop(); PerformanceReportBroker.PublishReport("Memory", "GarbageCollection", clock.Seconds); }
private void Collect(long bytesToCollect) { bool continueCollecting = false; bool needMoreMemorySignalled = false; if (bytesToCollect <= 0) { Platform.Log(LogLevel.Debug, "Memory is not above high watermark; firing collected event to check if more memory is required."); MemoryCollectedEventArgs args = new MemoryCollectedEventArgs(0, 0, 0, TimeSpan.Zero, false); OnMemoryCollected(args); continueCollecting = needMoreMemorySignalled = args.NeedMoreMemory; } else { continueCollecting = true; Platform.Log(LogLevel.Debug, "Memory *is* above high watermark; collecting ..."); } if (!continueCollecting) return; int batchSize = 10; int collectionNumber = 0; while (continueCollecting) { CodeClock clock = new CodeClock(); clock.Start(); long bytesCollected = 0; int largeObjectsCollected = 0; int containersUnloaded = 0; int i = 0; foreach (ILargeObjectContainer container in GetNextBatchOfContainersToCollect(batchSize)) { ++i; try { long bytesHeldBefore = container.BytesHeldCount; int largeObjectsHeldBefore = container.LargeObjectCount; container.Unload(); long bytesHeldAfter = container.BytesHeldCount; int largeObjectsHeldAfter = container.LargeObjectCount; int largeObjectsHeldDifference = largeObjectsHeldBefore - largeObjectsHeldAfter; largeObjectsCollected += largeObjectsHeldDifference; if (largeObjectsHeldDifference > 0) ++containersUnloaded; long bytesDifference = (bytesHeldBefore - bytesHeldAfter); bytesCollected += bytesDifference; _totalBytesCollected += bytesDifference; } catch (Exception e) { Platform.Log(LogLevel.Warn, e, "An unexpected error occurred while attempting to collect large object memory."); } //when needMoreMemorySignalled is true, we need to be more aggressive and keep collecting. if (!needMoreMemorySignalled && _totalBytesCollected >= bytesToCollect) break; } batchSize *= 2; _totalContainersUnloaded += containersUnloaded; _totalLargeObjectsCollected += largeObjectsCollected; ++_totalNumberOfCollections; clock.Stop(); continueCollecting = i > 0; if (!continueCollecting) continue; PerformanceReportBroker.PublishReport("Memory", "CollectionIteration", clock.Seconds); MemoryCollectedEventArgs args = new MemoryCollectedEventArgs( containersUnloaded, largeObjectsCollected, bytesCollected, TimeSpan.FromSeconds(clock.Seconds), false); OnMemoryCollected(args); needMoreMemorySignalled = args.NeedMoreMemory; continueCollecting = needMoreMemorySignalled || _totalBytesCollected < bytesToCollect; if (Platform.IsLogLevelEnabled(LogLevel.Debug)) { Platform.Log(LogLevel.Debug, "Large object collection #{0}: freed {1} MB in {2}, Containers Unloaded: {3}, Large Objects Collected: {4}, Need More Memory: {5}, Last Batch: {6}", ++collectionNumber, args.BytesCollectedCount / (float)OneMegabyte, clock, containersUnloaded, largeObjectsCollected, needMoreMemorySignalled, i); } } CollectGarbage(); }
private static int[] ConstructFinalLut(IComposedLut outputLut, bool invert) { CodeClock clock = new CodeClock(); clock.Start(); int[] outputLutData = outputLut.Data; if (_finalLutBuffer == null || _finalLutBuffer.Length != outputLutData.Length) _finalLutBuffer = new int[outputLutData.Length]; int numberOfEntries = _finalLutBuffer.Length; fixed (int* pOutputLutData = outputLutData) { fixed (int* pFinalLutData = _finalLutBuffer) { int* pFinalLut = pFinalLutData; if (!invert) { int firstColorMappedPixelValue = outputLut.MinOutputValue; for (int i = 0; i < numberOfEntries; ++i) *(pFinalLut++) = *(pOutputLutData + i) - firstColorMappedPixelValue; } else { int lastColorMappedPixelValue = outputLut.MaxOutputValue; for (int i = 0; i < numberOfEntries; ++i) *(pFinalLut++) = lastColorMappedPixelValue - *(pOutputLutData + i); } } } clock.Stop(); PerformanceReportBroker.PublishReport("ImageRenderer", "ConstructFinalLut", clock.Seconds); return _finalLutBuffer; }
private ImageViewerComponent LoadAndOpenFiles() { var codeClock = new CodeClock(); codeClock.Start(); var viewer = CreateViewer(false); // don't find priors for files loaded off the local disk. var desktopWindow = DesktopWindow ?? Application.ActiveDesktopWindow; try { UserCancelled = false; bool cancelled; viewer.LoadImages(_filenames.ToArray(), desktopWindow, out cancelled); UserCancelled = cancelled; } catch (Exception e) { if (!HandleErrors) throw; ExceptionHandler.Report(e, SR.MessageFailedToOpenImages, desktopWindow); } if (UserCancelled || (!AnySopsLoaded(viewer) && !AllowEmptyViewer)) { viewer.Dispose(); return null; } var args = new LaunchImageViewerArgs(WindowBehaviour) {Title = Title}; ImageViewerComponent.Launch(viewer, args); codeClock.Stop(); Platform.Log(LogLevel.Debug, string.Format("TTFI: {0}", codeClock)); return viewer; }
private void UpdateImageScroller() { //This method can be called repeatedly and will essentially be a no-op if nothing needs to change. //In tiled mode, it could be a little inefficient to call repeatedly, but it's the lesser of the evils. //Otherwise, we're subscribing to a multitude of events and updating different things at different times. //Not to mention, that doesn't cover every case, like sorting images. It's nothing compared to //the cost of updating the scroll control itself, anyway. CodeClock clock = new CodeClock(); clock.Start(); bool visibleBefore = ImageScrollerVisible; bool visibleNow = false; IDisplaySet displaySet = _imageBox.DisplaySet; if (displaySet != null) { int tileCount = _imageBox.Tiles.Count; int maximum = Math.Max(0, displaySet.PresentationImages.Count - tileCount); if (maximum > 0) { visibleNow = true; int topLeftIndex = Math.Max(0, _imageBox.TopLeftPresentationImageIndex); _imageScroller.SetValueAndRange(topLeftIndex, 0, maximum); _imageScroller.Increment = Math.Max(1, tileCount); _imageScroller.Value = topLeftIndex; } } if (visibleBefore != visibleNow) { ImageScrollerVisible = visibleNow; //UpdateImageScroller is only called right before a Tile is drawn, so we suppress //the Tile drawing as a result of a size change here. SetTileParentImageBoxRectangles(true); } clock.Stop(); //Trace.WriteLine(String.Format("UpdateScroller: {0}", clock)); }
private ImageViewerComponent LoadAndOpenStudies() { var codeClock = new CodeClock(); codeClock.Start(); var viewer = CreateViewer(LoadPriors); var desktopWindow = DesktopWindow ?? Application.ActiveDesktopWindow; try { viewer.LoadStudies(_studiesToOpen); } catch (Exception e) { ExceptionHandler.Report(e, SR.MessageFailedToOpenStudy, desktopWindow); } if (!AnySopsLoaded(viewer) && !AllowEmptyViewer) { viewer.Dispose(); return null; } var args = new LaunchImageViewerArgs(WindowBehaviour) {Title = Title}; ImageViewerComponent.Launch(viewer, args); codeClock.Stop(); Platform.Log(LogLevel.Debug, string.Format("TTFI: {0}", codeClock)); return viewer; }
private DicomFile TryClientRetrieveImageHeader(out Exception lastRetrieveException) { // retry parameters const int retryTimeout = 1500; int retryDelay = 50; int retryCounter = 0; CodeClock timeoutClock = new CodeClock(); timeoutClock.Start(); lastRetrieveException = null; while (true) { try { if (retryCounter > 0) Platform.Log(LogLevel.Info, "Retrying retrieve headers for Sop '{0}' (Attempt #{1})", this.SopInstanceUid, retryCounter); return _loader.LoadDicomFile(new LoadDicomFileArgs(this.StudyInstanceUid, this.SeriesInstanceUid, this.SopInstanceUid, true, false)); } catch (Exception ex) { lastRetrieveException = ex; timeoutClock.Stop(); if (timeoutClock.Seconds*1000 >= retryTimeout) { // log an alert that we are aborting (exception trace at debug level only) int elapsed = (int) (1000*timeoutClock.Seconds); Platform.Log(LogLevel.Warn, "Failed to retrieve headers for Sop '{0}'; Aborting after {1} attempts in {2} ms", this.SopInstanceUid, retryCounter, elapsed); Platform.Log(LogLevel.Debug, ex, "[GetHeaders Fail-Abort] Sop: {0}, Retry Attempts: {1}, Elapsed: {2} ms", this.SopInstanceUid, retryCounter, elapsed); break; } timeoutClock.Start(); retryCounter++; // log the retry (exception trace at debug level only) Platform.Log(LogLevel.Warn, "Failed to retrieve headers for Sop '{0}'; Retrying in {1} ms", this.SopInstanceUid, retryDelay); Platform.Log(LogLevel.Debug, ex, "[GetHeaders Fail-Retry] Sop: {0}, Retry in: {1} ms", this.SopInstanceUid, retryDelay); MemoryManager.Collect(retryDelay); retryDelay *= 2; } } return null; }
public byte[] GetUncompressedPixelData() { try { //construct this object before the lock so there's no chance of deadlocking //with the parent data source (because we are accessing its tags at the //same time as it's trying to get the pixel data). FramePixelDataRetriever retriever = new FramePixelDataRetriever(this); lock (_syncLock) { RetrievePixelDataResult result; if (_retrieveResult == null) { AbortAttemptIfNecessary(); ResetAttemptData(); _retrievesAttempted++; result = retriever.Retrieve(); } else result = _retrieveResult; //free this memory up in case it's holding a compressed buffer. _retrieveResult = null; CodeClock clock = new CodeClock(); clock.Start(); //synchronize the call to decompress; it's really already synchronized by //the parent b/c it's only called from CreateFrameNormalizedPixelData, but it doesn't hurt. byte[] pixelData = result.GetPixelData(); clock.Stop(); Platform.Log(LogLevel.Debug, "[Decompress Info] Sop/Frame: {0}/{1}, Transfer Syntax: {2}, Uncompressed bytes: {3}, Elapsed (s): {4}", retriever.SopInstanceUid, FrameNumber, retriever.TransferSyntaxUid, pixelData.Length, clock.Seconds); return pixelData; } } catch(Exception ex) { _lastError = ex; throw; } }
/// <summary> /// Figure out which studies have been deleted and/or updated. /// </summary> private void ProcessChangedStudiesAsync(out DateTime? queryStartTime, out List<string> deletedStudyUids, out List<StudyEntry> updatedStudies) { deletedStudyUids = new List<string>(); updatedStudies = new List<StudyEntry>(); queryStartTime = null; DateTime now = DateTime.Now; var fiveSeconds = TimeSpan.FromSeconds(5); var rapidChangeInterval = TimeSpan.FromMilliseconds(300); lock (_syncLock) { if (_queryingForUpdates) return; //Already querying. //Nothing to query for? Return. if (_setChangedStudies.Count == 0 || !_lastStudyChangeTime.HasValue) return; bool studiesChanging = now - _lastStudyChangeTime.Value < rapidChangeInterval; if (studiesChanging) { //Many DIFFERENT studies are changing in very rapid succession. Delay until it settles down, which usually isn't long. Platform.Log(LogLevel.Debug, "Studies are still actively changing - delaying update."); return; } if (!_hastenUpdateQuery) { bool updatedRecently = _lastUpdateQueryEndTime.HasValue && now - _lastUpdateQueryEndTime < fiveSeconds; if (updatedRecently) { //We just finished an update query less than 5 seconds ago. Platform.Log(LogLevel.Debug, "Studies were updated within the last 5 seconds - delaying update."); return; } } //Reset this before the immediate query. _hastenUpdateQuery = false; //Add everything to the deleted list. deletedStudyUids.AddRange(_setChangedStudies.Keys); _setChangedStudies.Clear(); //We are officially querying for updates. _queryingForUpdates = true; } queryStartTime = now; string studyUids = DicomStringHelper.GetDicomStringArray(deletedStudyUids); try { var clock = new CodeClock(); clock.Start(); var criteria = new StudyRootStudyIdentifier { StudyInstanceUid = studyUids }; var request = new GetStudyEntriesRequest { Criteria = new StudyEntry { Study = criteria } }; IList<StudyEntry> entries = null; //We're doing it this way here because it's local only. Platform.GetService<IStudyStoreQuery>(s => entries = s.GetStudyEntries(request).StudyEntries); foreach (var entry in entries) { //If we got a result back, then it's not deleted. deletedStudyUids.Remove(entry.Study.StudyInstanceUid); updatedStudies.Add(entry); } clock.Stop(); Platform.Log(LogLevel.Debug, "Study update query took {0}.", clock); } catch (Exception e) { Platform.Log(LogLevel.Error, e); } finally { lock (_syncLock) { //Finished querying for updates. _queryingForUpdates = false; _lastUpdateQueryEndTime = now; } } }
/// <summary> /// Called by <see cref="StandardSopFrameData.GetNormalizedOverlayData"/> to create a new byte buffer containing normalized /// overlay pixel data for a particular overlay plane. /// </summary> /// <remarks> /// See <see cref="StandardSopFrameData.GetNormalizedOverlayData"/> for details on the expected format of the byte buffer. /// </remarks> /// <param name="overlayNumber">The 1-based overlay plane number.</param> /// <returns>A new byte buffer containing the normalized overlay pixel data.</returns> protected override byte[] CreateNormalizedOverlayData(int overlayNumber) { //TODO (CR December 2010): make this a helper method somewhere, since it's now identical to the one in StreamingSopFrameData? var overlayIndex = overlayNumber - 1; byte[] overlayData = null; var clock = new CodeClock(); clock.Start(); // check whether or not the overlay plane exists before attempting to ascertain // whether or not the overlay is embedded in the pixel data var overlayPlaneModuleIod = new OverlayPlaneModuleIod(Parent); if (overlayPlaneModuleIod.HasOverlayPlane(overlayIndex)) { if (_overlayCache[overlayIndex] == null) { var overlayPlane = overlayPlaneModuleIod[overlayIndex]; if (!overlayPlane.HasOverlayData) { // if the overlay is embedded, trigger retrieval of pixel data which will populate the cache for us GetNormalizedPixelData(); } else { // try to compute the offset in the OverlayData bit stream where we can find the overlay frame that applies to this image frame int overlayFrame; int bitOffset; if (overlayPlane.TryGetRelevantOverlayFrame(FrameNumber, Parent.NumberOfFrames, out overlayFrame) && overlayPlane.TryComputeOverlayDataBitOffset(overlayFrame, out bitOffset)) { // offset found - unpack only that overlay frame var od = new OverlayData(bitOffset, overlayPlane.OverlayRows, overlayPlane.OverlayColumns, overlayPlane.IsBigEndianOW, overlayPlane.OverlayData); _overlayCache[overlayIndex] = od.Unpack(); } else { // no relevant overlay frame found - i.e. the overlay for this image frame is blank _overlayCache[overlayIndex] = new byte[0]; } } } overlayData = _overlayCache[overlayIndex]; } clock.Stop(); PerformanceReportBroker.PublishReport("DicomMessageSopDataSource", "CreateNormalizedOverlayData", clock.Seconds); return overlayData; }
static partial void StartClock(ref CodeClock codeClock);
/// <summary> /// Draws an <see cref="ImageGraphic"/>. /// </summary> protected override void DrawImageGraphic(ImageGraphic imageGraphic) { CodeClock clock = new CodeClock(); clock.Start(); Surface.ImageBuffer.Graphics.Clear(Color.FromArgb(0x0, 0xFF, 0xFF, 0xFF)); DrawImageGraphic(Surface.ImageBuffer, imageGraphic); Surface.FinalBuffer.RenderImage(Surface.ImageBuffer); clock.Stop(); PerformanceReportBroker.PublishReport("GDIRenderer", "DrawImageGraphic", clock.Seconds); }
static partial void StopClock(CodeClock codeClock, string method, int pixels, int subsamples);
/// <summary> /// Called when <see cref="DrawArgs.DrawMode"/> is equal to <b>DrawMode.Refresh</b>. /// </summary> protected override void Refresh() { CodeClock clock = new CodeClock(); clock.Start(); if (Surface.FinalBuffer != null) Surface.FinalBuffer.RenderToScreen(); clock.Stop(); PerformanceReportBroker.PublishReport("GDIRenderer", "Refresh", clock.Seconds); }
private void DoDraw() { EventsHelper.Fire(_drawing, this, EventArgs.Empty); CodeClock clock = new CodeClock(); clock.Start(); if (this.Surface != null) { System.Drawing.Graphics graphics = this.CreateGraphics(); this.Surface.WindowID = this.Handle; this.Surface.ContextID = graphics.GetHdc(); this.Surface.ClientRectangle = this.ClientRectangle; this.Surface.ClipRectangle = this.ClientRectangle; DrawArgs args = new DrawArgs(this.Surface, new WinFormsScreenProxy(Screen.FromControl(this)), DrawMode.Render); _isDrawing = true; try { _tile.Draw(args); _lastRenderExceptionMessage = null; } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "An error has occured while rendering the contents of a tile."); _lastRenderExceptionMessage = ex is RenderingException ? ((RenderingException) ex).UserMessage : ex.Message; // we cannot simply pass the existing Graphics because we haven't released its hDC yet // if we do, we'll get a "Object is currently in use elsewhere" exception DrawErrorMessage(_lastRenderExceptionMessage, Surface.ContextID, ClientRectangle); } finally { graphics.ReleaseHdc(this.Surface.ContextID); graphics.Dispose(); _isDrawing = false; } } //Cause the tile to paint/refresh. Invalidate(); Update(); clock.Stop(); string str = String.Format("TileControl.Draw: {0}, {1}\n", clock.ToString(), this.Size.ToString()); Trace.Write(str); }
/// <summary> /// Draws the Text Overlay. /// </summary> protected void DrawTextOverlay(IPresentationImage presentationImage) { CodeClock clock = new CodeClock(); clock.Start(); if (presentationImage == null || !(presentationImage is IAnnotationLayoutProvider)) return; IAnnotationLayout layout = ((IAnnotationLayoutProvider)presentationImage).AnnotationLayout; if (layout == null || !layout.Visible) return; foreach (AnnotationBox annotationBox in layout.AnnotationBoxes) { if (annotationBox.Visible) { string annotationText = annotationBox.GetAnnotationText(presentationImage); if (!String.IsNullOrEmpty(annotationText)) DrawAnnotationBox(annotationText, annotationBox); } } clock.Stop(); PerformanceReportBroker.PublishReport("RendererBase", "DrawTextOverlay", clock.Seconds); }
private void Render(bool fullQuality, UpdateOverlayCallback updateOverlayCallback) { lock (_lockRender) { try { var mirrored = false; var mTime = -1; if (_sceneGraphRoot != null) { _sceneGraphRoot.UpdateSceneGraph(_vtkRenderer); mirrored = _sceneGraphRoot.ViewPortSpatialTransform.FlipX ^ _sceneGraphRoot.ViewPortSpatialTransform.FlipY; mTime = _sceneGraphRoot.GetMTime(); } _vtkRenderWindow.SetDesiredUpdateRate(fullQuality ? _stillFrameRate : _dynamicFrameRate); // decide whether or not to render the VTK layer of the image based on the last modification time of the scene graph // do not use the renderer or renderwindow's MTime because they are affected by the update rate change above var renderTime = -1f; if (mTime > _lastRenderTime) { var renderClock = _renderClock ?? (_renderClock = new CodeClock()); renderClock.Clear(); renderClock.Start(); var bmpData = ImageBuffer.Bitmap.LockBits(new Rectangle(0, 0, _clientRectangle.Width, _clientRectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb); try { _vtkRenderWindow.Render(); // since we just rendered, the correct OpenGL context is still 'current' on this thread so we don't need to makeCurrent glReadBuffer(GL_FRONT_LEFT); // select the front buffer glDisable(GL_TEXTURE_2D); // according to VTK code, some video drivers have issues with this feature glPixelStorei(GL_PACK_ALIGNMENT, 4); // align to 4 byte boundaries (since we're copying 32-bit pixels anyway) // now read from the OpenGL buffer directly into our surface buffer var pData = bmpData.Stride > 0 ? bmpData.Scan0 : bmpData.Scan0 + (bmpData.Height - 1)*bmpData.Stride; glReadPixels(0, 0, _clientRectangle.Width, _clientRectangle.Height, GL_BGRA, OpenGlImplementation.ReadPixelsTypeBgra, pData); // OpenGL buffer data is a bottom-up image, and the GDI+ memory bitmap might be top-bottom, so we flip the scan lines here if (bmpData.Stride > 0) FlipImage(bmpData.Scan0, bmpData.Height, bmpData.Stride); } finally { ImageBuffer.Bitmap.UnlockBits(bmpData); // only record the last render time for full quality renders only if (fullQuality) _lastRenderTime = mTime; } renderClock.Stop(); renderTime = renderClock.Seconds; // perform a single horizontal flip here if necessary, since the VTK camera does not support a mirrorred view port ImageBuffer.Bitmap.RotateFlip(mirrored ? RotateFlipType.RotateNoneFlipX : RotateFlipType.RotateNoneFlipNone); } if (renderTime >= 0) { if (VtkPresentationImageRenderer.ShowFps) { var font = _gdiFont ?? (_gdiFont = new Font(FontFamily.GenericMonospace, 12, FontStyle.Bold, GraphicsUnit.Point)); var msg = string.Format("FPS: {0,6}", renderTime >= 0.000001 ? (1/renderTime).ToString("f1") : "------"); ImageBuffer.Graphics.DrawString(msg, font, Brushes.Black, 11, 11); ImageBuffer.Graphics.DrawString(msg, font, Brushes.White, 10, 10); if (fullQuality) { msg = string.Format("TTI: {0,6} ms", renderTime >= 0.000001 ? (renderTime*1000).ToString("f1") : "------"); ImageBuffer.Graphics.DrawString(msg, font, Brushes.Black, 11, 15 + 11); ImageBuffer.Graphics.DrawString(msg, font, Brushes.White, 10, 15 + 10); } } } if (fullQuality) { if (_reportRenderingPerformance && _statRenderFrameCount > 0 && _statRenderDuration > 0.000001) { var avgLowFrameRate = _statRenderFrameCount/_statRenderDuration; Platform.Log(LogLevel.Info, "VTKRenderer: LOD FPS: {0:f1} ({1} frame(s) in {2:f1} ms); FINAL: {3:f1} ms", avgLowFrameRate, _statRenderFrameCount, _statRenderDuration*1000, renderTime*1000); } _statRenderFrameCount = 0; _statRenderDuration = 0; } else { _statRenderDuration += renderTime; ++_statRenderFrameCount; } if (updateOverlayCallback != null) updateOverlayCallback.Invoke(); FinalBuffer.RenderImage(ImageBuffer); FinalBuffer.RenderImage(OverlayBuffer); FinalBuffer.RenderToScreen(); EventsHelper.Fire(_invalidated, this, new EventArgs()); } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "VTK Rendering Exception"); } } }