BioLib  4.3.0
A GUI-less version of Bio .NET library for editing & annotating various microscopy image formats.
Loading...
Searching...
No Matches
BioLib.SlideBase Class Reference
Inheritance diagram for BioLib.SlideBase:

Public Member Functions

 SlideBase (BioImage source, SlideImage im, bool enableCache=true)
 
void RebuildSchemaForWell (BioImage b)
 
override IReadOnlyDictionary< string, byte[]> GetExternImages ()
 
byte[] GetTile (TileInfo tileInfo, ZCT coord)
 

Static Public Member Functions

static string DetectVendor (string source)
 
static byte[] ConvertRgbaToRgb (byte[] rgbaArray)
 

Public Attributes

readonly SlideImage SlideImage
 

Protected Member Functions

void InitResolutions (IDictionary< int, BruTile.Resolution > resolutions, int tileWidth, int tileHeight)
 
override void Dispose (bool disposing)
 

Constructor & Destructor Documentation

◆ SlideBase()

BioLib.SlideBase.SlideBase ( BioImage source,
SlideImage im,
bool enableCache = true )
18 {
19 Source = source.file;
20 SlideImage = im;
21 Image = im;
22 double baseUnitsPerPixel = source.PhysicalSizeX > 0
23 ? source.PhysicalSizeX
24 : source.PhysicalSizeY > 0
25 ? source.PhysicalSizeY
26 : 1;
27 MinUnitsPerPixel = UseRealResolution ? baseUnitsPerPixel : 1;
28 if (MinUnitsPerPixel <= 0) MinUnitsPerPixel = 1;
29 var height = SlideImage.Dimensions.Height;
30 var width = SlideImage.Dimensions.Width;
31 double stageX = 0;
32 double stageY = 0;
33 if (source.Resolutions != null && source.Resolutions.Count > 0)
34 {
35 stageX = source.Resolutions[0].StageSizeX;
36 stageY = source.Resolutions[0].StageSizeY;
37 }
38 //ExternInfo = GetInfo();
39 Schema = new TileSchema
40 {
41 YAxis = YAxis.OSM,
42 Format = "jpg",
43 Extent = new Extent(
44 stageX,
45 -(stageY + (height * MinUnitsPerPixel)),
46 stageX + (width * MinUnitsPerPixel),
47 -stageY),
48 OriginX = stageX,
49 OriginY = stageY,
50 };
51 InitResolutions(Schema.Resolutions, 256, 256);
52 }
readonly SlideImage SlideImage
Definition SlideBase.cs:16
void InitResolutions(IDictionary< int, BruTile.Resolution > resolutions, int tileWidth, int tileHeight)
Definition SlideBase.cs:289
ImageDimension Dimensions
Definition SlideImage.cs:139

Member Function Documentation

◆ ConvertRgbaToRgb()

static byte[] BioLib.SlideBase.ConvertRgbaToRgb ( byte[] rgbaArray)
static
274 {
275 // Initialize a new byte array for RGB24 format
276 byte[] rgbArray = new byte[(rgbaArray.Length / 4) * 3];
277
278 for (int i = 0, j = 0; i < rgbaArray.Length; i += 4, j += 3)
279 {
280 // Copy the R, G, B values, skip the A value
281 rgbArray[j] = rgbaArray[i]; // B
282 rgbArray[j + 1] = rgbaArray[i + 1]; // G
283 rgbArray[j + 2] = rgbaArray[i + 2]; // R
284 }
285
286 return rgbArray;
287 }

◆ DetectVendor()

static string BioLib.SlideBase.DetectVendor ( string source)
static
55 {
56 return SlideImage.DetectVendor(source);
57 }
static string DetectVendor(string filename)
Quickly determine whether a whole slide image is recognized.
Definition SlideImage.cs:71

◆ Dispose()

override void BioLib.SlideBase.Dispose ( bool disposing)
protected
309 {
310 if (!disposedValue)
311 {
312 if (disposing)
313 {
315 }
316 disposedValue = true;
317 }
318 base.Dispose(disposing);
319 }
virtual void Dispose(bool disposing)
Dispose.
Definition SlideImage.cs:639

◆ GetExternImages()

override IReadOnlyDictionary< string, byte[]> BioLib.SlideBase.GetExternImages ( )
141 {
142 throw new NotImplementedException();
143 /*
144 Dictionary<string, byte[]> images = new Dictionary<string, byte[]>();
145 var r = Math.Max(Schema.Extent.Height, Schema.Extent.Width) / 512;
146 images.Add("preview", GetSlice(new SliceInfo { Extent = Schema.Extent, Resolution = r }));
147 foreach (var item in SlideImage.GetAssociatedImages())
148 {
149 var dim = item.Value.Dimensions;
150 images.Add(item.Key, ImageUtil.GetJpeg(item.Value.Data, 4, 4 * (int)dim.Width, (int)dim.Width, (int)dim.Height));
151 }
152 return images;
153 */
154 }

◆ GetTile()

byte[] BioLib.SlideBase.GetTile ( TileInfo tileInfo,
ZCT coord )
156 {
157 try
158 {
159 if (Schema == null)
160 {
161 Console.WriteLine("Schema is null.");
162 return null;
163 }
164 var r = Schema.Resolutions[tileInfo.Index.Level].UnitsPerPixel;
165 var tileWidth = Schema.Resolutions[tileInfo.Index.Level].TileWidth;
166 var tileHeight = Schema.Resolutions[tileInfo.Index.Level].TileHeight;
167
168 // Tile extents are expressed in translated stage/world space, but
169 // the underlying Zarr pixels still start at image-local 0,0.
170 // Subtract the schema origin here so stage translation affects
171 // placement only, not the pixel read offset.
172 var schemaExtent = Schema.Extent;
173
174 // Tile placement uses translated world-space extents, but the
175 // underlying Zarr/OME pixels are still addressed on the tile grid.
176 // Derive the read origin from the tile index so translation does
177 // not introduce a fractional-pixel bias at the image edge.
178 var curLevelOffsetXPixel = tileInfo.Index.Col * tileWidth;
179 var curLevelOffsetYPixel = tileInfo.Index.Row * tileHeight;
180
181 // Use actual level pixel dimensions from BioImage.
182 int lev = tileInfo.Index.Level;
183 var levelPixelW = (long)(lev < SlideImage.BioImage.Resolutions.Count
184 ? SlideImage.BioImage.Resolutions[lev].SizeX
185 : Math.Round(Schema.Extent.Width / r));
186 var levelPixelH = (long)(lev < SlideImage.BioImage.Resolutions.Count
187 ? SlideImage.BioImage.Resolutions[lev].SizeY
188 : Math.Round(Schema.Extent.Height / r));
189
190 long readX = (long)curLevelOffsetXPixel;
191 long readY = (long)curLevelOffsetYPixel;
192
193 int curTileWidth = tileWidth;
194 int curTileHeight = tileHeight;
195
196 // Trim any part of the request that falls outside the image on
197 // the left/top instead of asking the Zarr reader to pad with black.
198 if (readX < 0)
199 {
200 curTileWidth += (int)readX;
201 readX = 0;
202 }
203 if (readY < 0)
204 {
205 curTileHeight += (int)readY;
206 readY = 0;
207 }
208
209 // Clamp tile read size so we never request pixels past the image boundary.
210 curTileWidth = (int)Math.Max(0, Math.Min(curTileWidth, levelPixelW - readX));
211 curTileHeight = (int)Math.Max(0, Math.Min(curTileHeight, levelPixelH - readY));
212
213 // Guard against fully OOB tiles
214 if (curTileWidth <= 0 || curTileHeight <= 0)
215 {
216 return new byte[tileWidth * tileHeight * 4]; // black padded tile
217 }
218
219 if (SlideImage == null)
220 {
221 Console.WriteLine("SlideImage is null.");
222 return null;
223 }
224
225 var bgraData = SlideImage.ReadRegion(
226 tileInfo.Index.Level, coord,
227 readX, readY,
228 curTileWidth, curTileHeight);
229
230 if (bgraData == null || bgraData.Length == 0)
231 return null;
232
233 int expectedSize = tileWidth * tileHeight * 4;
234
235 // Full tile — return as-is.
236 // Return the raw BGRA data (4 bytes/pixel) so the GL upload path
237 // receives a buffer that matches PixelFormat.Bgra / PixelType.UnsignedByte.
238 if (bgraData.Length == expectedSize)
239 return bgraData;
240
241 // Edge tile: ReadRegion returned fewer pixels than a full tile because
242 // this tile hangs off the image boundary. Pad into a full tileWidth x
243 // tileHeight RGBA buffer (rows outside the image stay transparent/zeroed)
244 // so the GL upload always receives a consistently-sized, correctly-strided
245 // buffer and never reads past the end.
246 int actualW = Math.Min(curTileWidth, tileWidth);
247 int actualH = Math.Min(curTileHeight, tileHeight);
248 int srcStride = actualW * 4;
249
250 // If ReadRegion returned even less than expected, derive safe row count
251 if (bgraData.Length < actualW * actualH * 4)
252 {
253 int safePixels = bgraData.Length / 4;
254 actualH = safePixels / actualW;
255 if (actualH == 0) return null;
256 }
257
258 byte[] padded = new byte[expectedSize]; // zero-initialised = transparent
259 for (int row = 0; row < actualH; row++)
260 {
261 int srcOffset = row * srcStride;
262 int dstOffset = row * tileWidth * 4;
263 Buffer.BlockCopy(bgraData, srcOffset, padded, dstOffset, srcStride);
264 }
265 return padded;
266 }
267 catch (Exception e)
268 {
269 Console.WriteLine(e.Message + " " + e.StackTrace);
270 }
271 return null;
272 }
List< Resolution > Resolutions
Definition Bio.cs:1979
unsafe byte[] ReadRegion(int level, ZCT zct, long x, long y, long width, long height)
Copy pre-multiplied BGRA data from a whole slide image.
Definition SlideImage.cs:282
BioImage BioImage
Definition SlideImage.cs:25

◆ InitResolutions()

void BioLib.SlideBase.InitResolutions ( IDictionary< int, BruTile.Resolution > resolutions,
int tileWidth,
int tileHeight )
protected
290 {
291 for (int i = 0; i < SlideImage.LevelCount; i++)
292 {
293 /*
294 bool useInternalWidth = int.TryParse(ExternInfo.TryGetValue($"openslide.level[{i}].tile-width", out var _w) ? (string)_w : null, out var w) && w >= tileWidth;
295 bool useInternalHeight = int.TryParse(ExternInfo.TryGetValue($"openslide.level[{i}].tile-height", out var _h) ? (string)_h : null, out var h) && h >= tileHeight;
296
297 bool useInternalSize = useInternalHeight && useInternalWidth;
298 var tw = useInternalSize ? w : tileWidth;
299 var th = useInternalSize ? h : tileHeight;
300 resolutions.Add(i, new Resolution(i, MinUnitsPerPixel * SlideImage.GetLevelDownsample(i), tw, th));
301 */
302 resolutions.Add(i, new BruTile.Resolution(i, SlideImage.BioImage.GetUnitPerPixel(i), tileWidth, tileHeight));
303 }
304 }
double GetUnitPerPixel(int level)
Get Unit Per Pixel for pyramidal images.
Definition Bio.cs:2328
int LevelCount
Get the number of levels in the whole slide image.
Definition SlideImage.cs:119

◆ RebuildSchemaForWell()

void BioLib.SlideBase.RebuildSchemaForWell ( BioImage b)
72 {
73 if (b.ZarrWellLevels == null || b.ZarrWellLevels.Count == 0)
74 {
75 Log($"[RebuildSchemaForWell] ZarrWellLevels empty — schema not rebuilt");
76 return;
77 }
78 int fi = Math.Clamp(b.WellIndex, 0, b.ZarrWellLevels.Count - 1);
79 var fieldLevels = b.ZarrWellLevels[fi];
80 if (fieldLevels == null || fieldLevels.Count == 0)
81 {
82 // Requested field not yet loaded — fall back to first non-null field
83 // so the schema matches what TryReadRegionAsync will actually serve.
84 fieldLevels = b.ZarrWellLevels.FirstOrDefault(l => l != null && l.Count > 0);
85 if (fieldLevels == null)
86 {
87 Log($"[RebuildSchemaForWell] no loaded fieldLevels, schema not rebuilt");
88 return;
89 }
90 Log($"[RebuildSchemaForWell] fi={fi} null, fell back to first loaded field");
91 }
92
93 int w0 = 1, h0 = 1;
94 GetFieldDims(fieldLevels[0], out w0, out h0);
95 Log($"[RebuildSchemaForWell] field fi={fi}, level0 w={w0} h={h0}, levels={fieldLevels.Count}");
96
97 double stageX = b.Resolutions.Count > 0 ? b.Resolutions[0].StageSizeX : 0;
98 double stageY = b.Resolutions.Count > 0 ? b.Resolutions[0].StageSizeY : 0;
99
100 var newSchema = new BruTile.TileSchema
101 {
102 YAxis = BruTile.YAxis.OSM,
103 Format = "jpg",
104 Extent = new BruTile.Extent(
105 stageX,
106 -(stageY + (h0 * b.PhysicalSizeY)),
107 stageX + (w0 * b.PhysicalSizeX),
108 -stageY),
109 OriginX = stageX,
110 OriginY = stageY,
111 };
112
113 for (int lev = 0; lev < fieldLevels.Count; lev++)
114 {
115 GetFieldDims(fieldLevels[lev], out int wL, out int hL);
116 double downsample = w0 > 0 ? (double)w0 / Math.Max(1, wL) : Math.Pow(2, lev);
117 newSchema.Resolutions[lev] = new BruTile.Resolution(lev, downsample, 256, 256);
118 Log($"[RebuildSchemaForWell] level {lev}: w={wL} h={hL} unitsPerPixel={downsample}");
119 }
120
121 Schema = newSchema;
122 }
return b
Definition Bio.cs:7393

Member Data Documentation

◆ SlideImage

readonly SlideImage BioLib.SlideBase.SlideImage

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