BioLib  3.9.1
A GUI-less version of Bio .NET library for editing & annotating various microscopy image formats.
Loading...
Searching...
No Matches
BioLib.ImageUtil Class Reference

Static Public Member Functions

static Image< L8 > Join8Bit (IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent)
 Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).
 
static Image< Rgb24 > JoinRGB24 (IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent)
 Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height and width is useful).
 
static Image< L16 > Join16 (IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent)
 Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).
 
static unsafe NetVips.Image JoinVipsRGB24 (IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent)
 Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).
 
static unsafe NetVips.Image JoinVips16 (IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent)
 Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).
 
static SixLabors.ImageSharp.Image CreateImageFromBytes (byte[] rgbBytes, int width, int height, AForge.PixelFormat px)
 

Member Function Documentation

◆ CreateImageFromBytes()

static SixLabors.ImageSharp.Image BioLib.ImageUtil.CreateImageFromBytes ( byte[] rgbBytes,
int width,
int height,
AForge.PixelFormat px )
static
514 {
515 if (px == AForge.PixelFormat.Format24bppRgb)
516 {
517 if (rgbBytes.Length != width * height * 3)
518 {
519 throw new ArgumentException("Byte array size does not match the dimensions of the image");
520 }
521
522 // Create a new image of the specified size
523 Image<Rgb24> image = new Image<Rgb24>(width, height);
524
525 // Index for the byte array
526 int byteIndex = 0;
527
528 // Iterate over the image pixels
529 for (int y = 0; y < height; y++)
530 {
531 for (int x = 0; x < width; x++)
532 {
533 // Create a color from the next three bytes
534 Rgb24 color = new Rgb24(rgbBytes[byteIndex], rgbBytes[byteIndex + 1], rgbBytes[byteIndex + 2]);
535 byteIndex += 3;
536 // Set the pixel
537 image[x, y] = color;
538 }
539 }
540
541 return image;
542 }
543 else
544 if (px == AForge.PixelFormat.Format16bppGrayScale)
545 {
546 if (rgbBytes.Length != width * height * 2)
547 {
548 throw new ArgumentException("Byte array size does not match the dimensions of the image");
549 }
550
551 // Create a new image of the specified size
552 Image<L16> image = new Image<L16>(width, height);
553
554 // Index for the byte array
555 int byteIndex = 0;
556
557 // Iterate over the image pixels
558 for (int y = 0; y < height; y++)
559 {
560 for (int x = 0; x < width; x++)
561 {
562 // Create a color from the next three bytes
563 L16 color = new L16(BitConverter.ToUInt16(rgbBytes, byteIndex));
564 byteIndex += 2;
565 // Set the pixel
566 image[x, y] = color;
567 }
568 }
569
570 return image;
571 }
572 else
573 if (px == AForge.PixelFormat.Format8bppIndexed)
574 {
575 if (rgbBytes.Length != width * height)
576 {
577 throw new ArgumentException("Byte array size does not match the dimensions of the image");
578 }
579
580 // Create a new image of the specified size
581 Image<L8> image = new Image<L8>(width, height);
582
583 // Index for the byte array
584 int byteIndex = 0;
585
586 // Iterate over the image pixels
587 for (int y = 0; y < height; y++)
588 {
589 for (int x = 0; x < width; x++)
590 {
591 // Create a color from the next three bytes
592 L8 color = new L8(rgbBytes[byteIndex]);
593 byteIndex++;
594 // Set the pixel
595 image[x, y] = color;
596 }
597 }
598
599 return image;
600 }
601 else
602 if (px == AForge.PixelFormat.Format32bppArgb)
603 {
604 if (rgbBytes.Length != width * height * 4)
605 {
606 throw new ArgumentException("Byte array size does not match the dimensions of the image");
607 }
608
609 // Create a new image of the specified size
610 Image<Bgra32> image = new Image<Bgra32>(width, height);
611
612 // Index for the byte array
613 int byteIndex = 0;
614
615 // Iterate over the image pixels
616 for (int y = 0; y < height; y++)
617 {
618 for (int x = 0; x < width; x++)
619 {
620 // Create a color from the next three bytes
621 Bgra32 color = new Bgra32(rgbBytes[byteIndex], rgbBytes[byteIndex + 1], rgbBytes[byteIndex + 2], rgbBytes[byteIndex + 3]);
622 byteIndex += 4;
623 // Set the pixel
624 image[x, y] = color;
625 }
626 }
627
628 return image;
629 }
630 return null;
631 }

◆ Join16()

static Image< L16 > BioLib.ImageUtil.Join16 ( IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles,
Extent srcPixelExtent,
Extent dstPixelExtent )
static

Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).

Parameters
srcPixelTilestile with tile extent collection
srcPixelExtentcanvas extent
dstPixelExtentjpeg output size
Returns
333 {
334 if (srcPixelTiles == null || srcPixelTiles.Count() == 0)
335 return null;
336 srcPixelExtent = srcPixelExtent.ToIntegerExtent();
337 dstPixelExtent = dstPixelExtent.ToIntegerExtent();
338 int canvasWidth = (int)srcPixelExtent.Width;
339 int canvasHeight = (int)srcPixelExtent.Height;
340 var dstWidth = (int)dstPixelExtent.Width;
341 var dstHeight = (int)dstPixelExtent.Height;
342 Image<L16> canvas = new Image<L16>(canvasWidth, canvasHeight);
343 foreach (var tile in srcPixelTiles)
344 {
345 try
346 {
347 var tileExtent = tile.Item1.ToIntegerExtent();
348 var intersect = srcPixelExtent.Intersect(tileExtent);
349 if (intersect.Width == 0 || intersect.Height == 0)
350 continue;
351 if (tile.Item2 == null)
352 continue;
353 Image<L16> tileRawData = (Image<L16>)CreateImageFromBytes(tile.Item2, (int)tileExtent.Width, (int)tileExtent.Height, AForge.PixelFormat.Format16bppGrayScale);
354 var tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX);
355 var tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY);
356 var canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX);
357 var canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY);
358 //We copy the tile region to the canvas.
359 for (int y = 0; y < intersect.Height; y++)
360 {
361 for (int x = 0; x < intersect.Width; x++)
362 {
363 int indx = canvasOffsetPixelX + x;
364 int indy = canvasOffsetPixelY + y;
365 int tindx = tileOffsetPixelX + x;
366 int tindy = tileOffsetPixelY + y;
367 canvas[indx, indy] = tileRawData[tindx, tindy];
368 }
369 }
370 tileRawData.Dispose();
371 }
372 catch (Exception e)
373 {
374 Console.WriteLine(e.ToString());
375 }
376
377 }
378 if (dstWidth != canvasWidth || dstHeight != canvasHeight)
379 {
380 try
381 {
382 canvas.Mutate(x => x.Resize(dstWidth, dstHeight));
383 return canvas;
384 }
385 catch (Exception e)
386 {
387 Console.WriteLine(e.Message);
388 }
389
390 }
391 return canvas;
392 }

◆ Join8Bit()

static Image< L8 > BioLib.ImageUtil.Join8Bit ( IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles,
Extent srcPixelExtent,
Extent dstPixelExtent )
static

Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).

Parameters
srcPixelTilestile with tile extent collection
srcPixelExtentcanvas extent
dstPixelExtentjpeg output size
Returns
25 {
26 if (srcPixelTiles == null || !srcPixelTiles.Any())
27 return null;
28
29 // Convert extents to integer extents
30 srcPixelExtent = srcPixelExtent.ToIntegerExtent();
31 dstPixelExtent = dstPixelExtent.ToIntegerExtent();
32
33 int canvasWidth = (int)srcPixelExtent.Width;
34 int canvasHeight = (int)srcPixelExtent.Height;
35 int dstWidth = (int)dstPixelExtent.Width;
36 int dstHeight = (int)dstPixelExtent.Height;
37
38 // Create the canvas image
39 var canvas = new Image<L8>(canvasWidth, canvasHeight);
40
41 foreach (var tile in srcPixelTiles)
42 {
43 try
44 {
45 if (tile?.Item2 == null) // Skip null tiles
46 continue;
47
48 var tileExtent = tile.Item1.ToIntegerExtent();
49 var intersect = srcPixelExtent.Intersect(tileExtent);
50
51 // Skip tiles that do not intersect with the source extent
52 if (intersect.Width == 0 || intersect.Height == 0)
53 continue;
54
55 // Create an image from the tile data
56 using Image<L8> tileRawData = (Image<L8>)CreateImageFromBytes(
57 tile.Item2,
58 (int)tileExtent.Width,
59 (int)tileExtent.Height,
60 AForge.PixelFormat.Format8bppIndexed
61 );
62
63 // Compute offsets
64 int tileOffsetX = (int)(intersect.MinX - tileExtent.MinX);
65 int tileOffsetY = (int)(intersect.MinY - tileExtent.MinY);
66 int canvasOffsetX = (int)(intersect.MinX - srcPixelExtent.MinX);
67 int canvasOffsetY = (int)(intersect.MinY - srcPixelExtent.MinY);
68
69 // Copy intersected region from tile to canvas
70 for (int y = 0; y < intersect.Height; y++)
71 {
72 for (int x = 0; x < intersect.Width; x++)
73 {
74 int canvasX = canvasOffsetX + x;
75 int canvasY = canvasOffsetY + y;
76 int tileX = tileOffsetX + x;
77 int tileY = tileOffsetY + y;
78
79 // Use the older approach to manipulate pixel data
80 canvas[canvasX, canvasY] = tileRawData[tileX, tileY];
81 }
82 }
83 }
84 catch (Exception ex)
85 {
86 Console.WriteLine($"Error processing tile: {ex.Message}\n{ex.StackTrace}");
87 }
88 }
89
90 // Resize if necessary
91 if (dstWidth != canvasWidth || dstHeight != canvasHeight)
92 {
93 try
94 {
95 canvas.Mutate(x => x.Resize(dstWidth, dstHeight));
96 }
97 catch (Exception ex)
98 {
99 Console.WriteLine($"Error resizing canvas: {ex.Message}\n{ex.StackTrace}");
100 return null;
101 }
102 }
103
104 return canvas; // Return the canvas image
105 }

◆ JoinRGB24()

static Image< Rgb24 > BioLib.ImageUtil.JoinRGB24 ( IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles,
Extent srcPixelExtent,
Extent dstPixelExtent )
static

Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height and width is useful).

Parameters
srcPixelTilestile with tile extent collection
srcPixelExtentcanvas extent
dstPixelExtentjpeg output size
Returns
115 {
116 if (srcPixelTiles == null || !srcPixelTiles.Any())
117 return null;
118
119 try
120 {
121 // Safely convert to integer extents
122 srcPixelExtent = SafeToIntegerExtent(srcPixelExtent);
123 dstPixelExtent = SafeToIntegerExtent(dstPixelExtent);
124
125 int canvasWidth = (int)srcPixelExtent.Width;
126 int canvasHeight = (int)srcPixelExtent.Height;
127 int dstWidth = (int)dstPixelExtent.Width;
128 int dstHeight = (int)dstPixelExtent.Height;
129
130 // Validate dimensions
131 if (canvasWidth <= 0 || canvasHeight <= 0)
132 {
133 Console.WriteLine($"Invalid canvas dimensions: {canvasWidth}x{canvasHeight}");
134 return null;
135 }
136
137 Image<Rgb24> canvas = new Image<Rgb24>(canvasWidth, canvasHeight);
138
139 foreach (var tile in srcPixelTiles)
140 {
141 try
142 {
143 // Skip invalid tiles
144 if (tile?.Item2 == null || tile.Item2.Length == 0)
145 continue;
146
147 // Safely convert tile extent
148 var tileExtent = SafeToIntegerExtent(tile.Item1);
149
150 // Validate tile extent
151 if (tileExtent.Width <= 0 || tileExtent.Height <= 0)
152 continue;
153
154 // Calculate intersection manually to avoid errors
155 var intersect = SafeIntersect(srcPixelExtent, tileExtent);
156
157 // Skip if no intersection
158 if (intersect == null || intersect.Value.Width <= 0 || intersect.Value.Height <= 0)
159 continue;
160
161 // Create image from tile data
162 Image<Rgb24> tileRawData = null;
163 try
164 {
165 tileRawData = (Image<Rgb24>)CreateImageFromBytes(
166 tile.Item2,
167 (int)tileExtent.Width,
168 (int)tileExtent.Height,
169 AForge.PixelFormat.Format24bppRgb
170 );
171 }
172 catch (Exception ex)
173 {
174 Console.WriteLine($"Failed to create image from tile data: {ex.Message}");
175 continue;
176 }
177
178 if (tileRawData == null)
179 continue;
180
181 // Calculate offsets (using Floor instead of Ceiling for better precision)
182 int tileOffsetPixelX = (int)Math.Max(0, intersect.Value.MinX - tileExtent.MinX);
183 int tileOffsetPixelY = (int)Math.Max(0, intersect.Value.MinY - tileExtent.MinY);
184 int canvasOffsetPixelX = (int)Math.Max(0, intersect.Value.MinX - srcPixelExtent.MinX);
185 int canvasOffsetPixelY = (int)Math.Max(0, intersect.Value.MinY - srcPixelExtent.MinY);
186
187 // Calculate safe copy dimensions
188 int copyWidth = (int)Math.Min(
189 Math.Min(intersect.Value.Width, tileRawData.Width - tileOffsetPixelX),
190 canvas.Width - canvasOffsetPixelX
191 );
192
193 int copyHeight = (int)Math.Min(
194 Math.Min(intersect.Value.Height, tileRawData.Height - tileOffsetPixelY),
195 canvas.Height - canvasOffsetPixelY
196 );
197
198 // Validate copy dimensions
199 if (copyWidth <= 0 || copyHeight <= 0)
200 {
201 tileRawData.Dispose();
202 continue;
203 }
204
205 // Copy the tile region to the canvas with bounds checking
206 for (int y = 0; y < copyHeight; y++)
207 {
208 int canvasY = canvasOffsetPixelY + y;
209 int tileY = tileOffsetPixelY + y;
210
211 // Safety check
212 if (canvasY < 0 || canvasY >= canvas.Height ||
213 tileY < 0 || tileY >= tileRawData.Height)
214 break;
215
216 for (int x = 0; x < copyWidth; x++)
217 {
218 int canvasX = canvasOffsetPixelX + x;
219 int tileX = tileOffsetPixelX + x;
220
221 // Safety check
222 if (canvasX < 0 || canvasX >= canvas.Width ||
223 tileX < 0 || tileX >= tileRawData.Width)
224 break;
225
226 canvas[canvasX, canvasY] = tileRawData[tileX, tileY];
227 }
228 }
229
230 tileRawData.Dispose();
231 }
232 catch (Exception e)
233 {
234 Console.WriteLine($"Error processing tile: {e.Message}");
235 }
236 }
237
238 // Resize if needed
239 if (dstWidth > 0 && dstHeight > 0 &&
240 (dstWidth != canvasWidth || dstHeight != canvasHeight))
241 {
242 try
243 {
244 canvas.Mutate(x => x.Resize(dstWidth, dstHeight));
245 }
246 catch (Exception e)
247 {
248 Console.WriteLine($"Error resizing canvas: {e.Message}");
249 }
250 }
251
252 return canvas;
253 }
254 catch (Exception e)
255 {
256 Console.WriteLine($"Fatal error in JoinRGB24: {e.Message}\n{e.StackTrace}");
257 return null;
258 }
259 }

◆ JoinVips16()

static unsafe NetVips.Image BioLib.ImageUtil.JoinVips16 ( IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles,
Extent srcPixelExtent,
Extent dstPixelExtent )
static

Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).

Parameters
srcPixelTilestile with tile extent collection
srcPixelExtentcanvas extent
dstPixelExtentjpeg output size
Returns
461 {
462 if (srcPixelTiles == null || !srcPixelTiles.Any())
463 return null;
464
465 srcPixelExtent = srcPixelExtent.ToIntegerExtent();
466 dstPixelExtent = dstPixelExtent.ToIntegerExtent();
467 int canvasWidth = (int)srcPixelExtent.Width;
468 int canvasHeight = (int)srcPixelExtent.Height;
469
470 // Create a base canvas. Adjust as necessary, for example, using a transparent image if needed.
471 Bitmap bf = new Bitmap(canvasWidth, canvasHeight, AForge.PixelFormat.Format16bppGrayScale);
472 NetVips.Image canvas = NetVips.Image.NewFromMemory(bf.Bytes, bf.SizeX, bf.SizeX, 1, Enums.BandFormat.Ushort);
473
474 foreach (var tile in srcPixelTiles)
475 {
476 if (tile.Item2 == null)
477 continue;
478
479 fixed (byte* pTileData = tile.Item2)
480 {
481 var tileExtent = tile.Item1.ToIntegerExtent();
482 NetVips.Image tileImage = NetVips.Image.NewFromMemory((IntPtr)pTileData, (ulong)tile.Item2.Length, (int)tileExtent.Width, (int)tileExtent.Height, 1, Enums.BandFormat.Ushort);
483
484 // Calculate positions and sizes for cropping and inserting
485 var intersect = srcPixelExtent.Intersect(tileExtent);
486 if (intersect.Width == 0 || intersect.Height == 0)
487 continue;
488
489 int tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX);
490 int tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY);
491 int canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX);
492 int canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY);
493
494 using (var croppedTile = tileImage.Crop(tileOffsetPixelX, tileOffsetPixelY, (int)intersect.Width, (int)intersect.Height))
495 {
496 // Instead of inserting directly, we composite over the base canvas
497 canvas = canvas.Composite2(croppedTile, Enums.BlendMode.Over, canvasOffsetPixelX, canvasOffsetPixelY);
498 }
499 }
500 }
501
502 // Resize if the destination extent differs from the source canvas size
503 if ((int)dstPixelExtent.Width != canvasWidth || (int)dstPixelExtent.Height != canvasHeight)
504 {
505 double scaleX = (double)dstPixelExtent.Width / canvasWidth;
506 double scaleY = (double)dstPixelExtent.Height / canvasHeight;
507 canvas = canvas.Resize(scaleX, vscale: scaleY, kernel: Enums.Kernel.Nearest);
508 }
509
510 return canvas;
511 }

◆ JoinVipsRGB24()

static unsafe NetVips.Image BioLib.ImageUtil.JoinVipsRGB24 ( IEnumerable< Tuple< Extent, byte[]> > srcPixelTiles,
Extent srcPixelExtent,
Extent dstPixelExtent )
static

Join by srcPixelTiles and cut by srcPixelExtent then scale to dstPixelExtent (only height an width is useful).

Parameters
srcPixelTilestile with tile extent collection
srcPixelExtentcanvas extent
dstPixelExtentjpeg output size
Returns
402 {
403 if (srcPixelTiles == null || !srcPixelTiles.Any())
404 return null;
405
406 srcPixelExtent = srcPixelExtent.ToIntegerExtent();
407 dstPixelExtent = dstPixelExtent.ToIntegerExtent();
408 int canvasWidth = (int)srcPixelExtent.Width;
409 int canvasHeight = (int)srcPixelExtent.Height;
410
411 // Create a base canvas. Adjust as necessary, for example, using a transparent image if needed.
412 NetVips.Image canvas = NetVips.Image.Black(canvasWidth, canvasHeight, bands: 3);
413
414 foreach (var tile in srcPixelTiles)
415 {
416 if (tile.Item2 == null)
417 continue;
418
419 fixed (byte* pTileData = tile.Item2)
420 {
421 var tileExtent = tile.Item1.ToIntegerExtent();
422 NetVips.Image tileImage = NetVips.Image.NewFromMemory((IntPtr)pTileData, (ulong)tile.Item2.Length, (int)tileExtent.Width, (int)tileExtent.Height, 3, Enums.BandFormat.Uchar);
423
424 // Calculate positions and sizes for cropping and inserting
425 var intersect = srcPixelExtent.Intersect(tileExtent);
426 if (intersect.Width == 0 || intersect.Height == 0)
427 continue;
428
429 int tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX);
430 int tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY);
431 int canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX);
432 int canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY);
433
434 using (var croppedTile = tileImage.Crop(tileOffsetPixelX, tileOffsetPixelY, (int)intersect.Width, (int)intersect.Height))
435 {
436 // Instead of inserting directly, we composite over the base canvas
437 canvas = canvas.Composite2(croppedTile, Enums.BlendMode.Over, canvasOffsetPixelX, canvasOffsetPixelY);
438 }
439 }
440 }
441
442 // Resize if the destination extent differs from the source canvas size
443 if ((int)dstPixelExtent.Width != canvasWidth || (int)dstPixelExtent.Height != canvasHeight)
444 {
445 double scaleX = (double)dstPixelExtent.Width / canvasWidth;
446 double scaleY = (double)dstPixelExtent.Height / canvasHeight;
447 canvas = canvas.Resize(scaleX, vscale: scaleY, kernel: Enums.Kernel.Nearest);
448 }
449
450 return canvas;
451 }

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