private static void Main(string[] args) { foreach (var slideName in Util.GetSlideFilenames(new string[] { args[1] })) { using (var slideCache = new SlideCache(slideName)){ foreach (var annotation in slideCache.Slide.GetAnnotations()) { var contained = new List <IAnnotation>(); foreach (var candidate in slideCache.Slide.GetAnnotations()) { if (candidate == annotation) { continue; } var rectangle = annotation.BoundingBox; rectangle.Intersect(candidate.BoundingBox); if (rectangle.IsEmpty) { continue; } if (annotation.BoundingBox.Contains(candidate.BoundingBox)) { contained.Add(candidate); } } var name = annotation.Name + "." + slideCache.SlideName + "." + annotation.Id; using (var b = annotation.Extract(1, contained)) b.Save(args[0] + "\\" + name + ".png"); Console.WriteLine(name + " exc:" + contained.Count); } } } }
private static void Main(string[] args) { foreach (var slideName in Util.GetSlideFilenames(args)) { using (var slideCache = new SlideCache(slideName)){ var hValues = File.Exists(slideCache.DataPath + "hValues.xml")?SlidePartitioner <double> .Load(slideCache.DataPath + "hValues.xml"):new SlidePartitioner <double>(slideCache.Slide, 1f, new Size(2000, 2000)); var eValues = File.Exists(slideCache.DataPath + "eValues.xml")?SlidePartitioner <double> .Load(slideCache.DataPath + "eValues.xml"):hValues.Duplicate <double>(); var hRange = new Range <double>(); var eRange = new Range <double>(); foreach (var tile in hValues.Values) { using (Bitmap tileImage = slideCache.Slide.GetImagePart(tile), hImage = tileImage.Clone() as Bitmap, eImage = tileImage.Clone() as Bitmap){ var gpH = new ColorDeconvolution().Get1stStain(hImage, ColorDeconvolution.KnownStain.HaematoxylinEosin); var gpE = new ColorDeconvolution().Get2ndStain(eImage, ColorDeconvolution.KnownStain.HaematoxylinEosin); var hSum = 0u; var eSum = 0u; var cnt = 0; foreach (var grayscalePixel in gpH.Pixels()) { hSum += grayscalePixel.V; eSum += gpE.GetPixel(grayscalePixel.X, grayscalePixel.Y); cnt++; } var meanH = (double)hSum / (double)cnt; tile.Data = meanH; hRange.Add(meanH); var meanE = (double)eSum / (double)cnt; eValues[tile.Index].Data = meanE; eRange.Add(meanE); gpH.Dispose(); gpE.Dispose(); if (slideCache.Slide.GetAnnotationsInArea(tile.SourceArea).Any()) { var tileCache = slideCache.GetTileCache(tile.Index); tileCache.SetImage("rgb", tileImage); tileCache.SetImage("h", gpH.Bitmap); tileCache.SetImage("e", gpE.Bitmap); } } Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done"); } var range = new Range <double> { hRange.Minimum, hRange.Maximum, eRange.Minimum, eRange.Maximum }; Func <double, Color> toColor = v => { var c = (int)Math.Round(range.Normalize(v) * 255d); return(Color.FromArgb(c, c, c)); }; slideCache.SetImage("hValues", hValues.GenerateHeatMap(toColor)); slideCache.SetImage("eValues", eValues.GenerateHeatMap(toColor)); slideCache.SetImage("overview", slideCache.Slide.GetImagePart(0, 0, slideCache.Slide.Size.Width, slideCache.Slide.Size.Height, hValues.Columns, hValues.Rows)); hValues.Save(slideCache.DataPath + "hValues.xml"); eValues.Save(slideCache.DataPath + "eValues.xml"); } } }
private static void Main(string[] args) { //Eingabe Parameter var inPathSlide = args[0]; var inPathDataset = args[1]; var inPathClassifier = args[2]; //Start der Verarbeitung var startProcTime = DateTime.Now; Console.WriteLine("Start: " + startProcTime); Console.WriteLine("Verarbeite Daten..."); //Verarbeitung //Load Dataset _dataSet = Glaukopis.Adapters.PicNetML.Util.LoadInstancesFromWekaArff(inPathDataset); //Load Classifier _classifier = (Classifier)SerializationHelper.read(inPathClassifier); foreach (var slideName in Glaukopis.SlideProcessing.Util.GetSlideFilenames(new string[] { inPathSlide })) { using (var slideCache = new SlideCache(slideName)) { //Compute Class var tissueSlidePartitioner = new SlidePartitioner <Dictionary <string, TissuAnnotaionEnum> >(slideCache.Slide, 1f, new Size(500, 500)); ComputeClass(tissueSlidePartitioner, slideCache); //Draw Heatmaps DrawHeatMapAll(tissueSlidePartitioner, slideCache); } } //Ender der Verarbeitung var endProcTime = DateTime.Now; Console.WriteLine("\n############ FINISHED ###############"); Console.WriteLine("Ende: " + endProcTime); //FINISH Console.Write("Benötigte Zeit: {0:F} Minuten", endProcTime.Subtract(startProcTime).TotalMinutes); Console.ReadKey(); }
private static void Main(string[] args) { foreach (var slideName in Util.GetSlideFilenames(args)) { using (var slideCache = new SlideCache(slideName)){ var scale = 0.2f; var tileSize = 200; var slidePartitionerFileName = slideCache.DataPath + "BudDetection.Tissue.xml"; var slidePartitioner = File.Exists(slidePartitionerFileName)?SlidePartitioner <bool?> .Load(slidePartitionerFileName):new SlidePartitioner <bool?>(slideCache.Slide, scale, new Size(tileSize, tileSize)); using (var overViewImage = slideCache.Slide.GetImagePart(0, 0, slideCache.Slide.Size.Width, slideCache.Slide.Size.Height, slidePartitioner.Columns, slidePartitioner.Rows)){ slideCache.SetImage("overview", overViewImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\... //ggf. heatmap hier erstellen } int i = 0; int max = slidePartitioner.Count; double percent = 0; foreach (var tile in slidePartitioner.Values) { percent = 100.0 * i++ / max; if (tile.Data.HasValue) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ":" + tile.Data.Value + " skipped " + percent + "%"); continue; } using (var tileImage = slideCache.Slide.GetImagePart(tile)){ if (false) { var tileCache = slideCache.GetTileCache(tile.Index); tileCache.SetImage("rgb", tileImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\[Index]\... ansonsten tileImage.Save("[uri].png") } var r = BudDetection.MyColorDeconvolution.Execute(tileImage); tile.Data = r.Value; //Wert zur Kachel speichern } Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done " + percent + "%"); if (Console.KeyAvailable) { break; } } slidePartitioner.Save(slidePartitionerFileName); using (var heatMap = slidePartitioner.GenerateHeatMap(b => b.HasValue?(b.Value?Color.Green:Color.White):Color.Black)) slideCache.SetImage("tissueHeatMap", heatMap); } } }
// This method creates and initializes the slide at the specified index and returns it. // The new slide is cached in the _slides array. private Slide GetSlide(int slideIndex, bool loadIfNeeded) { if (slideIndex < 0 || slideIndex >= SlideSettings.Slides.Count()) { return(null); } // Look into the cache first if (SlideCache.ContainsKey(slideIndex.ToString())) { return(SlideCache [slideIndex.ToString()]); } if (!loadIfNeeded) { return(null); } // Create the new slide Type slideClass = ClassOfSlide(slideIndex); var slide = (Slide)Activator.CreateInstance(slideClass); // Update its parameters var slideSettings = SlideSettings.Slides [slideIndex].Slide; if (slideSettings != null) { slide.Fetch(slideSettings); } SlideCache [slideIndex.ToString()] = slide; if (slide == null) { return(null); } // Setup the slide slide.SetupSlide(this); return(slide); }
private void WillOrderOutSlide(int slideIndex) { var slide = GetSlide(slideIndex, false); if (slide != null) { var node = slide.ContentNode; // Fade out and remove on completion SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.75f; SCNTransaction.SetCompletionBlock(() => node.RemoveFromParentNode()); node.Opacity = 0.0f; SCNTransaction.Commit(); slide.WillOrderOut(this); SlideCache.Remove(slideIndex.ToString()); } }
private static void ComputeClass(SlidePartitioner <Dictionary <string, TissuAnnotaionEnum> > tissueSlidePartitioner, SlideCache slideCache) { Parallel.ForEach(tissueSlidePartitioner.Values, (tile) => { using (var tileImage = slideCache.Slide.GetImagePart(tile)) { var tissueAnnotaion = ComputeFeatues(tileImage); var classifyDic = new Dictionary <string, TissuAnnotaionEnum>(); classifyDic.Add("ownClassificationJ48", ClassificationOwn.ClassifyJ48(tissueAnnotaion)); classifyDic.Add("ownClassifyJRip", ClassificationOwn.ClassifyJRip(tissueAnnotaion)); //Funktioiert leider nicht, immer leer "" //classifyDic.Add("wekaClassifyBagging",ClassifyWeka(tissueAnnotaion, _classifier, _dataSet)); tile.Data = classifyDic; Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done - Class: " + showOutput(tile.Data["ownClassificationJ48"])); } }); }
private static TissueAnnotaionList GetAnnotaions(String srcSlides, String outputPics) { TissueAnnotaionList annotationList = new TissueAnnotaionList(); int i = 0; //Get Anotaions from silde foreach (var slideName in Util.GetSlideFilenames(new String[] { srcSlides })) { using (var slideCache = new SlideCache(slideName)) { Parallel.ForEach(slideCache.Slide.GetAnnotations(), (annotation) => { if (abort != 0) { if (i >= abort) { return; } i++; } //Annotaions Bitmap extrahieren var contained = new List <IAnnotation>(); foreach (var candidate in slideCache.Slide.GetAnnotations()) { if (candidate == annotation) { continue; } var rectangle = annotation.BoundingBox; rectangle.Intersect(candidate.BoundingBox); if (rectangle.IsEmpty) { continue; } if (annotation.BoundingBox.Contains(candidate.BoundingBox)) { contained.Add(candidate); } } using (Bitmap annotationBitmap = annotation.Extract(1, contained)) { TissueAnnotationClass tissueAnnotation = new TissueAnnotationClass(annotation.Id, annotation.Name, slideCache.SlideName); //Werte Berechnsen tissueAnnotation = tissueAnnotation.ComputeFeatureValues(annotationBitmap); //Zur Liste hinzufügren annotationList.add(tissueAnnotation); //FOR DEBUG Save Image if (isSave) { annotationBitmap.Save(outputPics + "\\" + tissueAnnotation + ".png"); } Console.WriteLine(tissueAnnotation + " exc:" + contained.Count); } }); if (abort != 0 && i >= abort) { return(annotationList); } } } return(annotationList); }
private static void Main(string[] args) { const double GRENZ_ENTROPIE = 0.1; foreach (var slideName in Util.GetSlideFilenames(new string[] { args[0] })) { using (var slideCache = new SlideCache(slideName)){ // scale=1 -> baselayer , sollte so klein wie möglich sein um die Rechenzeit zu minimieren // targetSize Größe der zu prozessierenden Bilder, hängt von der hardware ab und bestimmt die Auflösung der Heatmap, für niedrige scale-Werte sollte auch die Größe reduziert werden // Auflösung: Breite=tissueSlidePartitioner.Columns Höhe=tissueSlidePartitioner.Rows // var: implizit typisiert, tatsächlich stark typisiert da der Compiler den Typ kennt; RMT->Goto To Definition // Empfehlung: http://shop.oreilly.com/product/0636920040323.do die 5.0 gibt es auch als pdf im Internet var tissueSlidePartitioner = new SlidePartitioner <bool>(slideCache.Slide, 0.2f, new Size(500, 500)); //Nicht unbedingt auf dem Baselayerr arbeiten zB. 1f --> 0.1 using (var overViewImage = slideCache.Slide.GetImagePart(0, 0, slideCache.Slide.Size.Width, slideCache.Slide.Size.Height, tissueSlidePartitioner.Columns, tissueSlidePartitioner.Rows)){ //TODO falls die Gewebeerkennung auf dem Übersichtsbild stattfinden soll, dann hier slideCache.SetImage("overview", overViewImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\... } //Multithreading Parallel.ForEach(tissueSlidePartitioner.Values, (tile) => { using (var tileImage = slideCache.Slide.GetImagePart(tile)) { var containsTissue = false; double entropie = 0.0; #region hier sollte containsTissue richtig bestimmt werden var bitmapProcessor = new BitmapProcessor(tileImage);//liefert schnelleren Zugriff auf die Pixel-Werte, alternativ auch SharpAccessory.Imaging.Processors.GrayscaleProcessor int[] greyArray = new int[256]; for (var y = 0; y < bitmapProcessor.Height; y++) { for (var x = 0; x < bitmapProcessor.Width; x++) { var r = bitmapProcessor.GetRed(x, y); var g = bitmapProcessor.GetGreen(x, y); var b = bitmapProcessor.GetBlue(x, y); var grauwert = (int)(r + g + b) / 3; greyArray[grauwert]++; } } bitmapProcessor.Dispose(); //Calculate Shannon Entropie entropie = calcEntropie(greyArray); //Contains Tissue ermitteln if (0 == tile.Index.Y) { containsTissue = false;//oberste Reihe sollte kein Gewebe enthalten } else { //if (slideCache.Slide.GetAnnotationsInArea(tile.SourceArea).Any()) containsTissue = true;//Kacheln mit Annotationen enthalten Gewebe if (entropie > GRENZ_ENTROPIE) { containsTissue = true; } } #endregion //Wert zur Kachel speichern tile.Data = containsTissue; //Only for Debug var saveImage = false; if (saveImage) { string path = args[1] + @"\" + slideCache.SlideName + @"\"; if (containsTissue) { path += @"isTissue\"; } else { path += @"noTissue\"; } if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } tileImage.Save(path + tile.Index.X + "-" + tile.Index.Y + ".png"); //var tileCache = slideCache.GetTileCache(tile.Index); //tileCache.SetImage("rgb",tileImage);//speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\[Index]\... ansonsten tileImage.Save("[uri].png") } Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done - containsTissue: " + containsTissue.ToString() + " - entropie: " + entropie.ToString()); } }); //true wird zu grün, false zu rot; syntax ist lambda (=>) mit einem conditional operator (?) Func <bool, Color> f = b => { if (b) { return(Color.Red); } else { return(Color.Green); } }; using (var heatMap = tissueSlidePartitioner.GenerateHeatMap(f)) { slideCache.SetImage("tissueHeatMap", heatMap); } } } }
private static void DrawHeatMapOneClass(SlidePartitioner <Dictionary <String, TissuAnnotaionEnum> > tissueSlidePartitioner, SlideCache slideCache, TissuAnnotaionEnum annotaion) { }
private static void DrawHeatMapAll(SlidePartitioner <Dictionary <String, TissuAnnotaionEnum> > tissueSlidePartitioner, SlideCache slideCache) { Func <Dictionary <String, TissuAnnotaionEnum>, Color> drawWekaClassificationBaggingFunc = classifyDic => TissuAnnotaionToColor(classifyDic["wekaClassifyBagging"]); Func <Dictionary <String, TissuAnnotaionEnum>, Color> drawOwnClassificationJ48Func = classifyDic => TissuAnnotaionToColor(classifyDic["ownClassificationJ48"]); Func <Dictionary <String, TissuAnnotaionEnum>, Color> drawOwnClassificationJRipFunc = classifyDic => TissuAnnotaionToColor(classifyDic["ownClassifyJRip"]); /* using (var heatMap = tissueSlidePartitioner.GenerateHeatMap(drawWekaClassificationBaggingFunc)) * { * slideCache.SetImage("ClassificationDetection_wekaClassifyBagging", heatMap); * }*/ using (var heatMap = tissueSlidePartitioner.GenerateHeatMap(drawOwnClassificationJ48Func)) { slideCache.SetImage("ClassificationDetection_ownClassifyJ48", heatMap); } using (var heatMap = tissueSlidePartitioner.GenerateHeatMap(drawOwnClassificationJRipFunc)) { slideCache.SetImage("ClassificationDetection_ownClassifyJRip", heatMap); } }
private static void Main(string[] args) { Console.WriteLine("Start SlideProzessor"); Console.WriteLine(args); foreach (var slideName in Util.GetSlideFilenames(args)) { Console.WriteLine("progress: " + slideName); using (var slideCache = new SlideCache(slideName)){ // Skalierung des Bildes var scale = 0.5f; // 0.1 var tileSize = 100; // 100 // Partionierung und speichern var slidePartitionerFileName = slideCache.DataPath + "BudDetection.Tissue.xml"; var slidePartitioner = File.Exists(slidePartitionerFileName)?SlidePartitioner <bool?> .Load(slidePartitionerFileName):new SlidePartitioner <bool?>(slideCache.Slide, scale, new Size(tileSize, tileSize)); using (var overViewImage = slideCache.Slide.GetImagePart(0, 0, slideCache.Slide.Size.Width, slideCache.Slide.Size.Height, slidePartitioner.Columns, slidePartitioner.Rows)){ slideCache.SetImage("overview", overViewImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\... //ggf. heatmap hier erstellen } // über alle Kacheln var counter = 0; foreach (var tile in slidePartitioner.Values) { counter = counter + 1; // ist bereits ein wert vorhanden, dann überspringen if (tile.Data.HasValue) { if (counter % 100 == 0) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ":" + tile.Data.Value + " skipped"); } continue; } // bitmap erzeugen aus der Kachel using (var tileImage = slideCache.Slide.GetImagePart(tile)){ // für debugging zwecke if (false) { var tileCache = slideCache.GetTileCache(tile.Index); tileCache.SetImage("rgb", tileImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\[Index]\... ansonsten tileImage.Save("[uri].png") } // Wert Berechnung var r = BudDetection.ColorDeconvolution.Execute(tileImage); tile.Data = r.Value; //Wert zur Kachel speichern } if (counter % 100 == 0) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done"); } // unterbrechbar via key eingabe if (Console.KeyAvailable) { break; } } // zustand der bearbeitng speichern slidePartitioner.Save(slidePartitionerFileName); using (var heatMap = slidePartitioner.GenerateHeatMap(b => b.HasValue?(b.Value?Color.Green:Color.White):Color.Black)) slideCache.SetImage("tissueHeatMap", heatMap); } } }
private static void Main(string[] args) { foreach (var slideName in Util.GetSlideFilenames(args)) { using (var slideCache = new SlideCache(slideName)){ var scale = 0.5f; var tileSize = 500; var slidePartitionerFileName = slideCache.DataPath + "BudDetection.Buds.xml"; var slidePartitioner = File.Exists(slidePartitionerFileName)?SlidePartitioner <int?> .Load(slidePartitionerFileName):new SlidePartitioner <int?>(slideCache.Slide, scale, new Size(tileSize, tileSize)); var heatMapHelper = new HeatMapHelper(slideCache.GetImage("tissueHeatMap")); var budCountRange = new Range <int>(); foreach (var tile in slidePartitioner.Values) { if (tile.Data.HasValue) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ":" + tile.Data.Value + " skipped"); budCountRange.Add(tile.Data.Value); continue; } var colors = heatMapHelper.GetAffectedColors(slideCache.Slide.Size, tile.SourceArea); var values = heatMapHelper.GetAffectedValues(slideCache.Slide.Size, tile.SourceArea); if (values.All(v => 0 != v)) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ": no tissue"); continue; } using (var tileImage = slideCache.Slide.GetImagePart(tile)){ if (false) { var tileCache = slideCache.GetTileCache(tile.Index); tileCache.SetImage("rgb", tileImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\[Index]\... ansonsten tileImage.Save("[uri].png") } var r = BudDetection.Detector.Execute(tileImage); //var r = ColorDeconvolution.Detect(tileImage); var layer = r.Value; tile.Data = layer.Objects.Count; budCountRange.Add(layer.Objects.Count); foreach (var io in layer.Objects) { //var bb=io.Contour.FindBoundingBox(); var offset = tile.SourceArea.Location; var sourcePoints = new List <Point>(); foreach (var contourPoint in io.Contour.GetPoints()) { double x = Math.Round(contourPoint.X / scale + offset.X); double y = Math.Round(contourPoint.Y / scale + offset.Y); var sourcePoint = new Point((int)x, (int)y); sourcePoints.Add(sourcePoint); } var annotation = slideCache.Slide.CreateAnnotation(AnnotationType.PolygonLine); foreach (var point in sourcePoints) { annotation.AppendPoints(point); } annotation.Color = Color.OrangeRed; annotation.Name = null == io.Class?"bud":io.Class.Name; } } Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done"); if (Console.KeyAvailable) { break; } } slidePartitioner.Save(slidePartitionerFileName); using (var heatMap = slidePartitioner.GenerateHeatMap(b => { if (!b.HasValue) { return(Color.LightSeaGreen); } var c = (int)Math.Round(budCountRange.Normalize(b.Value) * 255d); return(Color.FromArgb(c, c, c)); })) slideCache.SetImage("budHeatMap", heatMap); } } }
private static void Main(string[] args) { foreach (var slideName in Util.GetSlideFilenames(args)) { using (var slideCache = new SlideCache(slideName)){ // var scale = 0.1f; var scale = 0.5f; // besser so laut 19.07.2018 var tileSize = 100; var slidePartitionerFileName = slideCache.DataPath + "BudDetection.Buds.xml"; var slidePartitioner = File.Exists(slidePartitionerFileName)?SlidePartitioner <int?> .Load(slidePartitionerFileName):new SlidePartitioner <int?>(slideCache.Slide, scale, new Size(tileSize, tileSize)); var heatMapHelper = new HeatMapHelper(slideCache.GetImage("tissueHeatMap")); var budCountRange = new Range <int>(); var counter = 0; foreach (var tile in slidePartitioner.Values) { // test, ob schon was für die kackel berechnet wurde if (tile.Data.HasValue) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ":" + tile.Data.Value + " skipped"); budCountRange.Add(tile.Data.Value); continue; } // soll kachel bearbeitet werden var colors = heatMapHelper.GetAffectedColors(slideCache.Slide.Size, tile.SourceArea); var values = heatMapHelper.GetAffectedValues(slideCache.Slide.Size, tile.SourceArea); if (values.All(v => 255 == v)) // 255 weiß { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + ": no tissue"); continue; } using (var tileImage = slideCache.Slide.GetImagePart(tile)){ counter = counter + 1; if (false) { var tileCache = slideCache.GetTileCache(tile.Index); tileCache.SetImage("rgb", tileImage); //speichert unter C:\ProgramData\processingRepository\[slideCache.SlideName]\[Index]\... ansonsten tileImage.Save("[uri].png") } var r = BudDetection.Detector.Execute(tileImage); // importent call var layer = r.Value; tile.Data = layer.Objects.Count; budCountRange.Add(layer.Objects.Count); // für alle gefundene objekte wird eine annotation angelegt foreach (var io in layer.Objects) { //var bb=io.Contour.FindBoundingBox(); var offset = tile.SourceArea.Location; var sourcePoints = new List <Point>(); foreach (var contourPoint in io.Contour.GetPoints()) { double x = Math.Round(contourPoint.X / scale + offset.X); double y = Math.Round(contourPoint.Y / scale + offset.Y); var sourcePoint = new Point((int)x, (int)y); sourcePoints.Add(sourcePoint); } var annotation = slideCache.Slide.CreateAnnotation(AnnotationType.PolygonLine); foreach (var point in sourcePoints) { annotation.AppendPoints(point); } annotation.Color = Color.PaleVioletRed; annotation.Name = null == io.Class?"bud":io.Class.Name; } } if (counter % 100 == 0) { Console.WriteLine(slideCache.SlideName + "-" + tile.Index + " done"); } if (Console.KeyAvailable) { break; } } slidePartitioner.Save(slidePartitionerFileName); // normalisierung using (var heatMap = slidePartitioner.GenerateHeatMap(b => { if (!b.HasValue) { return(Color.LightSeaGreen); } var c = (int)Math.Round(budCountRange.Normalize(b.Value) * 255d); return(Color.FromArgb(c, c, c)); // grauwert(hell == viele butts) })) slideCache.SetImage("budHeatMap", heatMap); } } }