BioLib  3.9.1
A GUI-less version of Bio .NET library for editing & annotating various microscopy image formats.
Loading...
Searching...
No Matches
BioLib.SlideSourceBase Class Referenceabstract
Inheritance diagram for BioLib.SlideSourceBase:
BioLib.ISlideSource BioLib.ISliceProvider BioLib.ISlideExternInfo BioLib.SlideBase

Public Member Functions

bool HasTile (TileInfo tile, ZCT coord)
 
async Task FetchTilesAsync (List< BruTile.TileInfo > tiles, int level, ZCT coordinate, PointD PyramidalOrigin, AForge.Size PyramidalSize)
 Fetches tiles in priority order: center viewport first, then edges. Ensures visible content appears before prefetch content.
 
async Task< byte[]> GetSlice (SliceInfo sliceInfo, PointD PyramidalOrign, AForge.Size PyramidalSize)
 Get slice.
 
byte[] Get8BitBytes (Image< L8 > image)
 
byte[] GetRgb24Bytes (Image< Rgb24 > image)
 
byte[] Get16Bytes (Image< L16 > image)
 
IReadOnlyDictionary< string, byte[]> GetExternImages ()
 Extern image.
 
void Dispose ()
 
async Task< byte[]> GetTileAsync (TileInformation tileInfo, ZCT coord)
 
async Task< byte[]> GetTileAsync (BruTile.TileInfo tileInfo, ZCT coord)
 
- Public Member Functions inherited from BioLib.ISliceProvider
- Public Member Functions inherited from BioLib.ISlideExternInfo

Static Public Member Functions

static ISlideSource Create (BioImage source, SlideImage im, bool enableCache=true)
 

Public Attributes

TileCache cache = null
 
Stitch stitch = new Stitch()
 

Static Public Attributes

static Extent destExtent
 
static Extent sourceExtent
 
static double curUnitsPerPixel = 1
 
static bool UseVips = true
 

Protected Member Functions

virtual void Dispose (bool disposing)
 

Properties

static bool UseRealResolution = true [get, set]
 
double MinUnitsPerPixel [get, protected set]
 um/pixel
 
static bool UseGPU [get, set]
 
SlideImage Image [get, set]
 
ITileSchema Schema [get, protected set]
 
string Name [get, protected set]
 
Attribution Attribution [get, protected set]
 
IReadOnlyDictionary< string, object > ExternInfo [get, protected set]
 Extern info.
 
string Source [get, protected set]
 File path.
 
- Properties inherited from BioLib.ISliceProvider
- Properties inherited from BioLib.ISlideExternInfo

Member Function Documentation

◆ Create()

static ISlideSource BioLib.SlideSourceBase.Create ( BioImage source,
SlideImage im,
bool enableCache = true )
static
197 {
198
199 var ext = Path.GetExtension(source.file).ToUpper();
200 try
201 {
202 if (keyValuePairs.TryGetValue(ext, out var factory) && factory != null)
203 return factory.Invoke(source.file, enableCache);
204
205 if (!string.IsNullOrEmpty(SlideBase.DetectVendor(source.file)))
206 {
207 SlideBase b = new SlideBase(source, im, enableCache);
208
209 }
210 }
211 catch (Exception e)
212 {
213 Console.WriteLine(e.Message);
214 }
215 return null;
216 }

◆ Dispose() [1/2]

void BioLib.SlideSourceBase.Dispose ( )
597 {
598 Dispose(disposing: true);
599 GC.SuppressFinalize(this);
600 }

◆ Dispose() [2/2]

virtual void BioLib.SlideSourceBase.Dispose ( bool disposing)
protectedvirtual
580 {
581 if (!disposedValue)
582 {
583 if (disposing)
584 {
585 //_bgraCache.Dispose();
586 }
587 disposedValue = true;
588 }
589 }

◆ FetchTilesAsync()

async Task BioLib.SlideSourceBase.FetchTilesAsync ( List< BruTile.TileInfo > tiles,
int level,
ZCT coordinate,
PointD PyramidalOrigin,
AForge.Size PyramidalSize )

Fetches tiles in priority order: center viewport first, then edges. Ensures visible content appears before prefetch content.

281 {
282 if (tiles == null)
283 return;
284 // --------------------------------------------------------------------
285 // 1. Calculate viewport center (base-resolution coordinates)
286 // --------------------------------------------------------------------
287 double centerX = PyramidalOrigin.X + (PyramidalSize.Width * 0.5);
288 double centerY = PyramidalOrigin.Y + (PyramidalSize.Height * 0.5);
289
290 // --------------------------------------------------------------------
291 // 2. Sort tiles by squared distance (avoid sqrt)
292 // --------------------------------------------------------------------
293 var prioritizedTiles = tiles
294 .OrderBy(tile =>
295 {
296 double dx = tile.Extent.CenterX - centerX;
297 double dy = tile.Extent.CenterY - centerY;
298 return (dx * dx) + (dy * dy);
299 })
300 .ToList();
301
302 // --------------------------------------------------------------------
303 // 3. Fetch tiles in batches
304 // --------------------------------------------------------------------
305 int batchSize = GetOptimalBatchSize(PyramidalOrigin, PyramidalSize);
306
307 for (int i = 0; i < prioritizedTiles.Count; i += batchSize)
308 {
309 var batch = prioritizedTiles.Skip(i).Take(batchSize).ToList();
310
311 // Parallel fetch within batch
312 byte[][] results = await Task.WhenAll(
313 batch.Select(tile => FetchSingleTileAsync(tile, level, coordinate))
314 );
315
316 // ----------------------------------------------------------------
317 // 4. Insert into appropriate cache
318 // ----------------------------------------------------------------
319 for (int j = 0; j < batch.Count; j++)
320 {
321 var tile = batch[j];
322 var data = results[j];
323
324 if (data == null)
325 continue;
326 var info = new Info(
327 coordinate,
328 tile.Index,
329 this.Schema.Extent,
330 level
331 );
332 TileInformation tf = new TileInformation(tile.Index, tile.Extent, coord);
333 if (cache == null)
334 cache = new TileCache(this);
335 if(!cache.HasTile(tf))
336 cache.AddTile(tf, data);
337 TileInfo t = new TileInfo();
338 t.Extent = tile.Extent;
339 t.Index = tile.Index;
340 stitch.AddTile(new Stitch.GpuTile(t,data));
341 }
342 }
343 }

◆ Get16Bytes()

byte[] BioLib.SlideSourceBase.Get16Bytes ( Image< L16 > image)
542 {
543 int width = image.Width;
544 int height = image.Height;
545 byte[] bytes = new byte[width * height * 2];
546
547 int byteIndex = 0;
548 for (int y = 0; y < height; y++)
549 {
550 for (int x = 0; x < width; x++)
551 {
552 L16 pixel = image[x, y];
553 byte[] bts = BitConverter.GetBytes(pixel.PackedValue);
554 bytes[byteIndex++] = bts[0];
555 bytes[byteIndex++] = bts[1];
556 }
557 }
558
559 return bytes;
560 }

◆ Get8BitBytes()

byte[] BioLib.SlideSourceBase.Get8BitBytes ( Image< L8 > image)
504 {
505 int width = image.Width;
506 int height = image.Height;
507 byte[] rgbBytes = new byte[width * height]; // 3 bytes per pixel (RGB)
508
509 int byteIndex = 0;
510 for (int y = 0; y < height; y++)
511 {
512 for (int x = 0; x < width; x++)
513 {
514 L8 pixel = image[x, y];
515 rgbBytes[byteIndex++] = pixel.PackedValue;
516 }
517 }
518
519 return rgbBytes;
520 }

◆ GetExternImages()

IReadOnlyDictionary< string, byte[]> BioLib.SlideSourceBase.GetExternImages ( )
abstract

Extern image.

Returns

Implements BioLib.ISlideExternInfo.

◆ GetRgb24Bytes()

byte[] BioLib.SlideSourceBase.GetRgb24Bytes ( Image< Rgb24 > image)
522 {
523 int width = image.Width;
524 int height = image.Height;
525 byte[] rgbBytes = new byte[width * height * 3]; // 3 bytes per pixel (RGB)
526
527 int byteIndex = 0;
528 for (int y = 0; y < height; y++)
529 {
530 for (int x = 0; x < width; x++)
531 {
532 Rgb24 pixel = image[x, y];
533 rgbBytes[byteIndex++] = pixel.B;
534 rgbBytes[byteIndex++] = pixel.G;
535 rgbBytes[byteIndex++] = pixel.R;
536 }
537 }
538
539 return rgbBytes;
540 }

◆ GetSlice()

async Task< byte[]> BioLib.SlideSourceBase.GetSlice ( SliceInfo sliceInfo,
PointD PyramidalOrigin,
AForge.Size PyramidalSize )

Get slice.

Parameters
sliceInfoSlice info
Returns

Implements BioLib.ISliceProvider.

345 {
346 if (cache == null)
347 cache = new TileCache(this);
348 var curLevel = Image.BioImage.LevelFromResolution(sliceInfo.Resolution);
349 var curUnitsPerPixel = Schema.Resolutions[curLevel].UnitsPerPixel;
350
351 var tileInfos = Schema.GetTileInfos(sliceInfo.Extent, curLevel);
352 if (tileInfos.Count() == 0)
353 {
354 tileInfos = Schema.GetTileInfos(sliceInfo.Extent.PixelToWorldInvertedY(curUnitsPerPixel), curLevel);
355 }
356 await FetchTilesAsync(tileInfos.ToList(), curLevel, sliceInfo.Coordinate, PyramidalOrign, PyramidalSize);
357
358 var srcPixelExtent = sliceInfo.Extent.WorldToPixelInvertedY(curUnitsPerPixel);
359 var dstPixelExtent = sliceInfo.Extent.WorldToPixelInvertedY(sliceInfo.Resolution);
360 var dstPixelHeight = sliceInfo.Parame.DstPixelHeight > 0 ? sliceInfo.Parame.DstPixelHeight : dstPixelExtent.Height;
361 var dstPixelWidth = sliceInfo.Parame.DstPixelWidth > 0 ? sliceInfo.Parame.DstPixelWidth : dstPixelExtent.Width;
362 destExtent = new Extent(0, 0, dstPixelWidth, dstPixelHeight);
363 sourceExtent = srcPixelExtent;
364 if (UseGPU)
365 {
366 try
367 {
368 if (stitch == null)
369 {
370 Console.WriteLine("GPU stitching not initialized - falling back to CPU");
371 UseGPU = false;
372 }
373 else if (tileInfos.Count() > 0)
374 {
375 // Get standard tile dimensions from schema
376 var schemaTileWidth = Schema.Resolutions[curLevel].TileWidth;
377 var schemaTileHeight = Schema.Resolutions[curLevel].TileHeight;
378
379 // Upload tiles to GPU stitch before rendering
380 foreach (BruTile.TileInfo t in tileInfos)
381 {
382 if (!stitch.HasTile(t))
383 {
384 TileInformation tf = new TileInformation(t.Index, t.Extent, sliceInfo.Coordinate);
385 byte[] tileData = await cache.GetTile(tf);
386 if (tileData != null)
387 {
388 // Calculate actual tile dimensions (edge tiles may be smaller)
389 var curTileWidth = (int)(t.Extent.MaxX > Schema.Extent.Width
390 ? schemaTileWidth - (t.Extent.MaxX - Schema.Extent.Width) / curUnitsPerPixel
391 : schemaTileWidth);
392 var curTileHeight = (int)(-t.Extent.MinY > Schema.Extent.Height
393 ? schemaTileHeight - (-t.Extent.MinY - Schema.Extent.Height) / curUnitsPerPixel
394 : schemaTileHeight);
395
396 // Validate dimensions against actual data
397 int expectedSize = curTileWidth * curTileHeight * 4;
398 if (tileData.Length != expectedSize)
399 {
400 // Fallback: derive from data length assuming RGBA
401 int pixelCount = tileData.Length / 4;
402 if (pixelCount == schemaTileWidth * schemaTileHeight)
403 {
404 curTileWidth = schemaTileWidth;
405 curTileHeight = schemaTileHeight;
406 }
407 else
408 {
409 // Try to find matching dimensions
410 curTileWidth = schemaTileWidth;
411 curTileHeight = pixelCount / schemaTileWidth;
412 if (curTileWidth * curTileHeight != pixelCount)
413 {
414 // Square fallback
415 curTileWidth = (int)Math.Sqrt(pixelCount);
416 curTileHeight = curTileWidth;
417 }
418 }
419 }
420
421 var gpuTile = new Stitch.GpuTile(t, tileData);
422 stitch.AddTile(gpuTile);
423 }
424 }
425 }
426 }
427 }
428 catch (Exception e)
429 {
430 Console.WriteLine(e.Message.ToString());
431 UseVips = true;
432 UseGPU = false;
433 }
434 }
435 else
436 if (UseVips)
437 {
438 try
439 {
440 List<Tuple<Extent, byte[]>> tiles = new List<Tuple<Extent, byte[]>>();
441 foreach (BruTile.TileInfo t in tileInfos)
442 {
443 TileInformation tf = new TileInformation(t.Index, t.Extent, sliceInfo.Coordinate);
444 byte[] c = await cache.GetTile(tf);
445 if (c != null)
446 tiles.Add(Tuple.Create(t.Extent.WorldToPixelInvertedY(curUnitsPerPixel), c));
447 }
448 NetVips.Image im = null;
449 if (Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format16bppGrayScale)
450 im = ImageUtil.JoinVips16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
451 else if (Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format24bppRgb)
452 im = ImageUtil.JoinVipsRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
453 return im.WriteToMemory();
454 }
455 catch (Exception e)
456 {
457 UseVips = false;
458 Console.WriteLine("Failed to use LibVips please install Libvips for your platform.");
459 Console.WriteLine(e.Message);
460 }
461 }
462 try
463 {
464 Image im = null;
465 List<Tuple<Extent, byte[]>> tiles = new List<Tuple<Extent, byte[]>>();
466 foreach (BruTile.TileInfo t in tileInfos)
467 {
468 TileInformation tf = new TileInformation(t.Index, t.Extent, sliceInfo.Coordinate);
469 byte[] c = await cache.GetTile(tf);
470 if (c != null)
471 tiles.Add(Tuple.Create(t.Extent.WorldToPixelInvertedY(curUnitsPerPixel), c));
472 }
473 if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format16bppGrayScale)
474 {
475 im = ImageUtil.Join16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
476 byte[] bts = Get16Bytes((Image<L16>)im);
477 im.Dispose();
478 return bts;
479 }
480 else if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format24bppRgb)
481 {
482 im = ImageUtil.JoinRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
483 byte[] bts = GetRgb24Bytes((Image<Rgb24>)im);
484 im.Dispose();
485 return bts;
486 }
487 else if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format8bppIndexed)
488 {
489 im = ImageUtil.Join8Bit(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
490 byte[] bts = Get8BitBytes((Image<L8>)im);
491 im.Dispose();
492 return bts;
493 }
494 }
495 catch (Exception er)
496 {
497 Console.WriteLine(er.Message);
498 return null;
499 }
500 return null;
501 }
async Task FetchTilesAsync(List< BruTile.TileInfo > tiles, int level, ZCT coordinate, PointD PyramidalOrigin, AForge.Size PyramidalSize)
Fetches tiles in priority order: center viewport first, then edges. Ensures visible content appears b...
Definition ISlideSource.cs:280

◆ GetTileAsync() [1/2]

async Task< byte[]> BioLib.SlideSourceBase.GetTileAsync ( BruTile.TileInfo tileInfo,
ZCT coord )
623 {
624 if (tileInfo == null)
625 return null;
626 if (cache == null)
627 cache = new TileCache(this);
628 if(cache.HasTile(new TileInformation(tileInfo.Index,tileInfo.Extent,coord)))
629 {
630 return await cache.GetTile(new TileInformation(tileInfo.Index, tileInfo.Extent, coord));
631 }
632 var r = Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
633 var tileWidth = Schema.Resolutions[tileInfo.Index.Level].TileWidth;
634 var tileHeight = Schema.Resolutions[tileInfo.Index.Level].TileHeight;
635 var curLevelOffsetXPixel = tileInfo.Extent.MinX / Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
636 var curLevelOffsetYPixel = -tileInfo.Extent.MaxY / Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
637 var curTileWidth = (int)(tileInfo.Extent.MaxX > Schema.Extent.Width ? tileWidth - (tileInfo.Extent.MaxX - Schema.Extent.Width) / r : tileWidth);
638 var curTileHeight = (int)(-tileInfo.Extent.MinY > Schema.Extent.Height ? tileHeight - (-tileInfo.Extent.MinY - Schema.Extent.Height) / r : tileHeight);
639
640 var bgraData = await Image.ReadRegionAsync(tileInfo.Index.Level, (long)curLevelOffsetXPixel, (long)curLevelOffsetYPixel, curTileWidth, curTileHeight, new ZCT());
641 cache.AddTile(new TileInformation(tileInfo.Index, tileInfo.Extent, coord), bgraData);
642 return bgraData;
643 }

◆ GetTileAsync() [2/2]

async Task< byte[]> BioLib.SlideSourceBase.GetTileAsync ( TileInformation tileInfo,
ZCT coord )
603 {
604 if (tileInfo == null)
605 return null;
606 if (cache.HasTile(new TileInformation(tileInfo.Index, tileInfo.Extent, coord)))
607 {
608 return await cache.GetTile(new TileInformation(tileInfo.Index, tileInfo.Extent, coord));
609 }
610 var r = Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
611 var tileWidth = Schema.Resolutions[tileInfo.Index.Level].TileWidth;
612 var tileHeight = Schema.Resolutions[tileInfo.Index.Level].TileHeight;
613 var curLevelOffsetXPixel = tileInfo.Extent.MinX / Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
614 var curLevelOffsetYPixel = -tileInfo.Extent.MaxY / Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
615 var curTileWidth = (int)(tileInfo.Extent.MaxX > Schema.Extent.Width ? tileWidth - (tileInfo.Extent.MaxX - Schema.Extent.Width) / r : tileWidth);
616 var curTileHeight = (int)(-tileInfo.Extent.MinY > Schema.Extent.Height ? tileHeight - (-tileInfo.Extent.MinY - Schema.Extent.Height) / r : tileHeight);
617 var bgraData = await Image.ReadRegionAsync(tileInfo.Index.Level, (long)curLevelOffsetXPixel, (long)curLevelOffsetYPixel, curTileWidth, curTileHeight, tileInfo.Coordinate);
618 cache.AddTile(new TileInformation(tileInfo.Index, tileInfo.Extent, coord), bgraData);
619 return bgraData;
620 }

◆ HasTile()

bool BioLib.SlideSourceBase.HasTile ( TileInfo tile,
ZCT coord )
237 {
238 if (cache.GetTile(new TileInformation(tile.Index, tile.Extent, coord)) != null)
239 return true;
240 else
241 return false;
242 }

Property Documentation

◆ Attribution

Attribution BioLib.SlideSourceBase.Attribution
getprotected set
568{ get; protected set; }

◆ ExternInfo

IReadOnlyDictionary<string, object> BioLib.SlideSourceBase.ExternInfo
getprotected set

Extern info.

Implements BioLib.ISlideExternInfo.

570{ get; protected set; }

◆ Image

SlideImage BioLib.SlideSourceBase.Image
getset
562{ get; set; }

◆ MinUnitsPerPixel

double BioLib.SlideSourceBase.MinUnitsPerPixel
getprotected set

um/pixel

Implements BioLib.ISliceProvider.

218{ get; protected set; }

◆ Name

string BioLib.SlideSourceBase.Name
getprotected set
566{ get; protected set; }

◆ Schema

ITileSchema BioLib.SlideSourceBase.Schema
getprotected set
564{ get; protected set; }

◆ Source

string BioLib.SlideSourceBase.Source
getprotected set

File path.

Implements BioLib.ISlideExternInfo.

572{ get; protected set; }

◆ UseGPU

bool BioLib.SlideSourceBase.UseGPU
staticgetset
224 {
225 get
226 {
227 if (OperatingSystem.IsMacOS())
228 return false;
229 else
230 return true;
231 }
232 set;
233 }

◆ UseRealResolution

bool BioLib.SlideSourceBase.UseRealResolution = true
staticgetset
193{ get; set; } = true;

The documentation for this class was generated from the following file: