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

Classes

class  BioLib.Images
 
struct  BioLib.Resolution
 
class  BioLib.Cell
 
class  BioLib.Filt
 
class  BioLib.Filters
 
class  BioLib.ImageInfo
 
class  BioLib.NumPy
 
class  BioLib.NumPy.NpyHeader
 
class  BioLib.ArrayExtensions
 
class  BioLib.BioImage
 
class  BioLib.BioImage.WellPlate
 
class  BioLib.BioImage.WellPlate.Well
 
class  BioLib.BioImage.WellPlate.Well.Sample
 
class  BioLib.BioImage.ImageJDesc
 

Namespaces

namespace  BioLib
 

Typedefs

using Bitmap = AForge.Bitmap
 
using Channel = AForge.Channel
 
using Color = AForge.Color
 
using Context = Cairo.Context
 
using Path = System.IO.Path
 
using PixelFormat = AForge.PixelFormat
 
using Point = AForge.Point
 
using PointD = AForge.PointD
 
using Rectangle = AForge.Rectangle
 
using ZCT = AForge.ZCT
 

Enumerations

enum  BackEnd { OpenSlide , Bioformats , LibVips }
 

Functions

 [instance initializer]
 
static BioImage[] OpenSeries (string file, bool tab)
 It opens a tiff file, reads the number of pages, reads the number of channels, and then reads each page into a BioImage object.
 
static async Task< BioImage > OpenFile (string file, ZCT coord=new ZCT())
 This function opens a file and returns a BioImage object.
 
static async Task< BioImage > OpenFile (string file, bool tab)
 
static async Task< BioImage > OpenFileAsync (string file, int series, bool tab, bool addToImages)
 It opens a TIFF file and returns a BioImage object.
 
static bool IsTiffTiled (string imagePath)
 
static void InitDirectoryResolution (BioImage b, Tiff image, ImageJDesc jdesc=null)
 The function InitDirectoryResolution initializes the resolution properties of a BioImage object based on the resolution information stored in a TIFF image.
 
static async Task< BioImage > OpenFile (string file, int series, bool tab, bool addToImages, bool tile, int tileX, int tileY, int tileSizeX=800, int tileSizeY=600)
 The OpenFile function opens a BioImage file, reads its metadata, and loads the image data into a BioImage object.
 
static async Task< BioImage > OpenZarrAsync (string source, int multiscaleIndex=0, int datasetIndex=0, string? id=null, CancellationToken ct=default)
 Opens an OME-Zarr store and converts it into a BioImage. This is the direct entry point for callers that want a BioLib image instead of working with ZarrNET nodes.
 
static async Task< BioImage > OpenURL (string url, ZCT coord, int originX, int originY, int tileWidth, int tileHeight)
 
static AForge.Bitmap CreateAForgeBitmap (int width, int height, PixelFormat px, byte[] data, ZCT coord, bool littleEndian, bool interleaved)
 
static bool isTiffSeries (string file)
 
static bool isOME (string file)
 If the file is a TIFF, check the ImageDescription tag for the string "OME-XML". If it's there, return true. If it's not a TIFF, return true. If it's a PNG, JPG, JPEG, or BMP, return false.
 
static bool isOMESeries (string file)
 
static void SaveOME (string file, string ID)
 
static void SaveOME (BioImage image, string file)
 
static ROI[] SortByType (ROI[] annotations)
 Sorts an array of ROIs by their Type property (alphabetically by default).
 
static void SaveOMESeries (BioImage[] files, string f, bool planes)
 This function takes a list of image files and saves them as a single OME-TIFF file.
 
static void SaveZarr (BioImage b, string outputFile)
 
static async Task SaveOMEZarr (BioImage[] bms, string outputDir)
 Saves already-processed pyramid levels as an OME-Zarr multiscale dataset.
 
static int GetBands (PixelFormat format)
 The function "GetBands" returns the number of color bands for a given pixel format in the AForge library.
 
static void SaveOMEPyramidal (BioImage[] bms, string file, Enums.ForeignTiffCompression compression, int compressionLevel)
 The function SaveOMEPyramidal saves a collection of BioImages as a pyramidal OME-TIFF file.
 
static BioImage OpenOME (string file, bool tab)
 The function "OpenOME" opens a bioimage file in the OME format and returns the first image in the series.
 
static BioImage OpenOME (string file, int serie)
 
static BioImage FromNumpy (string file)
 Converts a numpy array to BioImage.
 
static BioImage FilesToStack (string[] files, int sizeZ, int sizeC, int sizeT)
 It takes a list of files, and creates a new BioImage object with the first file in the list. Then it loops through the rest of the files, adding the buffers from each file to the new BioImage object. Finally, it updates the coordinates of the new BioImage object, and adds it to the Images list.
 
static BioImage FolderToStack (string path, bool tab)
 It takes a folder of images and creates a stack from them.
 
static void OpenVips (BioImage b)
 The function "OpenVips" takes a BioImage object and an integer representing the number of pages, and adds each page of the image file to the BioImage's vipPages list using the NetVips library.
 
static Bitmap ExtractRegionFromTiledTiff (BioImage b, int x, int y, int width, int height, int level)
 The function ExtractRegionFromTiledTiff takes a BioImage object, coordinates, width, height, and resolution as input, and returns a Bitmap object representing the extracted region from the tiled TIFF image.
 
static BioImage OpenOME (string file, int serie, bool tab, bool addToImages, bool tile, int tilex, int tiley, int tileSizeX, int tileSizeY, bool useOpenSlide=true)
 
int GetFrameIndex (int z, int c, int t)
 
void SetFrameIndex (int z, int c, int t, int val)
 
async Task< Bitmap > GetWellFieldBitmap (int fieldIndex, int z=0, int c=0, int t=0)
 
async Task< Bitmap > GetTile (int index, int level, int tilex, int tiley, int tileSizeX, int tileSizeY)
 
async Task< Bitmap > GetTile (int index, int level, int tilex, int tiley, int tileSizeX, int tileSizeY, AForge.ZCT? coordinateOverride, bool returnRaw=false)
 
async Task< byte[]> GetTileBytesRaw (int index, int level, int tilex, int tiley, int tileSizeX, int tileSizeY, AForge.ZCT? coordinateOverride=null)
 
BioImage GetRegion (int x, int y, int w, int h)
 
void StackThreshold (bool bit16)
 This function sets the minimum and maximum values of the image to the minimum and maximum values of the stack.
 
static int GetBitsPerPixel (int bt)
 
static int GetBitMaxValue (int bt)
 It returns the maximum value of a bit.
 
static PointD[] ToImageSpace (List< PointD > p, double stageSizeX, double stageSizeY, double physicalSizeX, double physicalSizeY)
 The function takes a list of points in stage space and converts them to image space using the provided stage and physical size parameters.
 
static PixelFormat GetPixelFormat (int rgbChannelCount, int bitsPerPixel)
 If the bits per pixel is 8, then the pixel format is either 8bppIndexed, 24bppRgb, or 32bppArgb. If the bits per pixel is 16, then the pixel format is either 16bppGrayScale or 48bppRgb.
 
static PixelFormat GetPixelFormat (int rgbChannelCount, ome.xml.model.enums.PixelType px)
 The function returns the appropriate PixelFormat based on the number of RGB channels and the pixel type.
 
static BioImage[] OpenOMESeries (string file, bool tab, bool addToImages)
 It opens a file, checks if it's tiled, if it is, it opens it as a tiled image, if not, it opens it as a normal image.
 
static async Task OpenAsync (string file, bool OME, bool newtab, bool images, int series)
 It opens a file in a new thread.
 
static async Task OpenAsync (string[] files, bool OME, bool tab, bool images)
 It opens a file asynchronously.
 
static void Open (string file)
 
static void Open (string file, int z=0, int c=0, int t=0)
 It opens a file.
 
static void Open (string[] files, ZCT coord)
 It opens a file.
 
static BioImage ImagesToStack (string[] files, bool tab)
 It takes a list of files, opens them, and then combines them into a single BioImage object.
 
static void Update (BioImage b)
 The function takes a BioImage object, opens the file, and returns a updated BioImage object.
 
async Task UpdateBuffersPyramidal (int level)
 Updates the Buffers based on current pyramidal origin and resolution.
 
static SKImage Convert16bppBitmapToSKImage (Bitmap sourceBitmap, int rMin=0, int rMax=ushort.MaxValue, int gMin=0, int gMax=ushort.MaxValue, int bMin=0, int bMax=ushort.MaxValue)
 Converts a 16-bit (gray or 48bpp RGB) bitmap to an SKImage, mapping the supplied per-channel min/max values to the 0-255 display range. This is the correct path for microscopy data which rarely uses the full 0-65535 range (e.g. 12-bit cameras top out at 4095).
 
static SKImage BitmapToSKImage (AForge.Bitmap bitm, int rMin=0, int rMax=ushort.MaxValue, int gMin=0, int gMax=ushort.MaxValue, int bMin=0, int bMax=ushort.MaxValue)
 
void UpdateBuffersWells ()
 
void Update ()
 
void Rename (string name)
 
static void OpenThread ()
 
static void SaveThread ()
 
static async Task SaveAsync (string file, string id, int serie, bool ome)
 The SaveAsync function saves data to a file asynchronously.
 
static void SaveSeriesThread ()
 
static async Task SaveSeriesAsync (BioImage[] imgs, string file, bool ome)
 The function SaveSeriesAsync saves a series of BioImage objects to a file asynchronously.
 
static void SavePyramidalThread ()
 
static async Task SavePyramidalAsync (BioImage[] imgs, string file, Enums.ForeignTiffCompression com, int compLevel)
 The function SavePyramidalAsync saves an array of BioImage objects as a pyramidal TIFF file asynchronously.
 
static bool VipsSupport (string file)
 The function checks if a given file is supported by the Vips library and returns true if it is, false otherwise.
 
static string OpenXML (string file)
 
static List< ROI > OpenOMEROIs (string file, int series)
 It reads the OME-XML file and converts the ROIs to a list of ROI objects.
 
static string ROIsToString (List< ROI > Annotations)
 It takes a list of ROI objects and returns a string of all the ROI objects in the list.
 
static string ROIToString (ROI an)
 This function takes an ROI object and returns a string that contains all the information about the ROI.
 
static ROI StringToROI (string sts)
 It takes a string and returns an ROI object.
 
static void ExportROIsCSV (string filename, List< ROI > Annotations)
 This function takes a list of ROIs and writes them to a CSV file.
 
static List< ROI > ImportROIsCSV (string filename)
 It reads the CSV file and converts each line into a ROI object.
 
static void ExportROIFolder (string path, string filename)
 ExportROIFolder(path, filename)
 
static void AutoThreshold (BioImage b, bool updateImageStats=true)
 It takes a BioImage object, and calculates the mean histogram for each channel, and for the entire image.
 
static void AutoThreshold ()
 It takes the current image, and finds the best threshold value for it.
 
static void AutoThresholdThread (BioImage b)
 It creates a new thread that calls the AutoThreshold function.
 
static int FindFocus (BioImage im, int Channel, int Time)
 The function finds the focus of a given BioImage at a specific channel and time by calculating the focus quality of each Z-plane and returning the coordinate with the highest focus quality.
 
static long CalculateFocusQuality (Bitmap b)
 The function calculates the focus quality of a given bitmap image.
 
void Dispose ()
 It disposes of all the buffers and channels in the image, removes the image from the Images list, and then forces the garbage collector to run.
 
override string ToString ()
 This function returns the filename of the object, and the location of the object in the 3D space.
 
static int GetSeriesCount (string file)
 
static BioImage operator/ (BioImage a, float b)
 This function divides each pixel in the image by a constant value.
 
static BioImage operator* (BioImage a, float b)
 
static BioImage operator+ (BioImage a, float b)
 This function adds a constant value to each pixel in the image.
 
static BioImage operator- (BioImage a, float b)
 Subtracts a scalar value from each pixel in the image.
 
static BioImage operator/ (BioImage a, ColorS b)
 This function divides each pixel in the image by the value of the color.
 
static BioImage operator* (BioImage a, ColorS b)
 This function takes a BioImage object and a ColorS object and returns a BioImage object.
 
static BioImage operator+ (BioImage a, ColorS b)
 It takes a BioImage object and a ColorS object and adds the ColorS object to each buffer in the BioImage object.
 
static BioImage operator- (BioImage a, ColorS b)
 The function subtracts a color from each pixel in the image.
 

Variables

alias FijiNet
 
OmeZarrReader zarrReader
 
MultiscaleNode multiscale
 
MultiscaleNode imagef
 
ResolutionLevelNode levelf
 
List< ResolutionLevelNode > levels
 
PlateMetadata zarrPlate
 
List< List< ResolutionLevelNode > > ZarrWellLevels = new()
 For HCS Plate Zarr stores: one entry per well-field, each containing the resolution levels for that field. Indexed by WellPlate.Well.Sample.Index.
 
int WellIndex [get, set]
 Gets or sets the currently active well-field index for HCS OME-Zarr plates. This is the zero-based index into ZarrWellLevels that identifies which well-field is displayed and fetched by GetTile and GetWellFieldBitmap.
 
int ZarrDisplayMax [get, set]
 
ushort ZarrDisplayMin [get, set]
 
List< PlaneResult > planes = new List<PlaneResult>()
 
BioImage. Planes
 
static Random rng = new Random()
 
return b
 
static bool vips = true
 
ImageReader imRead
 
Tiff tifRead
 
OpenSlideImage openSlideImage
 
static Bitmap bm
 
static bool useVips = true
 
static bool useGPU = true
 
static string openfile
 
static bool omes
 
static bool tab
 
static bool add
 
static int serie
 
static string savefile
 
static string saveid
 
static bool some
 
static int sserie
 
static List< string > sts = new List<string>()
 
static Enums.ForeignTiffCompression comp
 
static int compLev = 0
 
static BioImage[] bms
 
static NetVips.Image netim
 
const char NewLine = '\n'
 
const string columns = "ROIID,ROINAME,TYPE,ID,SHAPEINDEX,TEXT,S,C,Z,T,X,Y,W,H,POINTS,STROKECOLOR,STROKECOLORW,FILLCOLOR,FONTSIZE\n"
 

Typedef Documentation

◆ Bitmap

typedef AForge Bitmap Bitmap = AForge.Bitmap

◆ Channel

using Channel = AForge.Channel

◆ Color

typedef AForge Color Color = AForge.Color

◆ Context

using Context = Cairo.Context

◆ Path

using Path = System.IO.Path

◆ PixelFormat

using PixelFormat = AForge.PixelFormat

◆ Point

using Point = AForge.Point

◆ PointD

using PointD = AForge.PointD

◆ Rectangle

using Rectangle = AForge.Rectangle

◆ ZCT

using ZCT = AForge.ZCT

Enumeration Type Documentation

◆ BackEnd

enum BackEnd
Enumerator
OpenSlide 
Bioformats 
LibVips 
8526 {
8527 OpenSlide,
8528 Bioformats,
8529 LibVips,
8530 }
@ Bioformats

Function Documentation

◆ [instance initializer]()

[instance initializer]
5104{{ {string.Join(", ", IDs.Select(id => $"\"{id}\""))} }}, \"{file}\");");

◆ AutoThreshold() [1/2]

static void AutoThreshold ( )
static

It takes the current image, and finds the best threshold value for it.

11624 {
11625 AutoThreshold(bstats, update);
11626 Recorder.Record("BioImage.AutoThreshold();");
11627 }
static void AutoThreshold()
It takes the current image, and finds the best threshold value for it.
Definition Bio.cs:11623
Definition Recorder.cs:12
static void Record(MethodInfo inf, bool isInternal, params object[] args)
Definition Recorder.cs:20

◆ AutoThreshold() [2/2]

static void AutoThreshold ( BioImage b,
bool updateImageStats = true )
static

It takes a BioImage object, and calculates the mean histogram for each channel, and for the entire image.

The BioImage object is a class that contains a list of buffers, each of which contains a byte array of pixel data.

The function also calculates the mean histogram for each buffer, and stores it in the buffer's stats property.

The function also calculates the mean histogram for each channel, and stores it in the channel's stats property.

The function also calculates the mean histogram for the entire image, and stores it in the image's statistics property.

The mean histogram is calculated by adding up the histograms of each buffer, and then dividing by the number of buffers.

The histogram is calculated by looping through the byte array of pixel data, and incrementing the value of the histogram at the index of the pixel value.

The histogram

Parameters
BioImageThis is the image object that contains the image data.
updateImageStatsif true, the image stats will be updated.
11545 {
11546 bstats = b;
11547 Statistics statistics = null;
11548 if (b.bitsPerPixel > 8)
11549 statistics = new Statistics(true);
11550 else
11551 statistics = new Statistics(false);
11552 for (int i = 0; i < b.Buffers.Count; i++)
11553 {
11554 if(b.Buffers[i] == null)
11555 { continue; }
11556 if (b.Buffers[i].Stats == null || updateImageStats)
11557 b.Buffers[i].Stats = Statistics.FromBytes(b.Buffers[i]);
11558 if (b.Buffers[i].RGBChannelsCount == 1)
11559 statistics.AddStatistics(b.Buffers[i].Stats[0]);
11560 else
11561 {
11562 for (int r = 0; r < b.Buffers[i].RGBChannelsCount; r++)
11563 {
11564 statistics.AddStatistics(b.Buffers[i].Stats[r]);
11565 }
11566 }
11567 }
11568 for (int c = 0; c < b.Channels.Count; c++)
11569 {
11570 if (b.Buffers[0] == null)
11571 { continue; }
11572 Statistics[] sts = new Statistics[b.Buffers[0].RGBChannelsCount];
11573 for (int i = 0; i < b.Buffers[0].RGBChannelsCount; i++)
11574 {
11575 if (b.bitsPerPixel > 8)
11576 {
11577 sts[i] = new Statistics(true);
11578 }
11579 else
11580 sts[i] = new Statistics(false);
11581 }
11582 for (int z = 0; z < b.SizeZ; z++)
11583 {
11584 for (int t = 0; t < b.SizeT; t++)
11585 {
11586 int ind;
11587 if (b.Channels.Count > b.SizeC)
11588 {
11589 ind = b.GetFrameIndex(z, 0, t);
11590 }
11591 else
11592 ind = b.GetFrameIndex(z, c, t);
11593 if(b.Buffers.Count > ind)
11594 if (b.Buffers[ind].RGBChannelsCount == 1)
11595 sts[0].AddStatistics(b.Buffers[ind].Stats[0]);
11596 else
11597 {
11598 sts[0].AddStatistics(b.Buffers[ind].Stats[0]);
11599 sts[1].AddStatistics(b.Buffers[ind].Stats[1]);
11600 sts[2].AddStatistics(b.Buffers[ind].Stats[2]);
11601 if (b.Buffers[ind].RGBChannelsCount == 4)
11602 sts[3].AddStatistics(b.Buffers[ind].Stats[3]);
11603 }
11604 }
11605 }
11606 if (b.RGBChannelCount == 1)
11607 sts[0].MeanHistogram();
11608 else
11609 {
11610 sts[0].MeanHistogram();
11611 sts[1].MeanHistogram();
11612 sts[2].MeanHistogram();
11613 if (b.Buffers[0].RGBChannelsCount == 4)
11614 sts[3].MeanHistogram();
11615 }
11616 b.Channels[c].stats = sts;
11617 }
11618 statistics.MeanHistogram();
11619 b.statistics = statistics;
11620 Recorder.Record($"BioImage.AutoThreshold({b}, {updateImageStats.ToString().ToLowerInvariant()});");
11621 }
static List< string > sts
Definition Bio.cs:10698
return b
Definition Bio.cs:7393

◆ AutoThresholdThread()

static void AutoThresholdThread ( BioImage b)
static

It creates a new thread that calls the AutoThreshold function.

Parameters
BioImageThis is a class that holds the image data and some other information.
11632 {
11633 bstats = b;
11634 Thread th = new Thread(AutoThreshold);
11635 th.Start();
11636 }

◆ BitmapToSKImage()

static SKImage BitmapToSKImage ( AForge.Bitmap bitm,
int rMin = 0,
int rMax = ushort::MaxValue,
int gMin = 0,
int gMax = ushort::MaxValue,
int bMin = 0,
int bMax = ushort::MaxValue )
static
10543 {
10544 if (bitm.PixelFormat == PixelFormat.Format24bppRgb)
10545 return Convert24bppBitmapToSKImage(bitm);
10546 if (bitm.PixelFormat == PixelFormat.Format32bppArgb)
10547 return Convert32bppBitmapToSKImage(bitm);
10548 if (bitm.PixelFormat == PixelFormat.Float)
10549 return Convert32bppBitmapToSKImage(bitm.GetImageRGBA());
10550 if (bitm.PixelFormat == PixelFormat.Format8bppIndexed)
10551 return Convert8bppBitmapToSKImage(bitm);
10552 if (bitm.PixelFormat == PixelFormat.Format16bppGrayScale ||
10553 bitm.PixelFormat == PixelFormat.Format48bppRgb)
10554 return Convert16bppBitmapToSKImage(bitm, rMin, rMax, gMin, gMax, bMin, bMax);
10555 else
10556 throw new NotSupportedException("PixelFormat " + bitm.PixelFormat + " is not supported for SKImage.");
10557 }
AForge.PixelFormat PixelFormat
Definition Bio.cs:58
static SKImage Convert16bppBitmapToSKImage(Bitmap sourceBitmap, int rMin=0, int rMax=ushort.MaxValue, int gMin=0, int gMax=ushort.MaxValue, int bMin=0, int bMax=ushort.MaxValue)
Converts a 16-bit (gray or 48bpp RGB) bitmap to an SKImage, mapping the supplied per-channel min/max ...
Definition Bio.cs:10463

◆ CalculateFocusQuality()

static long CalculateFocusQuality ( Bitmap b)
static

The function calculates the focus quality of a given bitmap image.

Parameters
BitmapA class representing a bitmap image, which contains information about the image's size, pixel data, and color channels.
Returns
The method is returning a long value which represents the calculated focus quality of the input Bitmap image.
11676 {
11677 if (b.RGBChannelsCount == 1)
11678 {
11679 long sum = 0;
11680 long sumOfSquaresR = 0;
11681 for (int y = 0; y < b.SizeY; y++)
11682 for (int x = 0; x < b.SizeX; x++)
11683 {
11684 ColorS pixel = b.GetPixel(x, y);
11685 sum += pixel.R;
11686 sumOfSquaresR += pixel.R * pixel.R;
11687 }
11688 return sumOfSquaresR * b.SizeX * b.SizeY - sum * sum;
11689 }
11690 else
11691 {
11692 long sum = 0;
11693 long sumOfSquares = 0;
11694 for (int y = 0; y < b.SizeY; y++)
11695 for (int x = 0; x < b.SizeX; x++)
11696 {
11697 ColorS pixel = b.GetPixel(x, y);
11698 int p = (pixel.R + pixel.G + pixel.B) / 3;
11699 sum += p;
11700 sumOfSquares += p * p;
11701 }
11703 return sumOfSquares * b.SizeX * b.SizeY - sum * sum;
11704 }
11705 }
static MethodInfo GetCurrentMethodInfo()
Definition Recorder.cs:111
Definition Bio.cs:65

◆ Convert16bppBitmapToSKImage()

static SKImage Convert16bppBitmapToSKImage ( Bitmap sourceBitmap,
int rMin = 0,
int rMax = ushort::MaxValue,
int gMin = 0,
int gMax = ushort::MaxValue,
int bMin = 0,
int bMax = ushort::MaxValue )
static

Converts a 16-bit (gray or 48bpp RGB) bitmap to an SKImage, mapping the supplied per-channel min/max values to the 0-255 display range. This is the correct path for microscopy data which rarely uses the full 0-65535 range (e.g. 12-bit cameras top out at 4095).

10467 {
10468 int width = sourceBitmap.Width;
10469 int height = sourceBitmap.Height;
10470 bool isGray = sourceBitmap.PixelFormat == PixelFormat.Format16bppGrayScale;
10471
10472 // Clamp max != min to avoid divide-by-zero.
10473 if (rMax <= rMin) rMax = rMin + 1;
10474 if (gMax <= gMin) gMax = gMin + 1;
10475 if (bMax <= bMin) bMax = bMin + 1;
10476
10477 float rScale = 255f / (rMax - rMin);
10478 float gScale = 255f / (gMax - gMin);
10479 float bScale = 255f / (bMax - bMin);
10480
10481 SKBitmap skBitmap = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Opaque);
10482
10483 unsafe
10484 {
10485 fixed (byte* srcBytes = sourceBitmap.Bytes)
10486 {
10487 byte* dst = (byte*)skBitmap.GetPixels().ToPointer();
10488 bool swap = !sourceBitmap.LittleEndian;
10489 int expectedRowStride = isGray ? width * 2 : width * 6;
10490 int rowStride = GetSourceRowStride(sourceBitmap.Bytes, height, expectedRowStride);
10491
10492 if (isGray)
10493 {
10494 for (int y = 0; y < height; y++)
10495 {
10496 ushort* src16 = (ushort*)(srcBytes + y * rowStride);
10497 for (int x = 0; x < width; x++)
10498 {
10499 ushort v = src16[x];
10500 if (swap) v = (ushort)((v << 8) | (v >> 8));
10501 byte g = (byte)System.Math.Clamp((v - rMin) * rScale, 0f, 255f);
10502 int d = (y * width + x) * 4;
10503 dst[d + 0] = g; // B
10504 dst[d + 1] = g; // G
10505 dst[d + 2] = g; // R
10506 dst[d + 3] = 255;
10507 }
10508 }
10509 }
10510 else // Format48bppRgb: 3 × ushort per pixel
10511 {
10512 for (int y = 0; y < height; y++)
10513 {
10514 ushort* src16 = (ushort*)(srcBytes + y * rowStride);
10515 for (int x = 0; x < width; x++)
10516 {
10517 ushort r = src16[x * 3 + 0];
10518 ushort g = src16[x * 3 + 1];
10519 ushort b = src16[x * 3 + 2];
10520 if (swap)
10521 {
10522 r = (ushort)((r << 8) | (r >> 8));
10523 g = (ushort)((g << 8) | (g >> 8));
10524 b = (ushort)((b << 8) | (b >> 8));
10525 }
10526 int d = (y * width + x) * 4;
10527 dst[d + 0] = (byte)System.Math.Clamp((b - bMin) * bScale, 0f, 255f);
10528 dst[d + 1] = (byte)System.Math.Clamp((g - gMin) * gScale, 0f, 255f);
10529 dst[d + 2] = (byte)System.Math.Clamp((r - rMin) * rScale, 0f, 255f);
10530 dst[d + 3] = 255;
10531 }
10532 }
10533 }
10534 }
10535 }
10536
10537 return SKImage.FromBitmap(skBitmap);
10538 }

◆ CreateAForgeBitmap()

static AForge.Bitmap CreateAForgeBitmap ( int width,
int height,
PixelFormat px,
byte[] data,
ZCT coord,
bool littleEndian,
bool interleaved )
static
6367 {
6368 return new AForge.Bitmap(
6369 "",
6370 width,
6371 height,
6372 px,
6373 data,
6374 new AForge.ZCT(coord.Z, coord.C, coord.T),
6375 0,
6376 null,
6377 littleEndian: littleEndian,
6378 interleaved: interleaved);
6379 }

◆ Dispose()

void Dispose ( )

It disposes of all the buffers and channels in the image, removes the image from the Images list, and then forces the garbage collector to run.

11709 {
11710 try
11711 {
11712 imRead?.close();
11713 }
11714 catch
11715 {
11716 }
11717 imRead = null;
11718
11719 for (int i = 0; i < Buffers.Count; i++)
11720 {
11721 Buffers[i].Dispose();
11722 }
11723 for (int i = 0; i < Channels.Count; i++)
11724 {
11725 Channels[i].Dispose();
11726 }
11727 for (int i = 0; i < Annotations.Count; i++)
11728 {
11729 Annotations[i].Dispose();
11730 }
11731 }
ImageReader imRead
Definition Bio.cs:8521

◆ ExportROIFolder()

static void ExportROIFolder ( string path,
string filename )
static

ExportROIFolder(path, filename)

This function takes a folder path and a filename as input and exports all the ROIs in the folder as CSV files

Parameters
paththe path to the folder containing the OMERO ROI files
filenamethe name of the file you want to export
11504 {
11505 string[] fs = Directory.GetFiles(path);
11506 int i = 0;
11507 foreach (string f in fs)
11508 {
11509 List<ROI> annotations = OpenOMEROIs(f, 0);
11510 string ff = Path.GetFileNameWithoutExtension(f);
11511 ExportROIsCSV(path + "//" + ff + "-" + i.ToString() + ".csv", annotations);
11512 i++;
11513 }
11514 Recorder.Record($"BioImage.ExportROIFolder(\"{path}\", \"{filename}\");");
11515 }
static List< ROI > OpenOMEROIs(string file, int series)
It reads the OME-XML file and converts the ROIs to a list of ROI objects.
Definition Bio.cs:10837
static void ExportROIsCSV(string filename, List< ROI > Annotations)
This function takes a list of ROIs and writes them to a CSV file.
Definition Bio.cs:11470
System.IO.Path Path
Definition Bio.cs:57

◆ ExportROIsCSV()

static void ExportROIsCSV ( string filename,
List< ROI > Annotations )
static

This function takes a list of ROIs and writes them to a CSV file.

Parameters
filenamethe name of the file to be saved
AnnotationsList of ROI objects
11471 {
11472 string con = columns;
11473 con += ROIsToString(Annotations);
11474 File.WriteAllText(filename, con);
11475 Recorder.Record(BioLib.Recorder.GetCurrentMethodInfo(), false, filename, Annotations);
11476 }
static string ROIsToString(List< ROI > Annotations)
It takes a list of ROI objects and returns a string of all the ROI objects in the list.
Definition Bio.cs:11271
const string columns
Definition Bio.cs:10815

◆ ExtractRegionFromTiledTiff()

static Bitmap ExtractRegionFromTiledTiff ( BioImage b,
int x,
int y,
int width,
int height,
int level )
static

The function ExtractRegionFromTiledTiff takes a BioImage object, coordinates, width, height, and resolution as input, and returns a Bitmap object representing the extracted region from the tiled TIFF image.

Parameters
BioImageThe BioImage object represents an image file that contains multiple pages or resolutions. It contains information about the image file, such as the file path, the number of pages, and the format of each page.
xThe x-coordinate of the top-left corner of the region to extract from the tiled TIFF image.
yThe parameter "y" represents the starting y-coordinate of the region to be extracted from the tiled TIFF image.
widthThe width parameter represents the width of the region to be extracted from the tiled TIFF image.
heightThe height parameter represents the height of the region to be extracted from the tiled TIFF image.
resThe parameter "res" represents the resolution level of the tiled TIFF image. It is used to specify the level of detail or zoom level at which the image is being extracted.
Returns
The method is returning a Bitmap object.
7493 {
7494 try
7495 {
7496 // Validate inputs
7497 if (b == null) throw new ArgumentNullException(nameof(b));
7498 if (b.vipPages == null || b.vipPages.Count <= level) throw new ArgumentException("Invalid image level.");
7499
7500 // Extract the region from the specified level
7501 NetVips.Image subImage = b.vipPages[level].ExtractArea(x, y, width, height);
7502
7503 // Convert the NetVips.Image to Bitmap
7504 Bitmap bitmap = ConvertVipsImageToBitmap(subImage, b.bitsPerPixel);
7505
7506 // Clean up
7507 subImage.Dispose();
7508
7509 return bitmap;
7510 }
7511 catch (Exception ex)
7512 {
7513 Console.WriteLine($"Error: {ex.Message}\n{ex.StackTrace}");
7514 return null;
7515 }
7516 }
AForge.Bitmap Bitmap
Definition Bio.cs:53

◆ FilesToStack()

static BioImage FilesToStack ( string[] files,
int sizeZ,
int sizeC,
int sizeT )
static

It takes a list of files, and creates a new BioImage object with the first file in the list. Then it loops through the rest of the files, adding the buffers from each file to the new BioImage object. Finally, it updates the coordinates of the new BioImage object, and adds it to the Images list.

Parameters
filesan array of file paths
sizeZnumber of slices in the stack
sizeCnumber of channels
sizeTnumber of time points
Returns
A BioImage object.
7383 {
7384 BioImage b = new BioImage(files[0]);
7385 for (int i = 0; i < files.Length; i++)
7386 {
7387 BioImage bb = OpenFile(files[i], false).Result;
7388 b.Buffers.AddRange(bb.Buffers);
7389 }
7390 b.UpdateCoords(sizeZ, sizeC, sizeT);
7391 Images.AddImage(b);
7392 Recorder.Record($"BioImage.FilesToStack(new string[] {{ {string.Join(", ", files.Select(file => $"\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\""))} }}, {sizeZ}, {sizeC}, {sizeT});");
static async Task< BioImage > OpenFile(string file, ZCT coord=new ZCT())
This function opens a file and returns a BioImage object.
Definition Bio.cs:5145
Definition Bio.cs:1872
List< AForge.Bitmap > Buffers
Definition Bio.cs:1980
Definition Bio.cs:68
static void AddImage(BioImage im)
Definition Bio.cs:86

◆ FindFocus()

static int FindFocus ( BioImage im,
int Channel,
int Time )
static

The function finds the focus of a given BioImage at a specific channel and time by calculating the focus quality of each Z-plane and returning the coordinate with the highest focus quality.

Parameters
BioImageA BioImage object that contains the image data.
ChannelThe channel of the BioImage to analyze. A BioImage can have multiple channels, each representing a different fluorescent label or imaging modality.
TimeThe time point at which the focus is being calculated.
Returns
an integer value which represents the coordinate of the image with the highest focus quality in a given channel and time.
11649 {
11650 long mf = 0;
11651 int fr = 0;
11652 List<double> dt = new List<double>();
11653 ZCT c = new ZCT(0, 0, 0);
11654 for (int i = 0; i < im.SizeZ; i++)
11655 {
11656 long f = CalculateFocusQuality(im.Buffers[im.GetFrameIndex(i, Channel, Time)]);
11657 dt.Add(f);
11658 if (f > mf)
11659 {
11660 mf = f;
11661 fr = im.GetFrameIndex(i, Channel, Time);
11662 }
11663 }
11664
11665 Recorder.Record($"BioImage.FindFocus({im}, {Channel}, {Time});");
11666 return fr;
11667 }
AForge.Channel Channel
Definition Bio.cs:54
static long CalculateFocusQuality(Bitmap b)
The function calculates the focus quality of a given bitmap image.
Definition Bio.cs:11675
AForge.ZCT ZCT
Definition Bio.cs:62
int SizeZ
Definition Bio.cs:2882

◆ FolderToStack()

static BioImage FolderToStack ( string path,
bool tab )
static

It takes a folder of images and creates a stack from them.

Parameters
paththe path to the folder containing the images
Returns
A BioImage object.
7401 {
7402 string[] files = Directory.GetFiles(path);
7403 BioImage b = new BioImage(files[0]);
7404 int z = 0;
7405 int c = 0;
7406 int t = 0;
7407 BioImage bb = null;
7408 for (int i = 0; i < files.Length; i++)
7409 {
7410 string[] st = files[i].Split('_');
7411 if (st.Length > 3)
7412 {
7413 z = int.Parse(st[1].Replace("Z", ""));
7414 c = int.Parse(st[2].Replace("C", ""));
7415 t = int.Parse(st[3].Replace("T", ""));
7416 }
7417 bb = OpenFile(files[i], tab).Result;
7418 b.Buffers.AddRange(bb.Buffers);
7419 }
7420 if (z == 0)
7421 {
7422 /*TO DO
7423 ImagesToStack im = new ImagesToStack();
7424 if (im.ShowDialog() != DialogResult.OK)
7425 return null;
7426 b.UpdateCoords(im.SizeZ, im.SizeC, im.SizeT);
7427 */
7428 }
7429 else
7430 b.UpdateCoords(z + 1, c + 1, t + 1);
7431 Images.AddImage(b);
7432 Recorder.Record($"BioImage.FolderToStack(\"{path.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {tab.ToString().ToLowerInvariant()});");
7433 return b;
7434 }
static bool tab
Definition Bio.cs:10658
void UpdateCoords()
It takes a list of images and assigns them to a 3D array of coordinates.
Definition Bio.cs:3446

◆ FromNumpy()

static BioImage FromNumpy ( string file)
static

Converts a numpy array to BioImage.

Parameters
file
Returns
7159 {
7160 BioImage bm = new BioImage(file);
7161 var (shape, dataType, data) = NumPy.ReadNpyFile(file);
7162
7163 // Handle different shapes and cast the data into appropriate multi-dimensional arrays
7164 if (shape.Length == 5)
7165 {
7166 if (dataType == NumPy.NpyDataType.Float32)
7167 {
7168 float[,,,,] fs = NumPy.ConvertToMultidimensional<float, float[,,,,]>(data, shape);
7169 for (int z = 0; z < shape[2]; z++)
7170 {
7171 for (int c = 0; c < shape[1]; c++)
7172 {
7173 for (int t = 0; t < shape[0]; t++)
7174 {
7175 Bitmap b = new Bitmap(shape[3], shape[4], PixelFormat.Float);
7176 for (int y = 0; y < shape[4]; y++)
7177 {
7178 for (int x = 0; x < shape[3]; x++)
7179 {
7180 b.SetValue(x, y, fs[t, c, z, x, y]);
7181 }
7182 }
7183 bm.Buffers.Add(b);
7184 }
7185 }
7186 }
7187 }
7188 else if (dataType == NumPy.NpyDataType.UInt8)
7189 {
7190 uint[,,,,] fs = NumPy.ConvertToMultidimensional<uint, uint[,,,,]>(data, shape);
7191 for (int z = 0; z < shape[2]; z++)
7192 {
7193 for (int c = 0; c < shape[1]; c++)
7194 {
7195 for (int t = 0; t < shape[0]; t++)
7196 {
7197 Bitmap b = new Bitmap(shape[3], shape[4], PixelFormat.Format8bppIndexed);
7198 for (int y = 0; y < shape[4]; y++)
7199 {
7200 for (int x = 0; x < shape[3]; x++)
7201 {
7202 b.SetValue(x, y, fs[t, c, z, x, y]);
7203 }
7204 }
7205 bm.Buffers.Add(b);
7206 }
7207 }
7208 }
7209 }
7210 bm.sizeZ = shape[2];
7211 bm.sizeC = shape[1];
7212 bm.sizeT = shape[0];
7213 }
7214 else if (shape.Length == 4)
7215 {
7216 if (dataType == NumPy.NpyDataType.Float32)
7217 {
7218 float[,,,] fs = NumPy.ConvertToMultidimensional<float, float[,,,]>(data, shape);
7219 for (int z = 0; z < shape[1]; z++)
7220 {
7221 for (int c = 0; c < shape[0]; c++)
7222 {
7223 Bitmap b = new Bitmap(shape[2], shape[3], PixelFormat.Float);
7224 for (int y = 0; y < shape[3]; y++)
7225 {
7226 for (int x = 0; x < shape[2]; x++)
7227 {
7228 b.SetValue(x, y, fs[c, z, x, y]);
7229 }
7230 }
7231 bm.Buffers.Add(b);
7232 }
7233 }
7234 }
7235 else if (dataType == NumPy.NpyDataType.UInt8)
7236 {
7237 uint[,,,] fs = NumPy.ConvertToMultidimensional<uint, uint[,,,]>(data, shape);
7238 for (int z = 0; z < shape[1]; z++)
7239 {
7240 for (int c = 0; c < shape[0]; c++)
7241 {
7242 Bitmap b = new Bitmap(shape[2], shape[3], PixelFormat.Format8bppIndexed);
7243 for (int y = 0; y < shape[3]; y++)
7244 {
7245 for (int x = 0; x < shape[2]; x++)
7246 {
7247 b.SetValue(x, y, fs[c, z, x, y]);
7248 }
7249 }
7250 bm.Buffers.Add(b);
7251 }
7252 }
7253 }
7254 bm.sizeZ = shape[1];
7255 bm.sizeC = shape[0];
7256 }
7257 else if (shape.Length == 3)
7258 {
7259 if (dataType == NumPy.NpyDataType.Float32)
7260 {
7261 float[,,] fs = NumPy.ConvertToMultidimensional<float, float[,,]>(data, shape);
7262 for (int z = 0; z < shape[0]; z++)
7263 {
7264 Bitmap b = new Bitmap(shape[1], shape[2], PixelFormat.Float);
7265 for (int y = 0; y < shape[1]; y++)
7266 {
7267 for (int x = 0; x < shape[2]; x++)
7268 {
7269 b.SetValue(x, y, fs[z, x, y]);
7270 }
7271 }
7272 bm.Buffers.Add(b);
7273 }
7274 }
7275 else if (dataType == NumPy.NpyDataType.UInt8)
7276 {
7277 uint[,,] fs = NumPy.ConvertToMultidimensional<uint, uint[,,]>(data, shape);
7278 for (int z = 0; z < shape[0]; z++)
7279 {
7280 Bitmap b = new Bitmap(shape[1], shape[2], PixelFormat.Format8bppIndexed);
7281 for (int y = 0; y < shape[1]; y++)
7282 {
7283 for (int x = 0; x < shape[2]; x++)
7284 {
7285 b.SetValue(x, y, fs[z, x, y]);
7286 }
7287 }
7288 bm.Buffers.Add(b);
7289 }
7290 }
7291 bm.sizeZ = shape[0];
7292 bm.sizeC = 1;
7293 }
7294 else if (shape.Length == 2)
7295 {
7296 if (dataType == NumPy.NpyDataType.Float32)
7297 {
7298 float[,] fs = NumPy.ConvertToMultidimensional<float, float[,]>(data, shape);
7299 Bitmap b = new Bitmap(shape[0], shape[1], PixelFormat.Float);
7300 for (int y = 0; y < shape[1]; y++)
7301 {
7302 for (int x = 0; x < shape[0]; x++)
7303 {
7304 b.SetValue(x, y, fs[x, y]);
7305 }
7306 }
7307 bm.Buffers.Add(b);
7308 }
7309 else if (dataType == NumPy.NpyDataType.UInt8)
7310 {
7311 uint[,] fs = NumPy.ConvertToMultidimensional<uint, uint[,]>(data, shape);
7312 Bitmap b = new Bitmap(shape[0], shape[1], PixelFormat.Format8bppIndexed);
7313 for (int y = 0; y < shape[1]; y++)
7314 {
7315 for (int x = 0; x < shape[0]; x++)
7316 {
7317 b.SetValue(x, y, fs[x, y]);
7318 }
7319 }
7320 bm.Buffers.Add(b);
7321 }
7322 bm.sizeC = 1;
7323 bm.sizeZ = 1;
7324 }
7325 else
7326 {
7327 throw new InvalidOperationException("Unsupported array shape.");
7328 }
7329
7330 for (int i = 0; i < bm.SizeC; i++)
7331 {
7332 if (bm.Buffers[0].PixelFormat == PixelFormat.Float || bm.Buffers[0].PixelFormat == PixelFormat.UInt || bm.Buffers[0].PixelFormat == PixelFormat.Int)
7333 bm.Channels.Add(new Channel(i, 32, 1));
7334 else
7335 if (bm.Buffers[0].PixelFormat == PixelFormat.Format8bppIndexed)
7336 bm.Channels.Add(new Channel(i, 8, 1));
7337 }
7338 bm.sizeT = 1;
7339 bm.Coords = new int[bm.SizeZ, bm.SizeC, bm.SizeT];
7340 bm.UpdateCoords();
7341 if (dataType == NumPy.NpyDataType.Float32)
7342 bm.Resolutions.Add(new Resolution(bm.SizeX, bm.SizeY, PixelFormat.Float, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 0, 0, 0));
7343 else
7344 if (dataType == NumPy.NpyDataType.UInt32)
7345 bm.Resolutions.Add(new Resolution(bm.SizeX, bm.SizeY, PixelFormat.UInt, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 0, 0, 0));
7346 else
7347 if (dataType == NumPy.NpyDataType.Int32)
7348 bm.Resolutions.Add(new Resolution(bm.SizeX, bm.SizeY, PixelFormat.Int, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 0, 0, 0));
7349 else
7350 if (dataType == NumPy.NpyDataType.UInt8)
7351 bm.Resolutions.Add(new Resolution(bm.SizeX, bm.SizeY, PixelFormat.Format8bppIndexed, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 0, 0, 0));
7352 foreach (var item in bm.Buffers)
7353 {
7354 item.Stats = Statistics.FromBytes(item);
7355 }
7356
7357 bm.Volume = new VolumeD(new Point3D(bm.StageSizeX, bm.StageSizeY, bm.StageSizeZ), new Point3D(bm.PhysicalSizeX * bm.SizeX, bm.PhysicalSizeY * bm.SizeY, bm.PhysicalSizeZ * bm.SizeZ));
7358 AutoThreshold(bm, false);
7359 if (bm.bitsPerPixel > 8)
7360 bm.StackThreshold(true);
7361 else
7362 bm.StackThreshold(false);
7363 bm.StackOrder = Order.ZCT;
7364 bm.ID = Path.GetFileName(file);
7365 bm.Filename = bm.ID;
7366 bm.file = file;
7368 Recorder.Record($"BioImage.OpenXML(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\");");
7369 return bm;
7370 }
static Bitmap bm
Definition Bio.cs:8524
Definition Bio.cs:1165
NpyDataType
Definition Bio.cs:1167
static Array ReadNpyFile(string filePath)
Definition Bio.cs:1387
Definition Bio.cs:203

◆ GetBands()

static int GetBands ( PixelFormat format)
static

The function "GetBands" returns the number of color bands for a given pixel format in the AForge library.

Parameters
PixelFormatThe PixelFormat parameter is an enumeration that represents the format of a pixel in an image. It specifies the number of bits per pixel and the color space used by the pixel. The PixelFormat enumeration is defined in the AForge.Imaging namespace.
Returns
The method is returning the number of color bands for a given pixel format.
6961 {
6962 switch (format)
6963 {
6964 case AForge.PixelFormat.Format8bppIndexed: return 1;
6965 case AForge.PixelFormat.Format16bppGrayScale: return 1;
6966 case AForge.PixelFormat.Format24bppRgb: return 3;
6967 case AForge.PixelFormat.Format32bppArgb:
6968 case AForge.PixelFormat.Format32bppPArgb:
6969 case AForge.PixelFormat.Format32bppRgb:
6970 return 4;
6971 case AForge.PixelFormat.Format48bppRgb:
6972 return 3;
6973 default:
6974 throw new NotSupportedException($"Unsupported pixel format: {format}");
6975 }
6976 }

◆ GetBitMaxValue()

static int GetBitMaxValue ( int bt)
static

It returns the maximum value of a bit.

Parameters
btbit depth
Returns
The maximum value of a bit.
9741 {
9742 if (bt == 8)
9743 return 255;
9744 if (bt == 9)
9745 return 512;
9746 else if (bt == 10)
9747 return 1023;
9748 else if (bt == 11)
9749 return 2047;
9750 else if (bt == 12)
9751 return 4095;
9752 else if (bt == 13)
9753 return 8191;
9754 else if (bt == 14)
9755 return 16383;
9756 else if (bt == 15)
9757 return 32767;
9758 else
9759 return 65535;
9760 }

◆ GetBitsPerPixel()

static int GetBitsPerPixel ( int bt)
static

‍If the number is less than or equal to 255, then it's 8 bits. If it's less than or equal to 512, then it's 9 bits. If it's less than or equal to 1023, then it's 10 bits. If it's less than or equal to 2047, then it's 11 bits. If it's less than or equal to 4095, then it's 12 bits. If it's less than or equal to 8191, then it's 13 bits. If it's less than or equal to 16383, then it's 14 bits. If it's less than or equal to 32767, then it's 15 bits. If it's less than or equal to 65535, then it's 16 bits

Parameters
btThe number of bits per pixel.
Returns
The number of bits per pixel.
9715 {
9716 if (bt <= 255)
9717 return 8;
9718 if (bt <= 512)
9719 return 9;
9720 else if (bt <= 1023)
9721 return 10;
9722 else if (bt <= 2047)
9723 return 11;
9724 else if (bt <= 4095)
9725 return 12;
9726 else if (bt <= 8191)
9727 return 13;
9728 else if (bt <= 16383)
9729 return 14;
9730 else if (bt <= 32767)
9731 return 15;
9732 else
9733 return 16;
9734 }

◆ GetFrameIndex()

int GetFrameIndex ( int z,
int c,
int t )
8497 {
8498 try
8499 {
8500 if (StackOrder == Order.ZCT)
8501 {
8502 return Coords[z, c, t];
8503 }
8504 else if (StackOrder == Order.CZT)
8505 {
8506 return Coords[c, z, t];
8507 }
8508 else
8509 return Coords[t, c, z];
8510 }
8511 catch (Exception e)
8512 {
8513 return 0;
8514 }
8515
8516 }

◆ GetPixelFormat() [1/2]

static PixelFormat GetPixelFormat ( int rgbChannelCount,
int bitsPerPixel )
static

If the bits per pixel is 8, then the pixel format is either 8bppIndexed, 24bppRgb, or 32bppArgb. If the bits per pixel is 16, then the pixel format is either 16bppGrayScale or 48bppRgb.

Parameters
rgbChannelCountThe number of channels in the image. For example, a grayscale image has 1 channel, a color image has 3 channels (red, green, blue).
bitsPerPixel8 or 16
Returns
The PixelFormat of the image.
9809 {
9810 if (bitsPerPixel == 8)
9811 {
9812 if (rgbChannelCount == 1)
9813 return PixelFormat.Format8bppIndexed;
9814 else if (rgbChannelCount == 3)
9815 return PixelFormat.Format24bppRgb;
9816 else if (rgbChannelCount == 4)
9817 return PixelFormat.Format32bppArgb;
9818 }
9819 else
9820 {
9821 if (rgbChannelCount == 1)
9822 return PixelFormat.Format16bppGrayScale;
9823 if (rgbChannelCount == 3)
9824 return PixelFormat.Format48bppRgb;
9825 }
9826 throw new NotSupportedException("Not supported pixel format.");
9827 }

◆ GetPixelFormat() [2/2]

static PixelFormat GetPixelFormat ( int rgbChannelCount,
ome.xml.model.enums.PixelType px )
static

The function returns the appropriate PixelFormat based on the number of RGB channels and the pixel type.

Parameters
rgbChannelCountThe rgbChannelCount parameter represents the number of channels in the RGB color model. It can have a value of either 1 (for grayscale images) or 3 (for RGB color images).
pxThe parameter "px" is of type ome.xml.model.enums.PixelType. It represents the pixel type of the image, such as INT8, UINT8, INT16, or UINT16.
Returns
The method returns a PixelFormat value based on the input parameters.
9839 {
9840 if (rgbChannelCount == 1)
9841 {
9842 if (px == ome.xml.model.enums.PixelType.INT8 || px == ome.xml.model.enums.PixelType.UINT8)
9843 return PixelFormat.Format8bppIndexed;
9844 else if (px == ome.xml.model.enums.PixelType.INT16 || px == ome.xml.model.enums.PixelType.UINT16)
9845 return PixelFormat.Format16bppGrayScale;
9846 else if (px == ome.xml.model.enums.PixelType.FLOAT)
9847 return PixelFormat.Float;
9848 }
9849 else if (rgbChannelCount == 3)
9850 {
9851 if (px == ome.xml.model.enums.PixelType.INT8 || px == ome.xml.model.enums.PixelType.UINT8)
9852 return PixelFormat.Format24bppRgb;
9853 else if (px == ome.xml.model.enums.PixelType.INT16 || px == ome.xml.model.enums.PixelType.UINT16)
9854 return PixelFormat.Format48bppRgb;
9855 }
9856 else
9857 return PixelFormat.Format32bppArgb;
9858 throw new InvalidDataException("RGBChannels Count of " + rgbChannelCount + " not supported.");
9859 }

◆ GetRegion()

BioImage GetRegion ( int x,
int y,
int w,
int h )
9652 {
9653 string id = System.IO.Path.GetFileNameWithoutExtension(Filename);
9654 id.Replace(".ome", "");
9655 BioImage bm = Copy();
9656 bm.ID = id + ".ome.tif";
9657 bm.Filename = id + ".ome.tif";
9658 bm.Volume = new VolumeD(new Point3D(StageSizeX + (PhysicalSizeX * PyramidalOrigin.X), StageSizeY + (PhysicalSizeY * PyramidalOrigin.Y), StageSizeZ),
9659 new Point3D(w * PhysicalSizeX, h * PhysicalSizeY, SizeZ * PhysicalSizeZ));
9660 bm.Resolutions.Add(new Resolution(w, h, bm.Buffers[0].PixelFormat, PhysicalSizeX, PhysicalSizeY, PhysicalSizeZ, StageSizeX, StageSizeY, StageSizeZ));
9661 bm.Resolutions.RemoveAt(0);
9662 Recorder.AddLine("BioLib.Images.GetImage(\"" + bm.ID + "\").GetRegion(" + x + "," + y + "," + w + "," + h + ");", false);
9663 return bm;
9664 }
static void AddLine(string s, bool isInternal)
Definition Recorder.cs:106

◆ GetSeriesCount()

static int GetSeriesCount ( string file)
static
11741 {
11742 ImageReader r = new ImageReader();
11743 try
11744 {
11745 r.setId(file);
11746 return r.getSeriesCount();
11747 }
11748 finally
11749 {
11750 try
11751 {
11752 r.close();
11753 }
11754 catch
11755 {
11756 }
11757 }
11758 }

◆ GetTile() [1/2]

async Task< Bitmap > GetTile ( int index,
int level,
int tilex,
int tiley,
int tileSizeX,
int tileSizeY )

◆ GetTile() [2/2]

async Task< Bitmap > GetTile ( int index,
int level,
int tilex,
int tiley,
int tileSizeX,
int tileSizeY,
AForge.ZCT? coordinateOverride,
bool returnRaw = false )
8832 {
8833 BioImage b = this;
8834 Bitmap syntheticTile = await GetSyntheticPyramidTileAsync(index, level, tilex, tiley, tileSizeX, tileSizeY, coordinateOverride, returnRaw).ConfigureAwait(false);
8835 if (syntheticTile != null)
8836 return syntheticTile;
8837
8838 if (b.IsZarrSource)
8839 {
8840 if (zarrReader == null)
8841 zarrReader = await OmeZarrReader.OpenAsync(b.SourceFile);
8842
8843 if (multiscale == null)
8844 {
8845 var (ms, plateNode) = await ResolveMultiscaleAsync(zarrReader, b.SourceFile).ConfigureAwait(false);
8846 multiscale = ms;
8847 imagef = ms; // keep imagef in sync with multiscale
8848 zarrPlate = plateNode;
8849
8850 if (plateNode != null && Plate == null)
8851 await BuildZarrWellPlate(b, plateNode, b.SourceFile).ConfigureAwait(false);
8852 }
8853
8854 // For HCS plates, ZarrWellLevels[b.Level] holds the levels for the
8855 // currently selected well-field (set by PlateTool via b.Level).
8856 List<ResolutionLevelNode> activeLevels;
8857 if (Type == ImageType.well && ZarrWellLevels.Count > 0)
8858 {
8859 int fieldIndex = Math.Clamp(b.WellIndex, 0, ZarrWellLevels.Count - 1);
8860
8861 // Lazy-load: ZarrWellLevels entries are null for fields that were not
8862 // the active well at initial load time. When the user switches to a
8863 // different well we load its levels on demand here so every well can
8864 // be displayed without requiring a full reload of the image.
8865 if (ZarrWellLevels[fieldIndex] == null && b.Plate != null)
8866 {
8867 // Reconstruct the field URL from the plate well/sample metadata.
8868 // Sample.Index is assigned sequentially across all wells/fields in
8869 // the same order BuildZarrWellPlate iterates them, so we find the
8870 // sample whose Index matches fieldIndex.
8871 string fieldUrl = null;
8872 foreach (var well in b.Plate.Wells)
8873 {
8874 foreach (var sample in well.Samples)
8875 {
8876 if (sample.Index == fieldIndex)
8877 {
8878 // well.ID == wellMeta.Path (e.g. "A/1")
8879 // sample.ID == fieldIdx as string (e.g. "0")
8880 fieldUrl = b.SourceFile.TrimEnd('/') + "/"
8881 + well.ID.Trim('/') + "/"
8882 + sample.ID;
8883 break;
8884 }
8885 }
8886 if (fieldUrl != null) break;
8887 }
8888
8889 if (fieldUrl != null)
8890 {
8891 try
8892 {
8893 var fieldReader = await OmeZarrReader.OpenAsync(fieldUrl).ConfigureAwait(false);
8894 var fieldMs = await fieldReader.AsMultiscaleImageAsync().ConfigureAwait(false);
8895 var fLevels = (await fieldMs.OpenAllResolutionLevelsAsync().ConfigureAwait(false)).ToList();
8896 ZarrWellLevels[fieldIndex] = fLevels;
8897 }
8898 catch (Exception ex)
8899 {
8900 Console.WriteLine($"GetTile: failed to lazy-load well field {fieldIndex} from {fieldUrl}: {ex.Message}");
8901 }
8902 }
8903 }
8904
8905 // If still null (field does not exist or failed to load) fall back to
8906 // the first successfully loaded field so we return something rather than
8907 // crashing with a NullReferenceException.
8908 activeLevels = ZarrWellLevels[fieldIndex]
8909 ?? ZarrWellLevels.FirstOrDefault(l => l != null)
8910 ?? (levels ??= (await multiscale.OpenAllResolutionLevelsAsync()).ToList());
8911 }
8912 else
8913 {
8914 if (levels == null)
8915 levels = (await multiscale.OpenAllResolutionLevelsAsync()).ToList();
8916 activeLevels = levels;
8917 }
8918
8919 // Within a field the level parameter selects the pyramid level.
8920 // Bug fix: HCS plates must also honour the requested pyramid level —
8921 // previously ZarrWellLevels paths were hard-coded to level 0, which
8922 // caused the viewer to always fetch full-resolution data regardless of
8923 // the current zoom, producing wrong edge-tile extents and stripe artefacts.
8924 int pyramidLevel = Math.Clamp(level, 0, activeLevels.Count - 1);
8925
8926 // Determine level dimensions BEFORE calling ReadTileAsync so we can
8927 // guard against out-of-bounds tile requests. GetAxisSize only reads
8928 // the zarr node shape (metadata), not pixel data, so this is cheap.
8929 int levelW = GetAxisSize(activeLevels[pyramidLevel], "x");
8930 int levelH = GetAxisSize(activeLevels[pyramidLevel], "y");
8931
8932 // Fall back to b.Resolutions only when GetAxisSize returns 0.
8933 if (levelW == 0 || levelH == 0)
8934 {
8935 int resLevel = Math.Clamp(pyramidLevel, 0, b.Resolutions.Count - 1);
8936 levelW = b.Resolutions[resLevel].SizeX;
8937 levelH = b.Resolutions[resLevel].SizeY;
8938 }
8939
8940 int actualW = Math.Max(0, Math.Min(tileSizeX, levelW - tilex));
8941 int actualH = Math.Max(0, Math.Min(tileSizeY, levelH - tiley));
8942
8943 // Guard: tile origin is entirely outside the image — return a zeroed tile
8944 // immediately WITHOUT calling ReadTileAsync. Out-of-bounds coordinates
8945 // cause the zarr reader to clamp/wrap to chunk 0 and return pixel data from
8946 // a different plane, producing the bright-noise strip at the image boundary.
8947 ZCT tileCoord = coordinateOverride ?? b.Coordinate;
8948 if (coordinateOverride == null &&
8949 index >= 0 && index < b.Buffers.Count && b.Buffers[index] != null)
8950 {
8951 tileCoord = b.Buffers[index].Coordinate;
8952 }
8953
8954 if (actualW <= 0 || actualH <= 0)
8955 {
8956
8957 PixelFormat fmtEmpty = b.Resolutions.Count > 0
8958 ? b.Resolutions[Math.Clamp(pyramidLevel, 0, b.Resolutions.Count - 1)].PixelFormat
8959 : AForge.PixelFormat.Format16bppGrayScale;
8960 int elemEmpty = (fmtEmpty == AForge.PixelFormat.Format16bppGrayScale ||
8961 fmtEmpty == AForge.PixelFormat.Format48bppRgb) ? 2 : 1;
8962 return new Bitmap("", tileSizeX, tileSizeY, fmtEmpty,
8963 new byte[tileSizeY * tileSizeX * elemEmpty], tileCoord, index);
8964 }
8965
8966 // Bug fix: ReadTileAsync parameter order is (x, y, sizeX, sizeY, z, c, t).
8967 // Previously T was passed as z and Z as t, which reads the wrong slice on
8968 // images with more than one z or t index.
8969 int channelCount = GetAxisSize(activeLevels[pyramidLevel], "c", 1);
8970 if (channelCount == 3)
8971 {
8972 try
8973 {
8974 var red = await activeLevels[pyramidLevel].ReadTileAsync(
8975 tilex, tiley, actualW, actualH,
8976 z: tileCoord.Z,
8977 c: 0,
8978 t: tileCoord.T).ConfigureAwait(false);
8979 var green = await activeLevels[pyramidLevel].ReadTileAsync(
8980 tilex, tiley, actualW, actualH,
8981 z: tileCoord.Z,
8982 c: 1,
8983 t: tileCoord.T).ConfigureAwait(false);
8984 var blue = await activeLevels[pyramidLevel].ReadTileAsync(
8985 tilex, tiley, actualW, actualH,
8986 z: tileCoord.Z,
8987 c: 2,
8988 t: tileCoord.T).ConfigureAwait(false);
8989
8990 if (red != null && green != null && blue != null &&
8991 red.DataType == "uint8" && green.DataType == "uint8" && blue.DataType == "uint8")
8992 {
8993 GetRegionDimensions(red, actualW, actualH, out int rgbPlaneW, out int rgbPlaneH);
8994 int pixelCount = rgbPlaneW * rgbPlaneH;
8995 byte[] rgbPixels = new byte[pixelCount * 4];
8996 int copyCount = Math.Min(pixelCount,
8997 Math.Min(red.Data.Length, Math.Min(green.Data.Length, blue.Data.Length)));
8998 for (int i = 0; i < copyCount; i++)
8999 {
9000 int d = i * 4;
9001 rgbPixels[d + 0] = blue.Data[i];
9002 rgbPixels[d + 1] = green.Data[i];
9003 rgbPixels[d + 2] = red.Data[i];
9004 rgbPixels[d + 3] = 255;
9005 }
9006
9007 Bitmap rgbBm = new Bitmap(
9008 "",
9009 rgbPlaneW,
9010 rgbPlaneH,
9011 AForge.PixelFormat.Format32bppArgb,
9012 rgbPixels,
9013 tileCoord,
9014 index,
9015 null,
9016 true,
9017 true);
9018
9019 if (returnRaw)
9020 return rgbBm;
9021
9022 return rgbBm;
9023 }
9024 }
9025 catch
9026 {
9027 // Fall back to the single-channel path below.
9028 }
9029 }
9030
9031 var planef = await activeLevels[pyramidLevel].ReadTileAsync(
9032 tilex, tiley, actualW, actualH,
9033 z: tileCoord.Z,
9034 c: tileCoord.C,
9035 t: tileCoord.T);
9036
9037 if (planef.DataType == "uint16")
9038 {
9039 GetRegionDimensions(planef, actualW, actualH, out int planeW, out int planeH);
9040 int srcStride = planeW * 2;
9041 int dstStride = tileSizeX * 2;
9042 byte[] tileData = new byte[tileSizeY * dstStride];
9043 int copyH = Math.Min(actualH, planeH);
9044 int copyWBytes = Math.Min(actualW, planeW) * 2;
9045
9046 for (int row = 0; row < copyH; row++)
9047 {
9048 Buffer.BlockCopy(
9049 planef.Data,
9050 row * srcStride,
9051 tileData,
9052 row * dstStride,
9053 Math.Min(copyWBytes, dstStride));
9054 }
9055
9056 if (returnRaw)
9057 {
9058 Bitmap bm = new Bitmap(
9059 "",
9060 tileSizeX,
9061 tileSizeY,
9062 AForge.PixelFormat.Format16bppGrayScale,
9063 tileData,
9064 tileCoord,
9065 index,
9066 null,
9067 true,
9068 true);
9069 return bm;
9070 }
9071
9072 if (b.IsZarrSource)
9073 b.EnsureZarrDisplayRange(planef.Data, true, planeW, planeH);
9074
9075 int rMin2 = 0;
9076 int rMax2 = ushort.MaxValue;
9077 lock (b.ZarrDisplayRangeSync)
9078 {
9079 if (b.ZarrDisplayMax > b.ZarrDisplayMin)
9080 {
9081 rMin2 = b.ZarrDisplayMin;
9082 rMax2 = (ushort)b.ZarrDisplayMax;
9083 }
9084 }
9085 if (rMax2 <= rMin2)
9086 {
9087 rMin2 = 0;
9088 rMax2 = ushort.MaxValue;
9089 }
9090
9091 byte[] bgra = new byte[tileSizeX * tileSizeY * 4];
9092 float scale = rMax2 > rMin2 ? 255f / (rMax2 - rMin2) : 1f;
9093 int pixels = tileSizeX * tileSizeY;
9094 int srcPixelCount = Math.Min(pixels, tileData.Length / 2);
9095 for (int i = 0; i < srcPixelCount; i++)
9096 {
9097 int s = i * 2;
9098 ushort v = (ushort)(tileData[s] | (tileData[s + 1] << 8));
9099 byte g = (byte)System.Math.Clamp((v - rMin2) * scale, 0f, 255f);
9100 int d = i * 4;
9101 bgra[d + 0] = g;
9102 bgra[d + 1] = g;
9103 bgra[d + 2] = g;
9104 bgra[d + 3] = 255;
9105 }
9106
9107 return new Bitmap(
9108 "",
9109 tileSizeX,
9110 tileSizeY,
9111 AForge.PixelFormat.Format32bppArgb,
9112 bgra,
9113 tileCoord,
9114 index,
9115 null,
9116 true,
9117 true);
9118 }
9119 else if (planef.DataType == "uint8")
9120 {
9121 PixelFormat fmt = AForge.PixelFormat.Format8bppIndexed;
9122 int elementSize = 1;
9123 int dstStride = tileSizeX * elementSize;
9124 GetRegionDimensions(planef, actualW, actualH, out int planeW, out int planeH);
9125 int srcStride = planeW * elementSize;
9126
9127 byte[] tileData = new byte[tileSizeY * dstStride];
9128 int copyH = Math.Min(actualH, planeH);
9129 int copyRowBytes = Math.Min(srcStride, dstStride);
9130
9131 if (copyRowBytes > 0 && copyH > 0)
9132 {
9133 for (int row = 0; row < copyH; row++)
9134 {
9135 Buffer.BlockCopy(
9136 planef.Data,
9137 row * srcStride,
9138 tileData,
9139 row * dstStride,
9140 copyRowBytes);
9141 }
9142 }
9143
9144 Bitmap bm = new Bitmap(
9145 "",
9146 tileSizeX,
9147 tileSizeY,
9148 fmt,
9149 tileData,
9150 tileCoord,
9151 index,
9152 null,
9153 true,
9154 true);
9155 if (returnRaw)
9156 return bm;
9157
9158 int rMin2 = 0, rMax2 = 0;
9159 if (b.Channels.Count > 0)
9160 {
9161 int chIdx = Math.Clamp(b.Coordinate.C, 0, b.Channels.Count - 1);
9162 rMin2 = b.Channels[chIdx].RangeR.Min;
9163 rMax2 = b.Channels[chIdx].RangeR.Max;
9164 }
9165 if (rMin2 >= rMax2)
9166 {
9167 rMin2 = 0;
9168 rMax2 = byte.MaxValue;
9169 }
9170 return RangedRGBA(bm, rMin2, rMax2);
9171 }
9172 else
9173 {
9174 throw new NotSupportedException(planef.DataType + " is not supported.");
9175 }
9176 }
9177 if (b.Tag != null)
9178 {
9179 if (b.Tag.ToString() == "OMERO")
9180 {
9181 //This is a OMERO file we need to update it.
9182 int i = 0;
9183 for (int z = 0; z < b.SizeZ; z++)
9184 {
9185 for (int c = 0; c < b.SizeC; c++)
9186 {
9187 for (int t = 0; t < b.SizeT; t++)
9188 {
9189 if (i == index)
9190 return OMERO.GetTile(b, new ZCT(z, c, t), tilex, tiley, tileSizeX, tileSizeY, level).Result;
9191 i++;
9192 }
9193 }
9194 }
9195 }
9196 }
9197 if (vips && b.openSlideImage == null)
9198 {
9199 //We can get a tile faster with libvips rather than bioformats.
9200 Bitmap bmp = ExtractRegionFromTiledTiff(b, tilex, tiley, tileSizeX, tileSizeY, level);
9201 if (bmp != null)
9202 {
9203 return bmp;
9204 }
9205 else
9206 vips = false;
9207 }
9208 // Prefer the synthetic-level-aware OpenSlide wrapper when available.
9209 if (b.OpenSlideBase != null)
9210 {
9211 TileInfo tf = new TileInfo();
9212 int tileCol = (int)Math.Floor((double)tilex / Math.Max(1, tileSizeX));
9213 int tileRow = (int)Math.Floor((double)tiley / Math.Max(1, tileSizeY));
9214 tf.Index = new TileIndex(tileCol, tileRow, level);
9215
9216 byte[] bts = b.OpenSlideBase.GetTile(tf);
9217 if (bts == null)
9218 return null;
9219
9220 bts = NormalizeArgbTileBytes(bts, tileSizeX, tileSizeY);
9221 Bitmap bm = new Bitmap("", tileSizeX, tileSizeY, AForge.PixelFormat.Format32bppArgb, bts, new ZCT(), 0);
9222 return bm;
9223 }
9224 //We check if we can open this with OpenSlide as this is faster than Bioformats with IKVM.
9225 if (b.openSlideImage != null)
9226 {
9227 double downsample = b.GetLevelDownsample(level);
9228 long level0X = (long)Math.Round(tilex * downsample);
9229 long level0Y = (long)Math.Round(tiley * downsample);
9230
9231 byte[] bts = b.openSlideImage.ReadRegion(level, level0X, level0Y, tileSizeX, tileSizeY);
9232 bts = NormalizeArgbTileBytes(bts, tileSizeX, tileSizeY);
9233 Bitmap bm = new Bitmap("", tileSizeX, tileSizeY, AForge.PixelFormat.Format32bppArgb, bts, new ZCT(), 0);
9234 if (returnRaw)
9235 return bm;
9236 return bm;
9237 }
9238
9239 try
9240 {
9241 var imRead = b.imRead;
9242 if (imRead == null)
9243 return null;
9244
9245 lock (imRead)
9246 {
9247 if (imRead.getSeries() != level)
9248 imRead.setSeries(level);
9249 int SizeX = imRead.getSizeX();
9250 int SizeY = imRead.getSizeY();
9251 bool flat = imRead.hasFlattenedResolutions();
9252 bool littleEndian = imRead.isLittleEndian();
9253 bool interleaved = imRead.isInterleaved();
9254 PixelFormat PixelFormat = b.Resolutions[level].PixelFormat;
9255 if (tilex < 0)
9256 tilex = 0;
9257 if (tiley < 0)
9258 tiley = 0;
9259 if (tilex >= SizeX)
9260 tilex = SizeX;
9261 if (tiley >= SizeY)
9262 tiley = SizeY;
9263 int sx = tileSizeX;
9264 if (tilex + tileSizeX > SizeX)
9265 sx -= (tilex + tileSizeX) - (SizeX);
9266 int sy = tileSizeY;
9267 if (tiley + tileSizeY > SizeY)
9268 sy -= (tiley + tileSizeY) - (SizeY);
9269 //For some reason calling openBytes with 1x1px area causes an exception.
9270 if (sx <= 1)
9271 return null;
9272 if (sy <= 1)
9273 return null;
9274 byte[] bytesr = b.imRead.openBytes(index, tilex, tiley, sx, sy);
9275 var raw = new Bitmap(b.file, sx, sy, PixelFormat, bytesr, new ZCT(), index, null, littleEndian, true);
9276 if (PixelFormat == AForge.PixelFormat.Format16bppGrayScale &&
9277 b.IsZarrSource)
9278 {
9279 b.EnsureZarrDisplayRange(bytesr, littleEndian, sx, sy);
9280 }
9281
9282 int rMin3 = 0, rMax3 = 0;
9283 if (PixelFormat == AForge.PixelFormat.Format16bppGrayScale &&
9284 b.IsZarrSource)
9285 {
9286 lock (b.ZarrDisplayRangeSync)
9287 {
9288 rMin3 = b.ZarrDisplayMin;
9289 rMax3 = b.ZarrDisplayMax;
9290 }
9291 }
9292 else if (PixelFormat == AForge.PixelFormat.Format16bppGrayScale)
9293 {
9294 rMin3 = 0;
9295 rMax3 = ushort.MaxValue;
9296 }
9297 else if (b.Channels.Count > 0)
9298 {
9299 int chIdx3 = Math.Clamp(index % Math.Max(1, b.Channels.Count), 0, b.Channels.Count - 1);
9300 rMin3 = b.Channels[chIdx3].RangeR.Min;
9301 rMax3 = b.Channels[chIdx3].RangeR.Max;
9302 if (rMin3 >= rMax3)
9303 {
9304 rMin3 = 0;
9305 rMax3 = byte.MaxValue;
9306 }
9307 }
9308 return RangedRGBA(raw, rMin3, rMax3);
9309 }
9310 }
9311 catch (Exception e)
9312 {
9313 Console.WriteLine(e.Message);
9314 return null;
9315 }
9316 }
MultiscaleNode imagef
Definition Bio.cs:5266
static Bitmap ExtractRegionFromTiledTiff(BioImage b, int x, int y, int width, int height, int level)
The function ExtractRegionFromTiledTiff takes a BioImage object, coordinates, width,...
Definition Bio.cs:7492
PlateMetadata zarrPlate
Definition Bio.cs:5269
List< ResolutionLevelNode > levels
Definition Bio.cs:5268
static bool vips
Definition Bio.cs:7435
MultiscaleNode multiscale
Definition Bio.cs:5265
OmeZarrReader zarrReader
Definition Bio.cs:5264
List< List< ResolutionLevelNode > > ZarrWellLevels
For HCS Plate Zarr stores: one entry per well-field, each containing the resolution levels for that f...
Definition Bio.cs:5275
Definition OMERO.cs:34
static async Task< Bitmap > GetTile(BioImage b, ZCT coord, int x, int y, int width, int height, int level)
Definition OMERO.cs:804

◆ GetTileBytesRaw()

async Task< byte[]> GetTileBytesRaw ( int index,
int level,
int tilex,
int tiley,
int tileSizeX,
int tileSizeY,
AForge.ZCT? coordinateOverride = null )
9319 {
9320 if (IsZarrSource)
9321 {
9322 if (zarrReader == null)
9323 zarrReader = await OmeZarrReader.OpenAsync(SourceFile);
9324
9325 if (multiscale == null)
9326 {
9327 var (ms, plateNode) = await ResolveMultiscaleAsync(zarrReader, SourceFile).ConfigureAwait(false);
9328 multiscale = ms;
9329 imagef = ms;
9330 zarrPlate = plateNode;
9331
9332 if (plateNode != null && Plate == null)
9333 await BuildZarrWellPlate(this, plateNode, SourceFile).ConfigureAwait(false);
9334 }
9335
9336 List<ResolutionLevelNode> activeLevels;
9337 if (Type == ImageType.well && ZarrWellLevels.Count > 0)
9338 {
9339 int fieldIndex = Math.Clamp(WellIndex, 0, ZarrWellLevels.Count - 1);
9340 activeLevels = ZarrWellLevels[fieldIndex]
9341 ?? ZarrWellLevels.FirstOrDefault(l => l != null)
9342 ?? (levels ??= (await multiscale.OpenAllResolutionLevelsAsync()).ToList());
9343 }
9344 else
9345 {
9346 if (levels == null)
9347 levels = (await multiscale.OpenAllResolutionLevelsAsync()).ToList();
9348 activeLevels = levels;
9349 }
9350
9351 int pyramidLevel = Math.Clamp(level, 0, activeLevels.Count - 1);
9352 int levelW = GetAxisSize(activeLevels[pyramidLevel], "x");
9353 int levelH = GetAxisSize(activeLevels[pyramidLevel], "y");
9354 if (levelW == 0 || levelH == 0)
9355 {
9356 int resLevel = Math.Clamp(pyramidLevel, 0, Resolutions.Count - 1);
9357 levelW = Resolutions[resLevel].SizeX;
9358 levelH = Resolutions[resLevel].SizeY;
9359 }
9360
9361 int actualW = Math.Max(0, Math.Min(tileSizeX, levelW - tilex));
9362 int actualH = Math.Max(0, Math.Min(tileSizeY, levelH - tiley));
9363 if (actualW <= 0 || actualH <= 0)
9364 return new byte[tileSizeX * tileSizeY * (Resolutions[Math.Clamp(pyramidLevel, 0, Resolutions.Count - 1)].PixelFormat == AForge.PixelFormat.Format16bppGrayScale ? 2 : 1)];
9365
9366 ZCT tileCoord = coordinateOverride ?? Coordinate;
9367 if (coordinateOverride == null && index >= 0 && index < Buffers.Count && Buffers[index] != null)
9368 tileCoord = Buffers[index].Coordinate;
9369
9370 var planef = await activeLevels[pyramidLevel].ReadTileAsync(
9371 tilex, tiley, actualW, actualH,
9372 z: tileCoord.Z,
9373 c: tileCoord.C,
9374 t: tileCoord.T).ConfigureAwait(false);
9375
9376 if (planef == null || planef.Data == null)
9377 {
9378 int fallbackBytes = tileSizeX * tileSizeY *
9379 ((Resolutions[Math.Clamp(pyramidLevel, 0, Resolutions.Count - 1)].PixelFormat == AForge.PixelFormat.Format16bppGrayScale) ? 2 : 1);
9380 return new byte[fallbackBytes];
9381 }
9382
9383 int bytesPerPixel = (Resolutions[Math.Clamp(pyramidLevel, 0, Resolutions.Count - 1)].PixelFormat == AForge.PixelFormat.Format16bppGrayScale) ? 2 : 1;
9384 GetRegionDimensions(planef, actualW, actualH, out int planeW, out int planeH);
9385
9386 int copyW = actualW;
9387 int copyH = actualH;
9388 if (planeW > 0)
9389 copyW = Math.Min(copyW, planeW);
9390 if (planeH > 0)
9391 copyH = Math.Min(copyH, planeH);
9392
9393 if (planeW > 0 && planeH > 0 && copyW > 0 && copyH > 0)
9394 {
9395 int srcStride = planeW * bytesPerPixel;
9396 int dstStride = copyW * bytesPerPixel;
9397 byte[] cropped = new byte[copyW * copyH * bytesPerPixel];
9398 int copyRowBytes = Math.Min(dstStride, srcStride);
9399 for (int row = 0; row < copyH; row++)
9400 {
9401 int srcRow = row * srcStride;
9402 int dstRow = row * dstStride;
9403 if (srcRow >= planef.Data.Length)
9404 break;
9405 Buffer.BlockCopy(
9406 planef.Data,
9407 srcRow,
9408 cropped,
9409 dstRow,
9410 Math.Min(copyRowBytes, planef.Data.Length - srcRow));
9411 }
9412
9413 return cropped;
9414 }
9415
9416 return planef.Data;
9417 }
9418
9419 if (imRead != null)
9420 {
9421 lock (imRead)
9422 {
9423 if (imRead.getSeries() != level)
9424 imRead.setSeries(level);
9425
9426 int sizeX = imRead.getSizeX();
9427 int sizeY = imRead.getSizeY();
9428 if (tilex < 0) tilex = 0;
9429 if (tiley < 0) tiley = 0;
9430 if (tilex >= sizeX) tilex = sizeX;
9431 if (tiley >= sizeY) tiley = sizeY;
9432
9433 int sx = tileSizeX;
9434 if (tilex + tileSizeX > sizeX)
9435 sx -= (tilex + tileSizeX) - sizeX;
9436 int sy = tileSizeY;
9437 if (tiley + tileSizeY > sizeY)
9438 sy -= (tiley + tileSizeY) - sizeY;
9439
9440 if (sx <= 0 || sy <= 0)
9441 return Array.Empty<byte>();
9442
9443 return imRead.openBytes(index, tilex, tiley, sx, sy);
9444 }
9445 }
9446
9447 using Bitmap tile = await GetTile(index, level, tilex, tiley, tileSizeX, tileSizeY, coordinateOverride, true).ConfigureAwait(false);
9448 return tile?.Bytes;
9449 }
int WellIndex
Gets or sets the currently active well-field index for HCS OME-Zarr plates. This is the zero-based in...
Definition Bio.cs:5289
async Task< Bitmap > GetTile(int index, int level, int tilex, int tiley, int tileSizeX, int tileSizeY)

◆ GetWellFieldBitmap()

async Task< Bitmap > GetWellFieldBitmap ( int fieldIndex,
int z = 0,
int c = 0,
int t = 0 )
8622 {
8623 // Ensure the zarr reader and plate structure are initialised.
8624 if (zarrReader == null)
8625 zarrReader = await OmeZarrReader.OpenAsync(SourceFile).ConfigureAwait(false);
8626
8627 if (multiscale == null)
8628 {
8629 var (ms, plateNode) = await ResolveMultiscaleAsync(zarrReader, SourceFile).ConfigureAwait(false);
8630 multiscale = ms;
8631 zarrPlate = plateNode;
8632
8633 if (plateNode != null && Plate == null)
8634 await BuildZarrWellPlate(this, plateNode, SourceFile).ConfigureAwait(false);
8635 }
8636
8637 // Validate field index bounds.
8638 if (ZarrWellLevels.Count == 0 || fieldIndex < 0 || fieldIndex >= ZarrWellLevels.Count)
8639 return null;
8640
8641 // Lazy-load: if this field's levels haven't been loaded yet, load them now
8642 // using the same pattern as GetTile so every well field can be displayed.
8643 if (ZarrWellLevels[fieldIndex] == null && Plate != null)
8644 {
8645 string fieldUrl = null;
8646 foreach (var well in Plate.Wells)
8647 {
8648 foreach (var sample in well.Samples)
8649 {
8650 if (sample.Index == fieldIndex)
8651 {
8652 fieldUrl = Filename.TrimEnd('/') + "/"
8653 + well.ID.Trim('/') + "/"
8654 + sample.ID;
8655 break;
8656 }
8657 }
8658 if (fieldUrl != null) break;
8659 }
8660
8661 if (fieldUrl != null)
8662 {
8663 try
8664 {
8665 var fieldReader = await OmeZarrReader.OpenAsync(fieldUrl).ConfigureAwait(false);
8666 var fieldMs = await fieldReader.AsMultiscaleImageAsync().ConfigureAwait(false);
8667 var fLevels = (await fieldMs.OpenAllResolutionLevelsAsync().ConfigureAwait(false)).ToList();
8668 ZarrWellLevels[fieldIndex] = fLevels;
8669 }
8670 catch (Exception ex)
8671 {
8672 Console.WriteLine($"GetWellFieldBitmap: failed to lazy-load well field {fieldIndex} from {fieldUrl}: {ex.Message}");
8673 }
8674 }
8675 }
8676
8677 var fieldLevels = ZarrWellLevels[fieldIndex];
8678 if (fieldLevels == null || fieldLevels.Count == 0)
8679 return null;
8680
8681 // Use the coarsest (last) pyramid level for a cheap thumbnail.
8682 int thumbLevel = fieldLevels.Count - 1;
8683 var levelNode = fieldLevels[thumbLevel];
8684 var shape = levelNode.Shape;
8685
8686 // Shape axes are (..., y, x) — last two are always spatial.
8687 int rank = shape.Length;
8688 int imgW = (int)shape[rank - 1];
8689 int imgH = (int)shape[rank - 2];
8690
8691 if (imgW <= 0 || imgH <= 0)
8692 return null;
8693
8694 // Clamp z/c/t to valid ranges derived from the non-spatial axes.
8695 // Axis order is typically (t, c, z, y, x) for OME-Zarr.
8696 var axes = levelNode.EffectiveAxes;
8697 int safeZ = z, safeC = c, safeT = t;
8698 for (int i = 0; i < axes.Length; i++)
8699 {
8700 switch (axes[i].Name.ToLowerInvariant())
8701 {
8702 case "z": safeZ = (int)Math.Clamp(z, 0, shape[i] - 1); break;
8703 case "c": safeC = (int)Math.Clamp(c, 0, shape[i] - 1); break;
8704 case "t": safeT = (int)Math.Clamp(t, 0, shape[i] - 1); break;
8705 }
8706 }
8707
8708 int channelCount = GetAxisSize(levelNode, "c", 1);
8709 if (channelCount == 3)
8710 {
8711 try
8712 {
8713 var red = await levelNode.ReadTileAsync(
8714 tileOriginX: 0,
8715 tileOriginY: 0,
8716 tileSizeX: imgW,
8717 tileSizeY: imgH,
8718 z: safeZ,
8719 c: 0,
8720 t: safeT).ConfigureAwait(false);
8721 var green = await levelNode.ReadTileAsync(
8722 tileOriginX: 0,
8723 tileOriginY: 0,
8724 tileSizeX: imgW,
8725 tileSizeY: imgH,
8726 z: safeZ,
8727 c: 1,
8728 t: safeT).ConfigureAwait(false);
8729 var blue = await levelNode.ReadTileAsync(
8730 tileOriginX: 0,
8731 tileOriginY: 0,
8732 tileSizeX: imgW,
8733 tileSizeY: imgH,
8734 z: safeZ,
8735 c: 2,
8736 t: safeT).ConfigureAwait(false);
8737
8738 if (red != null && green != null && blue != null &&
8739 red.DataType == "uint8" && green.DataType == "uint8" && blue.DataType == "uint8")
8740 {
8741 var rs = red.Shape;
8742 int rgbPlaneW = (int)rs[rs.Length - 1];
8743 int rgbPlaneH = (int)rs[rs.Length - 2];
8744 int pixelCount = rgbPlaneW * rgbPlaneH;
8745 byte[] rgbPixels = new byte[pixelCount * 4];
8746 int copyCount = Math.Min(pixelCount,
8747 Math.Min(red.Data.Length, Math.Min(green.Data.Length, blue.Data.Length)));
8748
8749 for (int i = 0; i < copyCount; i++)
8750 {
8751 int d = i * 4;
8752 rgbPixels[d + 0] = blue.Data[i];
8753 rgbPixels[d + 1] = green.Data[i];
8754 rgbPixels[d + 2] = red.Data[i];
8755 rgbPixels[d + 3] = 255;
8756 }
8757
8758 var rgbCoord = new ZCT(safeZ, 0, safeT);
8759 var rgbBitmap = new Bitmap("", rgbPlaneW, rgbPlaneH, PixelFormat.Format32bppArgb, rgbPixels, rgbCoord, 0, null, true, true);
8760 return rgbBitmap;
8761 }
8762 }
8763 catch
8764 {
8765 // Fall back to the single-channel path below.
8766 }
8767 }
8768
8769 // Read the full spatial plane at the chosen z/c/t.
8770 var plane = await levelNode.ReadTileAsync(
8771 tileOriginX: 0,
8772 tileOriginY: 0,
8773 tileSizeX: imgW,
8774 tileSizeY: imgH,
8775 z: safeZ,
8776 c: safeC,
8777 t: safeT).ConfigureAwait(false);
8778
8779 // Derive actual returned dimensions from shape (avoids dependency on
8780 // PlaneResult.Width/Height which may not exist in all build configurations).
8781 var ps = plane.Shape;
8782 int planeW = (int)ps[ps.Length - 1];
8783 int planeH = (int)ps[ps.Length - 2];
8784
8785 PixelFormat fmt;
8786 int elementSize;
8787 if (plane.DataType == "uint16")
8788 {
8789 fmt = PixelFormat.Format16bppGrayScale;
8790 elementSize = 2;
8791 }
8792 else if (plane.DataType == "uint8")
8793 {
8794 fmt = PixelFormat.Format8bppIndexed;
8795 elementSize = 1;
8796 }
8797 else
8798 {
8799 throw new NotSupportedException(
8800 $"GetWellFieldBitmap: unsupported data type '{plane.DataType}'.");
8801 }
8802
8803 // Pack into a padded buffer sized to the actual plane dimensions.
8804 int stride = planeW * elementSize;
8805 byte[] pixels = new byte[planeH * stride];
8806 int copyBytes = Math.Min(plane.Data.Length, pixels.Length);
8807 Buffer.BlockCopy(plane.Data, 0, pixels, 0, copyBytes);
8808
8809 var coord = new ZCT(safeZ, safeC, safeT);
8810 var bm = new Bitmap("", planeW, planeH, fmt, pixels, coord, 0, null, true, false);
8811 int rMin1 = 0, rMax1 = 0;
8812 if (Channels.Count > 0)
8813 {
8814 int chIdx = Math.Clamp(c, 0, Channels.Count - 1);
8815 rMin1 = Channels[chIdx].RangeR.Min;
8816 rMax1 = Channels[chIdx].RangeR.Max;
8817 }
8818 if (fmt == PixelFormat.Format16bppGrayScale &&
8819 IsZarrSource &&
8821 {
8822 rMin1 = ZarrDisplayMin;
8823 rMax1 = ZarrDisplayMax;
8824 }
8825 return RangedRGBA(bm, rMin1, rMax1);
8826 }
int ZarrDisplayMax
Definition Bio.cs:5304
ushort ZarrDisplayMin
Definition Bio.cs:5305

◆ ImagesToStack()

static BioImage ImagesToStack ( string[] files,
bool tab )
static

It takes a list of files, opens them, and then combines them into a single BioImage object.

Parameters
filesan array of file paths
Returns
A BioImage object.
9954 {
9955 BioImage[] bs = new BioImage[files.Length];
9956 int z = 0;
9957 int c = 0;
9958 int t = 0;
9959 for (int i = 0; i < files.Length; i++)
9960 {
9961 string str = Path.GetFileNameWithoutExtension(files[i]);
9962 str = str.Replace(".ome", "");
9963 string[] st = str.Split('_');
9964 if (st.Length > 3)
9965 {
9966 z = int.Parse(st[1].Replace("Z", ""));
9967 c = int.Parse(st[2].Replace("C", ""));
9968 t = int.Parse(st[3].Replace("T", ""));
9969 }
9970 if (i == 0)
9971 bs[0] = OpenOME(files[i], tab);
9972 else
9973 {
9974 bs[i] = OpenFileAsync(files[i], 0, tab, false).Result;
9975 }
9976 }
9977 BioImage b = BioImage.CopyInfo(bs[0], true, true);
9978 for (int i = 0; i < files.Length; i++)
9979 {
9980 for (int bc = 0; bc < bs[i].Buffers.Count; bc++)
9981 {
9982 b.Buffers.Add(bs[i].Buffers[bc]);
9983 }
9984 }
9985 b.UpdateCoords(z + 1, c + 1, t + 1);
9986 b.Volume = new VolumeD(bs[0].Volume.Location, new Point3D(bs[0].SizeX * bs[0].PhysicalSizeX, bs[0].SizeY * bs[0].PhysicalSizeY, (z + 1) * bs[0].PhysicalSizeZ));
9987 Recorder.Record($"BioImage.ImagesToStack(new string[] {{ {string.Join(", ", files.Select(file => $"\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\""))} }}, {tab.ToString().ToLowerInvariant()});");
static BioImage OpenOME(string file, bool tab)
The function "OpenOME" opens a bioimage file in the OME format and returns the first image in the ser...
Definition Bio.cs:7137
static async Task< BioImage > OpenFileAsync(string file, int series, bool tab, bool addToImages)
It opens a TIFF file and returns a BioImage object.
Definition Bio.cs:5163
static BioImage CopyInfo(BioImage b, bool copyAnnotations, bool copyChannels)
Definition Bio.cs:2220

◆ ImportROIsCSV()

static List< ROI > ImportROIsCSV ( string filename)
static

It reads the CSV file and converts each line into a ROI object.

Parameters
filenameThe path to the CSV file.
Returns
A list of ROI objects.
11483 {
11484 List<ROI> list = new List<ROI>();
11485 if (!File.Exists(filename))
11486 return list;
11487 string[] sts = File.ReadAllLines(filename);
11488 //We start reading from line 1.
11489 for (int i = 1; i < sts.Length; i++)
11490 {
11491 list.Add(StringToROI(sts[i]));
11492 }
11493 Recorder.Record($"BioImage.ImportROIsCSV(\"{filename}\");");
11494 return list;
11495 }
static ROI StringToROI(string sts)
It takes a string and returns an ROI object.
Definition Bio.cs:11311

◆ InitDirectoryResolution()

static void InitDirectoryResolution ( BioImage b,
Tiff image,
ImageJDesc jdesc = null )
static

The function InitDirectoryResolution initializes the resolution properties of a BioImage object based on the resolution information stored in a TIFF image.

Parameters
BioImageBioImage is a class representing a bioimage, which contains information about the image resolution, size, and pixel format.
TiffThe "Tiff" parameter is an object of the Tiff class. It represents a TIFF image file and provides methods and properties to access and manipulate the image data.
ImageJDescImageJDesc is a class that contains information about the image dimensions and resolution in ImageJ format. It has the following properties:
5194 {
5195 Resolution res = new Resolution();
5196 FieldValue[] rs = image.GetField(TiffTag.RESOLUTIONUNIT);
5197 string unit = "NONE";
5198 if (rs != null)
5199 unit = rs[0].ToString();
5200 if (jdesc != null)
5201 res.PhysicalSizeZ = jdesc.finterval;
5202 else
5203 res.PhysicalSizeZ = 2.54 / 96;
5204 if (unit == "CENTIMETER")
5205 {
5206 if (image.GetField(TiffTag.XRESOLUTION) != null)
5207 {
5208 double x = image.GetField(TiffTag.XRESOLUTION)[0].ToDouble();
5209 res.PhysicalSizeX = (1000 / x);
5210 }
5211 if (image.GetField(TiffTag.YRESOLUTION) != null)
5212 {
5213 double y = image.GetField(TiffTag.YRESOLUTION)[0].ToDouble();
5214 res.PhysicalSizeY = (1000 / y);
5215 }
5216 }
5217 else
5218 if (unit == "INCH")
5219 {
5220 if (image.GetField(TiffTag.XRESOLUTION) != null)
5221 {
5222 double x = image.GetField(TiffTag.XRESOLUTION)[0].ToDouble();
5223 res.PhysicalSizeX = (2.54 / x) / 2.54;
5224 }
5225 if (image.GetField(TiffTag.YRESOLUTION) != null)
5226 {
5227 double y = image.GetField(TiffTag.YRESOLUTION)[0].ToDouble();
5228 res.PhysicalSizeY = (2.54 / y) / 2.54;
5229 }
5230 }
5231 else
5232 if (unit == "NONE")
5233 {
5234 if (jdesc != null && jdesc.ImageJ != null)
5235 {
5236 if (jdesc.unit == "micron")
5237 {
5238 if (image.GetField(TiffTag.XRESOLUTION) != null)
5239 {
5240 double x = image.GetField(TiffTag.XRESOLUTION)[0].ToDouble();
5241 res.PhysicalSizeX = (2.54 / x) / 2.54;
5242 }
5243 if (image.GetField(TiffTag.YRESOLUTION) != null)
5244 {
5245 double y = image.GetField(TiffTag.YRESOLUTION)[0].ToDouble();
5246 res.PhysicalSizeY = (2.54 / y) / 2.54;
5247 }
5248 }
5249 }
5250 else
5251 {
5252 res.PhysicalSizeX = 2.54 / 96;
5253 res.PhysicalSizeY = 2.54 / 96;
5254 res.PhysicalSizeZ = 2.54 / 96;
5255 }
5256 }
5257 res.SizeX = image.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
5258 res.SizeY = image.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
5259 int bitsPerPixel = image.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt();
5260 int RGBChannelCount = image.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToInt();
5261 res.PixelFormat = GetPixelFormat(RGBChannelCount, bitsPerPixel);
5262 b.Resolutions.Add(res);
5263 }
static PixelFormat GetPixelFormat(int rgbChannelCount, int bitsPerPixel)
If the bits per pixel is 8, then the pixel format is either 8bppIndexed, 24bppRgb,...
Definition Bio.cs:9808

◆ isOME()

static bool isOME ( string file)
static

If the file is a TIFF, check the ImageDescription tag for the string "OME-XML". If it's there, return true. If it's not a TIFF, return true. If it's a PNG, JPG, JPEG, or BMP, return false.

Parameters
filethe path to the image file
Returns
A boolean value.
6423 {
6424 if (file.EndsWith("ome.tif") || file.EndsWith("ome.tiff"))
6425 {
6426 return true;
6427 }
6428 if (file.EndsWith(".tif") || file.EndsWith(".TIF") || file.EndsWith("tiff") || file.EndsWith("TIFF"))
6429 {
6430 Tiff image = Tiff.Open(file, "r");
6431 string desc = "";
6432 FieldValue[] f = image.GetField(TiffTag.IMAGEDESCRIPTION);
6433 desc = f[0].ToString();
6434 image.Close();
6435 if (desc.Contains("OME-XML"))
6436 return true;
6437 else
6438 return false;
6439 }
6440 if ((file.EndsWith("png") || file.EndsWith("PNG") || file.EndsWith("jpg") || file.EndsWith("JPG") ||
6441 file.EndsWith("jpeg") || file.EndsWith("JPEG") || file.EndsWith("bmp") || file.EndsWith("BMP")))
6442 {
6443 return false;
6444 }
6445 else return true;
6446 }

◆ isOMESeries()

static bool isOMESeries ( string file)
static

‍If the file is an OME file and has more than one series, return true

Parameters
filethe file to be checked
Returns
A boolean value.
6453 {
6454 if (!isOME(file))
6455 return false;
6456 ImageReader reader = new ImageReader();
6457 var meta = (IMetadata)((OMEXMLService)new ServiceFactory().getInstance(typeof(OMEXMLService))).createOMEXMLMetadata();
6458 reader.setMetadataStore((MetadataStore)meta);
6459 file = file.Replace("\\", "/");
6460 reader.setId(file);
6461 bool ser = false;
6462 if (reader.getSeriesCount() > 1)
6463 ser = true;
6464 reader.close();
6465 reader = null;
6466 return ser;
6467 }
static bool isOME(string file)
If the file is a TIFF, check the ImageDescription tag for the string "OME-XML". If it's there,...
Definition Bio.cs:6422

◆ isTiffSeries()

static bool isTiffSeries ( string file)
static

‍The function checks if the image is a Tiff image and if it is, it checks if the image is a series of images

Parameters
filethe path to the file
Returns
a boolean value.
6388 {
6389 Tiff image = Tiff.Open(file, "r");
6390 string desc = "";
6391 FieldValue[] f = image.GetField(TiffTag.IMAGEDESCRIPTION);
6392 image.Close();
6393 string[] sts = desc.Split('\n');
6394 int index = 0;
6395 for (int i = 0; i < sts.Length; i++)
6396 {
6397 if (sts[i].StartsWith("-ImageInfo"))
6398 {
6399 string val = sts[i].Substring(11);
6400 val = val.Substring(0, val.IndexOf(':'));
6401 int serie = int.Parse(val);
6402 if (sts[i].Length > 10)
6403 {
6404 string cht = sts[i].Substring(sts[i].IndexOf('{'), sts[i].Length - sts[i].IndexOf('{'));
6405 ImageInfo info = JsonConvert.DeserializeObject<ImageInfo>(cht);
6406 if (info.Series > 1)
6407 return true;
6408 else
6409 return false;
6410 }
6411 }
6412 }
6413 return false;
6414 }
static int serie
Definition Bio.cs:10659
Definition Bio.cs:1066
int Series
Definition Bio.cs:1135

◆ IsTiffTiled()

static bool IsTiffTiled ( string imagePath)
static
5168 {
5169 if (imagePath.EndsWith(".tif") || imagePath.EndsWith(".tiff"))
5170 {
5171 using (Tiff tiff = Tiff.Open(imagePath, "r"))
5172 {
5173 if (tiff == null)
5174 {
5175 throw new Exception("Failed to open TIFF image.");
5176 }
5177 bool t = tiff.IsTiled();
5178 tiff.Close();
5179 return t;
5180 }
5181 }
5182 else return false;
5183 }

◆ Open() [1/3]

static void Open ( string file)
static
9928 {
9929 OpenFile(file, true).Wait();
9930 }

◆ Open() [2/3]

static void Open ( string file,
int z = 0,
int c = 0,
int t = 0 )
static

It opens a file.

Parameters
fileThe file to open.
9935 {
9936 OpenFile(file, true).Wait();
9937 }

◆ Open() [3/3]

static void Open ( string[] files,
ZCT coord )
static

It opens a file.

Parameters
filesThe files to open.
9942 {
9943 foreach (string file in files)
9944 {
9945 Open(file, coord.Z, coord.C, coord.T);
9946 }
9947 }
static void Open(string file)
Definition Bio.cs:9927

◆ OpenAsync() [1/2]

static async Task OpenAsync ( string file,
bool OME,
bool newtab,
bool images,
int series )
static

It opens a file in a new thread.

Parameters
fileThe file to open
9909 {
9910 openfile = file;
9911 omes = OME;
9912 tab = newtab;
9913 add = images;
9914 serie = series;
9915 await Task.Run(OpenThread);
9916 }
static bool add
Definition Bio.cs:10658
static string openfile
Definition Bio.cs:10657
static bool omes
Definition Bio.cs:10658
static void OpenThread()
Definition Bio.cs:10660

◆ OpenAsync() [2/2]

static async Task OpenAsync ( string[] files,
bool OME,
bool tab,
bool images )
static

It opens a file asynchronously.

Parameters
filesThe file(s) to open.
9921 {
9922 foreach (string file in files)
9923 {
9924 await OpenAsync(file, OME, tab, images, 0);
9925 }
9926 }
static async Task OpenAsync(string file, bool OME, bool newtab, bool images, int series)
It opens a file in a new thread.
Definition Bio.cs:9908

◆ OpenFile() [1/3]

static async Task< BioImage > OpenFile ( string file,
bool tab )
static
5152 {
5153 if (file.StartsWith("https://") || file.StartsWith("s3://"))
5154 return await OpenURL(file, new ZCT(), 0, 0, 800, 600).ConfigureAwait(false);
5155 return await OpenFileAsync(file, 0, tab, true).ConfigureAwait(false);
5156 }
static async Task< BioImage > OpenURL(string url, ZCT coord, int originX, int originY, int tileWidth, int tileHeight)
Definition Bio.cs:5806

◆ OpenFile() [2/3]

static async Task< BioImage > OpenFile ( string file,
int series,
bool tab,
bool addToImages,
bool tile,
int tileX,
int tileY,
int tileSizeX = 800,
int tileSizeY = 600 )
static

The OpenFile function opens a BioImage file, reads its metadata, and loads the image data into a BioImage object.

Parameters
fileThe file path of the bioimage to be opened.
seriesThe series parameter is an integer that specifies the series number of the image to open.
tabThe "tab" parameter is a boolean value that determines whether the BioImage should be opened in a new tab or not. If set to true, the BioImage will be opened in a new tab. If set to false, the BioImage will be opened in the current tab.
addToImagesA boolean value indicating whether the BioImage should be added to the Images collection.
tileThe "tile" parameter is a boolean value that determines whether the image should be opened as a tiled image or not. If set to true, the image will be opened as a tiled image. If set to false, the image will be opened as a regular image.
tileXThe tileX parameter is an integer that represents the starting X coordinate of the tile. It is used when opening a tiled image to specify the position of the tile within the image.
tileYThe tileY parameter is used to specify the starting Y coordinate of the tile when opening a tiled image. It determines the position of the tile within the image.
tileSizeXThe tileSizeX parameter is the width of each tile in pixels. It is used when opening a tiled TIFF image to specify the size of the tiles.
tileSizeYThe tileSizeY parameter is the height of each tile in pixels when the image is tiled.
Returns
The method is returning a BioImage object.
5335 {
5336 string fs = file.Replace("\\", "/");
5337 Console.WriteLine("Opening BioImage: " + file);
5338 if(file.StartsWith("https://") || file.StartsWith("s3://") || file.Contains(".zarr"))
5339 return await BioImage.OpenURL(file,new ZCT(0,0,0),tileX,tileY,tileSizeX,tileSizeY);
5340 if (file.EndsWith(".npy"))
5341 return BioImage.FromNumpy(file);
5342 bool ome = isOME(file);
5343 if (ome) return OpenOME(file, series, tab, addToImages, tile, tileX, tileY, tileSizeX, tileSizeY);
5344 bool tiled = IsTiffTiled(file);
5345 Console.WriteLine("IsTiled=" + tiled.ToString());
5346 tile = tiled;
5347
5348 Stopwatch st = new Stopwatch();
5349 st.Start();
5350 Status = "Opening Image";
5351 progFile = file;
5352 BioImage b = new BioImage(file);
5353 if (tiled && file.EndsWith(".tif") && !file.EndsWith(".ome.tif"))
5354 {
5355 try
5356 {
5357 //To open this we need libvips
5358 vips = VipsSupport(b.file);
5359 }
5360 catch (Exception e)
5361 {
5362 Console.WriteLine(e.Message);
5363 }
5364 }
5365 b.series = series;
5366 b.file = file;
5367 string fn = Path.GetFileNameWithoutExtension(file);
5368 string dir = Path.GetDirectoryName(file);
5369 if (File.Exists(fn + ".csv"))
5370 {
5371 string f = dir + "//" + fn + ".csv";
5372 b.Annotations = BioImage.ImportROIsCSV(f);
5373 }
5374 if (file.EndsWith("tif") || file.EndsWith("tiff") || file.EndsWith("TIF") || file.EndsWith("TIFF"))
5375 {
5376 Tiff image = Tiff.Open(file, "r");
5377 b.tifRead = image;
5378 int SizeX = image.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
5379 int SizeY = image.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
5380 b.littleEndian = image.IsBigEndian();
5381 int RGBChannelCount = image.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToInt();
5382 string desc = "";
5383
5384 FieldValue[] f = image.GetField(TiffTag.IMAGEDESCRIPTION);
5385 desc = f[0].ToString();
5386 ImageJDesc imDesc = null;
5387 b.sizeC = 1;
5388 b.sizeT = 1;
5389 b.sizeZ = 1;
5390 bool imagej = false;
5391 if (f != null && !tile)
5392 {
5393 imDesc = new ImageJDesc();
5394 desc = f[0].ToString();
5395 if (desc.StartsWith("ImageJ"))
5396 {
5397 imDesc.SetString(desc);
5398 if (imDesc.channels != 0)
5399 b.sizeC = imDesc.channels;
5400 else
5401 b.sizeC = 1;
5402 if (imDesc.slices != 0)
5403 b.sizeZ = imDesc.slices;
5404 else
5405 b.sizeZ = 1;
5406 if (imDesc.frames != 0)
5407 b.sizeT = imDesc.frames;
5408 else
5409 b.sizeT = 1;
5410 if (imDesc.finterval != 0)
5411 b.frameInterval = imDesc.finterval;
5412 else
5413 b.frameInterval = 1;
5414 if (imDesc.spacing != 0)
5415 b.imageInfo.PhysicalSizeZ = imDesc.spacing;
5416 else
5417 b.imageInfo.PhysicalSizeZ = 1;
5418 imagej = true;
5419 }
5420 }
5421 int stride = 0;
5423 if (RGBChannelCount == 1)
5424 {
5425 if (b.bitsPerPixel > 8)
5426 {
5427 PixelFormat = PixelFormat.Format16bppGrayScale;
5428 stride = SizeX * 2;
5429 }
5430 else
5431 {
5432 PixelFormat = PixelFormat.Format8bppIndexed;
5433 stride = SizeX;
5434 }
5435 }
5436 else
5437 if (RGBChannelCount == 3)
5438 {
5439 b.sizeC = 1;
5440 if (b.bitsPerPixel > 8)
5441 {
5442 PixelFormat = PixelFormat.Format48bppRgb;
5443 stride = SizeX * 2 * 3;
5444 }
5445 else
5446 {
5447 PixelFormat = PixelFormat.Format24bppRgb;
5448 stride = SizeX * 3;
5449 }
5450 }
5451 else
5452 {
5453 PixelFormat = PixelFormat.Format32bppArgb;
5454 stride = SizeX * 4;
5455 }
5456
5457 string[] sts = desc.Split('\n');
5458 int index = 0;
5459 for (int i = 0; i < sts.Length; i++)
5460 {
5461 if (sts[i].StartsWith("-Channel"))
5462 {
5463 string val = sts[i].Substring(9);
5464 val = val.Substring(0, val.IndexOf(':'));
5465 int serie = int.Parse(val);
5466 if (serie == series && sts[i].Length > 7)
5467 {
5468 string cht = sts[i].Substring(sts[i].IndexOf('{'), sts[i].Length - sts[i].IndexOf('{'));
5469 Channel.ChannelInfo info = JsonConvert.DeserializeObject<Channel.ChannelInfo>(cht);
5470 Channel ch = new Channel(index, b.bitsPerPixel, info.SamplesPerPixel);
5471 ch.info = info;
5472 b.Channels.Add(ch);
5473 if (index == 0)
5474 {
5475 b.rgbChannels[0] = 0;
5476 }
5477 else
5478 if (index == 1)
5479 {
5480 b.rgbChannels[1] = 1;
5481 }
5482 else
5483 if (index == 2)
5484 {
5485 b.rgbChannels[2] = 2;
5486 }
5487 index++;
5488 }
5489 }
5490 else
5491 if (sts[i].StartsWith("-ROI"))
5492 {
5493 string val = sts[i].Substring(5);
5494 val = val.Substring(0, val.IndexOf(':'));
5495 int serie = int.Parse(val);
5496 if (serie == series && sts[i].Length > 7)
5497 {
5498 string s = sts[i].Substring(sts[i].IndexOf("ROI:") + 4, sts[i].Length - (sts[i].IndexOf("ROI:") + 4));
5499 string ro = s.Substring(s.IndexOf(":") + 1, s.Length - (s.IndexOf(':') + 1));
5500 ROI roi = StringToROI(ro);
5501 b.Annotations.Add(roi);
5502 }
5503 }
5504 else
5505 if (sts[i].StartsWith("-ImageInfo"))
5506 {
5507 string val = sts[i].Substring(11);
5508 val = val.Substring(0, val.IndexOf(':'));
5509 int serie = int.Parse(val);
5510 if (serie == series && sts[i].Length > 10)
5511 {
5512 string cht = sts[i].Substring(sts[i].IndexOf('{'), sts[i].Length - sts[i].IndexOf('{'));
5513 b.imageInfo = JsonConvert.DeserializeObject<ImageInfo>(cht);
5514 }
5515 }
5516 }
5517 b.Coords = new int[b.SizeZ, b.SizeC, b.SizeT];
5518 if (tiled && tileSizeX == 0 && tileSizeY == 0)
5519 {
5520 tileSizeX = 1920;
5521 tileSizeY = 1080;
5522 }
5523
5524 //If this is a tiff file not made by Bio we init channels based on RGBChannels.
5525 if (b.Channels.Count == 0)
5526 b.Channels.Add(new Channel(0, b.bitsPerPixel, RGBChannelCount));
5527
5528 //Lets check to see the channels are correctly defined in this file
5529 for (int ch = 0; ch < b.Channels.Count; ch++)
5530 {
5531 if (b.Channels[ch].SamplesPerPixel != RGBChannelCount)
5532 {
5533 b.Channels[ch].SamplesPerPixel = RGBChannelCount;
5534 }
5535 }
5536
5537 b.Buffers = new List<Bitmap>();
5538 int pages = image.NumberOfDirectories() / b.seriesCount;
5539 if (!imagej)
5540 b.sizeZ = pages;
5541 int str = image.ScanlineSize();
5542 bool inter = true;
5543 InitDirectoryResolution(b, image, imDesc);
5544 if (tiled)
5545 {
5546 Console.WriteLine("Opening tiles.");
5547 if (vips)
5548 OpenVips(b);
5549 for (int t = 0; t < b.SizeT; t++)
5550 {
5551 for (int c = 0; c < b.SizeC; c++)
5552 {
5553 for (int z = 0; z < b.SizeZ; z++)
5554 {
5555 Bitmap bmp = b.GetTile(b.GetFrameIndex(z, c, t), b.level, tileX, tileY, tileSizeX, tileSizeY).Result;
5556 b.Buffers.Add(bmp);
5557 try { bmp.Stats = Statistics.FromBytes(bmp); } catch { }
5558 }
5559 }
5560 }
5561 Console.WriteLine("Calculating statisitics.");
5562 }
5563 else
5564 {
5565 for (int p = series * pages; p < (series + 1) * pages; p++)
5566 {
5567 image.SetDirectory((short)p);
5568
5569 int rowStride = Math.Max(stride, str);
5570 byte[] bytes = new byte[rowStride * SizeY];
5571 for (int im = 0, offset = 0; im < SizeY; im++)
5572 {
5573 image.ReadScanline(bytes, offset, im, 0);
5574 offset += rowStride;
5575 }
5576 Bitmap inf = new Bitmap(file, SizeX, SizeY, b.Resolutions[series].PixelFormat, bytes, new ZCT(0, 0, 0), p, null, b.littleEndian, inter);
5577 b.Buffers.Add(inf);
5578 try { inf.Stats = Statistics.FromBytes(inf); } catch { }
5579 }
5580 }
5581 image.Close();
5582 b.UpdateCoords();
5583 }
5584 else
5585 {
5586 Gdk.Pixbuf pf = new Gdk.Pixbuf(file);
5587 b.littleEndian = BitConverter.IsLittleEndian;
5588 PixelFormat px = GetPixelFormat(pf.NChannels, pf.BitsPerSample);
5589 b.Resolutions.Add(new Resolution(pf.Width, pf.Height, px, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 96 * (1 / 2.54) / 1000, 0, 0, 0));
5590 b.Buffers.Add(new Bitmap(pf.Width, pf.Height, pf.Width * pf.NChannels, px, pf.Pixels));
5591 b.Buffers.Last().ID = Bitmap.CreateID(file, 0);
5592 b.Buffers.Last().Stats = Statistics.FromBytes(b.Buffers.Last());
5593 b.Channels.Add(new Channel(0, b.bitsPerPixel, b.RGBChannelCount));
5594 b.Coords = new int[1, 1, 1];
5595 b.sizeC = 1;
5596 b.sizeT = 1;
5597 b.sizeZ = 1;
5598 }
5599 /*
5600 if (b.StageSizeX == -1)
5601 {
5602 b.imageInfo.Series = 0;
5603 b.stageSizeX = 0;
5604 b.stageSizeY = 0;
5605 b.stageSizeZ = 0;
5606 }
5607 */
5608 b.Volume = new VolumeD(new Point3D(b.StageSizeX, b.StageSizeY, b.StageSizeZ), new Point3D(b.PhysicalSizeX * b.SizeX, b.PhysicalSizeY * b.SizeY, b.PhysicalSizeZ * b.SizeZ));
5609
5610 //If file is ome and we have OME support then check for annotation in metadata.
5611 if (ome)
5612 {
5613 b.Annotations.AddRange(OpenOMEROIs(file, series));
5614 }
5615 AutoThreshold(b, false);
5616 if (b.bitsPerPixel > 8)
5617 b.StackThreshold(true);
5618 else
5619 b.StackThreshold(false);
5620 if (addToImages)
5621 Images.AddImage(b);
5622 //pr.Close();
5623 //pr.Dispose();
5624 st.Stop();
5625 b.loadTimeMS = st.ElapsedMilliseconds;
5626 Console.WriteLine("BioImage loaded " + b.ToString());
5627 Recorder.Record($"BioImage.OpenFile(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {series}, {tab.ToString().ToLowerInvariant()}, {addToImages.ToString().ToLowerInvariant()}, {tile.ToString().ToLowerInvariant()}, {tileX}, {tileY}, {tileSizeX}, {tileSizeY});");
5628 return b;
5629 }
static void OpenVips(BioImage b)
The function "OpenVips" takes a BioImage object and an integer representing the number of pages,...
Definition Bio.cs:7444
static void InitDirectoryResolution(BioImage b, Tiff image, ImageJDesc jdesc=null)
The function InitDirectoryResolution initializes the resolution properties of a BioImage object based...
Definition Bio.cs:5193
static bool VipsSupport(string file)
The function checks if a given file is supported by the Vips library and returns true if it is,...
Definition Bio.cs:10785
static bool IsTiffTiled(string imagePath)
Definition Bio.cs:5167
Definition ROI.cs:26

◆ OpenFile() [3/3]

static async Task< BioImage > OpenFile ( string file,
ZCT coord = new ZCT() )
static

This function opens a file and returns a BioImage object.

Parameters
fileThe path to the file to open.
Returns
A BioImage object.
5146 {
5147 if (file.StartsWith("https://") || file.StartsWith("s3://"))
5148 return await OpenURL(file, coord, 0, 0, 800, 600).ConfigureAwait(false);
5149 return await OpenFileAsync(file, 0, true, true).ConfigureAwait(false);
5150 }

◆ OpenFileAsync()

static async Task< BioImage > OpenFileAsync ( string file,
int series,
bool tab,
bool addToImages )
static

It opens a TIFF file and returns a BioImage object.

Parameters
filethe file path
seriesthe series number of the image to open
Returns
A BioImage object.
5164 {
5165 return await OpenFile(file, series, tab, addToImages, false, 0, 0, 800, 600);
5166 }

◆ OpenOME() [1/3]

static BioImage OpenOME ( string file,
bool tab )
static

The function "OpenOME" opens a bioimage file in the OME format and returns the first image in the series.

Parameters
fileThe "file" parameter is a string that represents the file path or name of the OME file that you want to open.
tabThe "tab" parameter is a boolean value that determines whether the image is opened in a new tab.
Returns
The method is returning a BioImage object.
7138 {
7139 return OpenOMESeries(file, tab, true)[0];
7140 }
static BioImage[] OpenOMESeries(string file, bool tab, bool addToImages)
It opens a file, checks if it's tiled, if it is, it opens it as a tiled image, if not,...
Definition Bio.cs:9867

◆ OpenOME() [2/3]

static BioImage OpenOME ( string file,
int serie )
static

‍OpenOME(string file, int serie)

The first parameter is a string, the second is an integer

Parameters
filethe path to the file
seriethe image series to open
Returns
A BioImage object.
7150 {
7151 return OpenOME(file, serie, true, false, false, 0, 0, 0, 0);
7152 }

◆ OpenOME() [3/3]

static BioImage OpenOME ( string file,
int serie,
bool tab,
bool addToImages,
bool tile,
int tilex,
int tiley,
int tileSizeX,
int tileSizeY,
bool useOpenSlide = true )
static
7696 {
7697 if (file == null || file == "")
7698 throw new InvalidDataException("File is empty or null");
7699 //We wait incase OME has not initialized.
7700 do
7701 {
7702 Thread.Sleep(10);
7703 } while (!initialized);
7704 Console.WriteLine("OpenOME " + file);
7705 reader = new ImageReader();
7706 if (tileSizeX == 0)
7707 tileSizeX = 1920;
7708 if (tileSizeY == 0)
7709 tileSizeY = 1080;
7710 progFile = file;
7711 BioImage b = new BioImage(file);
7712 b.Loading = true;
7713 if (b.meta == null)
7714 b.meta = service.createOMEXMLMetadata();
7715 string f = file.Replace("\\", "/");
7716 string cf = reader.getCurrentFile();
7717 if (cf != null)
7718 cf = cf.Replace("\\", "/");
7719 if (cf != f)
7720 {
7721 reader.close();
7722 reader.setMetadataStore(b.meta);
7723 reader.setOriginalMetadataPopulated(true);
7724 reader.setMetadataFiltered(false);
7725 try
7726 {
7727 Status = "Opening file " + b.Filename;
7728 reader.setId(f);
7729 }
7730 catch (Exception e)
7731 {
7732 Console.WriteLine(e.Message);
7733 return null;
7734 }
7735 }
7736
7737 //status = "Reading OME Metadata.";
7738 reader.setSeries(serie);
7739 int RGBChannelCount = reader.getRGBChannelCount();
7740 //OME reader.getBitsPerPixel(); sometimes returns incorrect bits per pixel, like when opening ImageJ images.
7741 //So we check the pixel type from xml metadata and if it fails we use the readers value.
7743 try
7744 {
7745 PixelFormat = GetPixelFormat(RGBChannelCount, b.meta.getPixelsType(serie));
7746 }
7747 catch (Exception)
7748 {
7749 PixelFormat = GetPixelFormat(RGBChannelCount, reader.getBitsPerPixel());
7750 }
7751
7752 b.id = file;
7753 b.file = file;
7754 int SizeX, SizeY;
7755 SizeX = reader.getSizeX();
7756 SizeY = reader.getSizeY();
7757 int SizeZ = reader.getSizeZ();
7758 b.sizeC = reader.getSizeC();
7759 b.sizeZ = reader.getSizeZ();
7760 b.sizeT = reader.getSizeT();
7761 b.littleEndian = reader.isLittleEndian();
7762 b.seriesCount = reader.getSeriesCount();
7763 b.imagesPerSeries = reader.getImageCount();
7764 b.imRead = reader;
7765 List<Resolution> ress = new List<Resolution>();
7766 b.series = serie;
7767 string order = reader.getDimensionOrder();
7768 if (vips && tile)
7769 OpenVips(b);
7770 //Lets get the channels and initialize them
7771 int i = 0;
7772 int sumSamples = 0;
7773 while (true)
7774 {
7775 Status = "Reading channels.";
7776 bool def = false;
7777 try
7778 {
7779 int s = b.meta.getChannelSamplesPerPixel(serie, i).getNumberValue().intValue();
7780 var px = b.meta.getPixelsType(serie);
7781 PixelFormat pf = GetPixelFormat(s, px);
7782 Channel ch = null;
7783 if(pf == PixelFormat.Format8bppIndexed)
7784 {
7785 ch = new Channel(i, 8, s);
7786 }
7787 else if (pf == PixelFormat.Format16bppGrayScale)
7788 {
7789 ch = new Channel(i, 16, s);
7790 }
7791 else if (pf == PixelFormat.Format24bppRgb)
7792 {
7793 ch = new Channel(i, 8, s);
7794 }
7795 if (pf == PixelFormat.Format32bppArgb)
7796 {
7797 ch = new Channel(i, 8, s);
7798 }
7799 else if (pf == PixelFormat.Format48bppRgb)
7800 {
7801 ch = new Channel(i, 16, s);
7802 }
7803 else if (pf == PixelFormat.Float)
7804 {
7805 ch = new Channel(i, 32, s);
7806 }
7807 ch.SamplesPerPixel = s;
7808 sumSamples += s;
7809 def = true;
7810 b.Channels.Add(ch);
7811 if (i == 0)
7812 {
7813 b.rgbChannels[0] = 0;
7814 }
7815 else
7816 if (i == 1)
7817 {
7818 b.rgbChannels[1] = 1;
7819 }
7820 else
7821 if (i == 2)
7822 {
7823 b.rgbChannels[2] = 2;
7824 }
7825 //If this channel is not defined we have loaded all the channels in the file.
7826 if (!def)
7827 break;
7828 if (b.meta.getChannelName(serie, i) != null)
7829 ch.Name = b.meta.getChannelName(serie, i);
7830 if (b.meta.getChannelAcquisitionMode(serie, i) != null)
7831 ch.AcquisitionMode = b.meta.getChannelAcquisitionMode(serie, i).ToString();
7832 if (b.meta.getChannelID(serie, i) != null)
7833 ch.info.ID = b.meta.getChannelID(serie, i);
7834 if (b.meta.getChannelFluor(serie, i) != null)
7835 ch.Fluor = b.meta.getChannelFluor(serie, i);
7836 if (b.meta.getChannelColor(serie, i) != null)
7837 {
7838 ome.xml.model.primitives.Color cc = b.meta.getChannelColor(serie, i);
7839 ch.Color = Color.FromArgb(cc.getRed(), cc.getGreen(), cc.getBlue());
7840 }
7841 if (b.meta.getChannelIlluminationType(serie, i) != null)
7842 ch.IlluminationType = b.meta.getChannelIlluminationType(serie, i).ToString();
7843 if (b.meta.getChannelContrastMethod(serie, i) != null)
7844 ch.ContrastMethod = b.meta.getChannelContrastMethod(serie, i).ToString();
7845 if (b.meta.getChannelEmissionWavelength(serie, i) != null)
7846 ch.Emission = b.meta.getChannelEmissionWavelength(serie, i).value().intValue();
7847 if (b.meta.getChannelExcitationWavelength(serie, i) != null)
7848 ch.Excitation = b.meta.getChannelExcitationWavelength(serie, i).value().intValue();
7849 if (b.meta.getLightEmittingDiodePower(serie, i) != null)
7850 ch.LightSourceIntensity = b.meta.getLightEmittingDiodePower(serie, i).value().doubleValue();
7851 if (b.meta.getLightEmittingDiodeID(serie, i) != null)
7852 ch.DiodeName = b.meta.getLightEmittingDiodeID(serie, i);
7853 if (b.meta.getChannelLightSourceSettingsAttenuation(serie, i) != null)
7854 ch.LightSourceAttenuation = b.meta.getChannelLightSourceSettingsAttenuation(serie, i).toString();
7855 }
7856 catch (Exception e)
7857 {
7858 Console.WriteLine(e.Message);
7859 if (!def)
7860 break;
7861 }
7862 i++;
7863 }
7864 try
7865 {
7866 if (b.meta.getObjectiveNominalMagnification(serie, 0) != null)
7867 {
7868 b.Magnification = b.meta.getObjectiveNominalMagnification(serie, 0).intValue();
7869 }
7870 }
7871 catch (Exception e)
7872 {
7873 Console.WriteLine(e.Message);
7874 }
7875
7876 //If the file doens't have channels we initialize them.
7877 if (b.Channels.Count == 0)
7878 {
7879 b.Channels.Add(new Channel(0, b.bitsPerPixel, RGBChannelCount));
7880 }
7881 try
7882 {
7883 Status = "Reading wells.";
7884 int wells = b.meta.getWellCount(0);
7885 if (wells > 0)
7886 {
7887 b.Plate = new WellPlate(b);
7888 tile = false;
7889 }
7890 }
7891 catch (Exception e)
7892 {
7893 //This file is not a well plate.
7894 Console.WriteLine(e.Message);
7895 }
7896 if (reader.getResolutionCount() > 0)
7897 ress.AddRange(GetResolutions(b));
7898 Console.WriteLine("Done reading resolutions.");
7899 Console.WriteLine("Determining pyramidal levels.");
7900 if (ress.Count > 1 && b.Type != ImageType.well && ress[0].SizeX > ress[1].SizeX)
7901 tile = true;
7902 if (b.Type == ImageType.pyramidal)
7903 {
7904 Console.WriteLine("Determining Label and Macro resolutions.");
7905 int pyramidEnd = 0;
7906 while (pyramidEnd + 1 < ress.Count)
7907 {
7908 var cur = ress[pyramidEnd];
7909 var next = ress[pyramidEnd + 1];
7910 if (cur.SizeX > next.SizeX &&
7911 cur.SizeY > next.SizeY &&
7912 cur.PixelFormat == next.PixelFormat)
7913 {
7914 pyramidEnd++;
7915 }
7916 else
7917 {
7918 break;
7919 }
7920 }
7921
7922 for (int r = 0; r <= pyramidEnd; r++)
7923 {
7924 b.Resolutions.Add(ress[r]);
7925 }
7926 }
7927 if (b.Resolutions.Count == 0)
7928 b.Resolutions.AddRange(ress);
7929 b.PyramidNativeLevelCount = b.Resolutions.Count;
7930 AppendSyntheticPyramidResolutions(b);
7931 try
7932 {
7933 string s = b.meta.getStageLabelName(serie);
7934 if (s != null)
7935 {
7936 Resolution res = new Resolution(b.SizeX, b.SizeY, PixelFormat, b.PhysicalSizeX, b.PhysicalSizeY, b.PhysicalSizeZ,
7937 b.meta.getStageLabelX(serie).value().doubleValue(),
7938 b.meta.getStageLabelY(serie).value().doubleValue(),
7939 b.meta.getStageLabelZ(serie).value().doubleValue());
7940 }
7941 }
7942 catch (Exception e)
7943 {
7944 Console.WriteLine("No Stage Cooordinates");
7945 }
7946
7947 b.Volume = new VolumeD(new Point3D(b.StageSizeX, b.StageSizeY, b.StageSizeZ), new Point3D(b.PhysicalSizeX * SizeX, b.PhysicalSizeY * SizeY, b.PhysicalSizeZ * SizeZ));
7948 int rc = b.meta.getROICount();
7949 for (int im = 0; im < rc; im++)
7950 {
7951 string roiID = b.meta.getROIID(im);
7952 string roiName = b.meta.getROIName(im);
7953 ZCT co = new ZCT(0, 0, 0);
7954 int scount = 1;
7955 try
7956 {
7957 scount = b.meta.getShapeCount(im);
7958 }
7959 catch (Exception e)
7960 {
7961 Console.WriteLine(e.Message.ToString());
7962 }
7963 for (int sc = 0; sc < scount; sc++)
7964 {
7965 Status = "Reading ROI " + (sc + 1).ToString() + "/" + scount;
7966 co = new ZCT(0, 0, 0);
7967 string typ;
7968 try
7969 {
7970 typ = b.meta.getShapeType(im, sc);
7971 }
7972 catch
7973 {
7974 typ = null;
7975 }
7976 if (string.IsNullOrWhiteSpace(typ))
7977 {
7978 try
7979 {
7980 byte[] maskProbe = b.meta.getMaskBinData(im, sc);
7981 if (maskProbe != null && maskProbe.Length > 0)
7982 typ = "Mask";
7983 }
7984 catch
7985 {
7986 // ignore and fall through
7987 }
7988 }
7989 if (string.IsNullOrWhiteSpace(typ))
7990 continue;
7991 ROI an = new ROI();
7992 an.roiID = roiID;
7993 an.roiName = roiName;
7994 an.shapeIndex = sc;
7995 if (typ == "Point")
7996 {
7997 an.type = ROI.Type.Point;
7998 an.id = b.meta.getPointID(im, sc);
7999 double dx = b.meta.getPointX(im, sc).doubleValue();
8000 double dy = b.meta.getPointY(im, sc).doubleValue();
8001 an.AddPoint(b.ToStageSpace(new PointD(dx, dy)));
8002 an.coord = new ZCT();
8003 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getPointTheZ(im, sc);
8004 if (nz != null)
8005 an.coord.Z = nz.getNumberValue().intValue();
8006 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getPointTheC(im, sc);
8007 if (nc != null)
8008 an.coord.C = nc.getNumberValue().intValue();
8009 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getPointTheT(im, sc);
8010 if (nt != null)
8011 an.coord.T = nt.getNumberValue().intValue();
8012 an.Text = b.meta.getPointText(im, sc);
8013 ome.units.quantity.Length fl = b.meta.getPointFontSize(im, sc);
8014 if (fl != null)
8015 an.fontSize = fl.value().intValue();
8016 ome.xml.model.enums.FontFamily ff = b.meta.getPointFontFamily(im, sc);
8017 if (ff != null)
8018 an.family = ff.name();
8019 ome.xml.model.primitives.Color col = b.meta.getPointStrokeColor(im, sc);
8020 if (col != null)
8021 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8022 ome.units.quantity.Length fw = b.meta.getPointStrokeWidth(im, sc);
8023 if (fw != null)
8024 an.strokeWidth = (float)fw.value().floatValue();
8025 ome.xml.model.primitives.Color colf = b.meta.getPointStrokeColor(im, sc);
8026 if (colf != null)
8027 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8028 }
8029 else
8030 if (typ == "Line")
8031 {
8032 an.type = ROI.Type.Line;
8033 an.id = b.meta.getLineID(im, sc);
8034 double px1 = b.meta.getLineX1(im, sc).doubleValue();
8035 double py1 = b.meta.getLineY1(im, sc).doubleValue();
8036 double px2 = b.meta.getLineX2(im, sc).doubleValue();
8037 double py2 = b.meta.getLineY2(im, sc).doubleValue();
8038 List<PointD> pds = new List<PointD>();
8039 pds.Add(b.ToStageSpace(new PointD(px1, py1)));
8040 pds.Add(b.ToStageSpace(new PointD(px2, py2)));
8041 an.Points = pds;
8042 an.UpdateBoundingBox();
8043 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getLineTheZ(im, sc);
8044 if (nz != null)
8045 co.Z = nz.getNumberValue().intValue();
8046 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getLineTheC(im, sc);
8047 if (nc != null)
8048 co.C = nc.getNumberValue().intValue();
8049 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getLineTheT(im, sc);
8050 if (nt != null)
8051 co.T = nt.getNumberValue().intValue();
8052 an.coord = new ZCT(co.Z, co.C, co.T);
8053 an.Text = b.meta.getLineText(im, sc);
8054 ome.units.quantity.Length fl = b.meta.getLineFontSize(im, sc);
8055 if (fl != null)
8056 an.fontSize = fl.value().intValue();
8057 ome.xml.model.enums.FontFamily ff = b.meta.getLineFontFamily(im, sc);
8058 if (ff != null)
8059 an.family = ff.name();
8060 ome.xml.model.primitives.Color col = b.meta.getLineStrokeColor(im, sc);
8061 if (col != null)
8062 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8063 ome.units.quantity.Length fw = b.meta.getLineStrokeWidth(im, sc);
8064 if (fw != null)
8065 an.strokeWidth = (float)fw.value().floatValue();
8066 ome.xml.model.primitives.Color colf = b.meta.getLineFillColor(im, sc);
8067 if (colf != null)
8068 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8069 }
8070 else
8071 if (typ == "Rectangle")
8072 {
8073 an.type = ROI.Type.Rectangle;
8074 an.id = b.meta.getRectangleID(im, sc);
8075 double px = b.meta.getRectangleX(im, sc).doubleValue();
8076 double py = b.meta.getRectangleY(im, sc).doubleValue();
8077 double pw = b.meta.getRectangleWidth(im, sc).doubleValue();
8078 double ph = b.meta.getRectangleHeight(im, sc).doubleValue();
8079 an.BoundingBox = b.ToStageSpace(new RectangleD(px, py, pw, ph));
8080 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getRectangleTheZ(im, sc);
8081 if (nz != null)
8082 co.Z = nz.getNumberValue().intValue();
8083 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getRectangleTheC(im, sc);
8084 if (nc != null)
8085 co.C = nc.getNumberValue().intValue();
8086 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getRectangleTheT(im, sc);
8087 if (nt != null)
8088 co.T = nt.getNumberValue().intValue();
8089 an.coord = new ZCT(co.Z, co.C, co.T);
8090
8091 an.Text = b.meta.getRectangleText(im, sc);
8092 ome.units.quantity.Length fl = b.meta.getRectangleFontSize(im, sc);
8093 if (fl != null)
8094 an.fontSize = fl.value().intValue();
8095 ome.xml.model.enums.FontFamily ff = b.meta.getRectangleFontFamily(im, sc);
8096 if (ff != null)
8097 an.family = ff.name();
8098 ome.xml.model.primitives.Color col = b.meta.getRectangleStrokeColor(im, sc);
8099 if (col != null)
8100 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8101 ome.units.quantity.Length fw = b.meta.getRectangleStrokeWidth(im, sc);
8102 if (fw != null)
8103 an.strokeWidth = (float)fw.value().floatValue();
8104 ome.xml.model.primitives.Color colf = b.meta.getRectangleFillColor(im, sc);
8105 if (colf != null)
8106 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8107 ome.xml.model.enums.FillRule fr = b.meta.getRectangleFillRule(im, sc);
8108
8109 }
8110 else
8111 if (typ == "Ellipse")
8112 {
8113 an.type = ROI.Type.Ellipse;
8114 an.id = b.meta.getEllipseID(im, sc);
8115 double px = b.meta.getEllipseX(im, sc).doubleValue();
8116 double py = b.meta.getEllipseY(im, sc).doubleValue();
8117 double ew = b.meta.getEllipseRadiusX(im, sc).doubleValue();
8118 double eh = b.meta.getEllipseRadiusY(im, sc).doubleValue();
8119 //We convert the ellipse radius to Rectangle
8120 double w = ew * 2;
8121 double h = eh * 2;
8122 double x = px - ew;
8123 double y = py - eh;
8124 an.BoundingBox = b.ToStageSpace(new RectangleD(x, y, w, h));
8125 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getEllipseTheZ(im, sc);
8126 if (nz != null)
8127 co.Z = nz.getNumberValue().intValue();
8128 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getEllipseTheC(im, sc);
8129 if (nc != null)
8130 co.C = nc.getNumberValue().intValue();
8131 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getEllipseTheT(im, sc);
8132 if (nt != null)
8133 co.T = nt.getNumberValue().intValue();
8134 an.coord = new ZCT(co.Z, co.C, co.T);
8135 an.Text = b.meta.getEllipseText(im, sc);
8136 ome.units.quantity.Length fl = b.meta.getEllipseFontSize(im, sc);
8137 if (fl != null)
8138 an.fontSize = fl.value().intValue();
8139 ome.xml.model.enums.FontFamily ff = b.meta.getEllipseFontFamily(im, sc);
8140 if (ff != null)
8141 an.family = ff.name();
8142 ome.xml.model.primitives.Color col = b.meta.getEllipseStrokeColor(im, sc);
8143 if (col != null)
8144 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8145 ome.units.quantity.Length fw = b.meta.getEllipseStrokeWidth(im, sc);
8146 if (fw != null)
8147 an.strokeWidth = (float)fw.value().floatValue();
8148 ome.xml.model.primitives.Color colf = b.meta.getEllipseFillColor(im, sc);
8149 if (colf != null)
8150 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8151 }
8152 else
8153 if (typ == "Polygon")
8154 {
8155 an.type = ROI.Type.Polygon;
8156 an.id = b.meta.getPolygonID(im, sc);
8157 an.closed = true;
8158 string pxs = b.meta.getPolygonPoints(im, sc);
8159 PointD[] pts = an.stringToPoints(pxs);
8160 pts = b.ToStageSpace(pts);
8161 if (pts.Length > 100)
8162 {
8163 an.type = ROI.Type.Freeform;
8164 }
8165 an.AddPoints(pts);
8166 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getPolygonTheZ(im, sc);
8167 if (nz != null)
8168 co.Z = nz.getNumberValue().intValue();
8169 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getPolygonTheC(im, sc);
8170 if (nc != null)
8171 co.C = nc.getNumberValue().intValue();
8172 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getPolygonTheT(im, sc);
8173 if (nt != null)
8174 co.T = nt.getNumberValue().intValue();
8175 an.coord = new ZCT(co.Z, co.C, co.T);
8176 an.Text = b.meta.getPolygonText(im, sc);
8177 ome.units.quantity.Length fl = b.meta.getPolygonFontSize(im, sc);
8178 if (fl != null)
8179 an.fontSize = fl.value().intValue();
8180 ome.xml.model.enums.FontFamily ff = b.meta.getPolygonFontFamily(im, sc);
8181 if (ff != null)
8182 an.family = ff.name();
8183 ome.xml.model.primitives.Color col = b.meta.getPolygonStrokeColor(im, sc);
8184 if (col != null)
8185 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8186 ome.units.quantity.Length fw = b.meta.getPolygonStrokeWidth(im, sc);
8187 if (fw != null)
8188 an.strokeWidth = (float)fw.value().floatValue();
8189 ome.xml.model.primitives.Color colf = b.meta.getPolygonFillColor(im, sc);
8190 if (colf != null)
8191 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8192 }
8193 else
8194 if (typ == "Polyline")
8195 {
8196 an.type = ROI.Type.Polyline;
8197 an.id = b.meta.getPolylineID(im, sc);
8198 string pxs = b.meta.getPolylinePoints(im, sc);
8199 PointD[] pts = an.stringToPoints(pxs);
8200 for (int pi = 0; pi < pts.Length; pi++)
8201 {
8202 pts[pi] = b.ToStageSpace(pts[pi]);
8203 }
8204 an.AddPoints(pts);
8205 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getPolylineTheZ(im, sc);
8206 if (nz != null)
8207 co.Z = nz.getNumberValue().intValue();
8208 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getPolylineTheC(im, sc);
8209 if (nc != null)
8210 co.C = nc.getNumberValue().intValue();
8211 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getPolylineTheT(im, sc);
8212 if (nt != null)
8213 co.T = nt.getNumberValue().intValue();
8214 an.coord = new ZCT(co.Z, co.C, co.T);
8215 an.Text = b.meta.getPolylineText(im, sc);
8216 ome.units.quantity.Length fl = b.meta.getPolylineFontSize(im, sc);
8217 if (fl != null)
8218 an.fontSize = fl.value().intValue();
8219 ome.xml.model.enums.FontFamily ff = b.meta.getPolylineFontFamily(im, sc);
8220 if (ff != null)
8221 an.family = ff.name();
8222 ome.xml.model.primitives.Color col = b.meta.getPolylineStrokeColor(im, sc);
8223 if (col != null)
8224 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8225 ome.units.quantity.Length fw = b.meta.getPolylineStrokeWidth(im, sc);
8226 if (fw != null)
8227 an.strokeWidth = (float)fw.value().floatValue();
8228 ome.xml.model.primitives.Color colf = b.meta.getPolylineFillColor(im, sc);
8229 if (colf != null)
8230 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8231 }
8232 else
8233 if (typ == "Label")
8234 {
8235 an.type = ROI.Type.Label;
8236 an.id = b.meta.getLabelID(im, sc);
8237 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getLabelTheZ(im, sc);
8238 if (nz != null)
8239 co.Z = nz.getNumberValue().intValue();
8240 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getLabelTheC(im, sc);
8241 if (nc != null)
8242 co.C = nc.getNumberValue().intValue();
8243 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getLabelTheT(im, sc);
8244 if (nt != null)
8245 co.T = nt.getNumberValue().intValue();
8246 an.coord = new ZCT(co.Z, co.C, co.T);
8247
8248 ome.units.quantity.Length fl = b.meta.getLabelFontSize(im, sc);
8249 if (fl != null)
8250 an.fontSize = fl.value().intValue();
8251 ome.xml.model.enums.FontFamily ff = b.meta.getLabelFontFamily(im, sc);
8252 if (ff != null)
8253 an.family = ff.name();
8254 ome.xml.model.primitives.Color col = b.meta.getLabelStrokeColor(im, sc);
8255 if (col != null)
8256 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8257 ome.units.quantity.Length fw = b.meta.getLabelStrokeWidth(im, sc);
8258 if (fw != null)
8259 an.strokeWidth = (float)fw.value().floatValue();
8260 ome.xml.model.primitives.Color colf = b.meta.getLabelFillColor(im, sc);
8261 if (colf != null)
8262 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8263 PointD p = new PointD(b.meta.getLabelX(im, sc).doubleValue(), b.meta.getLabelY(im, sc).doubleValue());
8264 an.UpdatePoint(b.ToStageSpace(p),0);
8265 an.Text = b.meta.getLabelText(im, sc);
8266 }
8267 else
8268 if (typ == "Mask")
8269 {
8270 byte[] bts = b.meta.getMaskBinData(im, sc);
8271 bool end = b.meta.getMaskBinDataBigEndian(im, sc).booleanValue();
8272 double h = b.meta.getMaskHeight(im, sc).doubleValue();
8273 double w = b.meta.getMaskWidth(im, sc).doubleValue();
8274 double x = b.meta.getMaskX(im, sc).doubleValue();
8275 double y = b.meta.getMaskY(im, sc).doubleValue();
8276 if (bts == null || bts.Length == 0)
8277 continue;
8278 var maskDimensions = GetMaskDimensionsForOmeMask(b, bts, w, h);
8279 int maskWidth = maskDimensions.Width;
8280 int maskHeight = maskDimensions.Height;
8281 if (maskWidth <= 0 || maskHeight <= 0)
8282 {
8283 continue;
8284 }
8285 double physX = w > 0 ? w / maskWidth : (b.PhysicalSizeX > 0 ? b.PhysicalSizeX : 1.0);
8286 double physY = h > 0 ? h / maskHeight : (b.PhysicalSizeY > 0 ? b.PhysicalSizeY : 1.0);
8287 PointD stageLoc = b.ToStageSpace(new PointD(x, y));
8288 an = ROI.CreateMask(
8289 co,
8290 bts,
8291 maskWidth,
8292 maskHeight,
8293 stageLoc,
8294 physX,
8295 physY,
8296 preserveDimensions: true);
8297 an.Text = b.meta.getMaskText(im, sc);
8298 an.id = b.meta.getMaskID(im, sc);
8299 an.UpdateBoundingBox();
8300 ome.xml.model.primitives.NonNegativeInteger nz = b.meta.getMaskTheZ(im, sc);
8301 if (nz != null)
8302 co.Z = nz.getNumberValue().intValue();
8303 ome.xml.model.primitives.NonNegativeInteger nc = b.meta.getMaskTheC(im, sc);
8304 if (nc != null)
8305 co.C = nc.getNumberValue().intValue();
8306 ome.xml.model.primitives.NonNegativeInteger nt = b.meta.getMaskTheT(im, sc);
8307 if (nt != null)
8308 co.T = nt.getNumberValue().intValue();
8309 an.coord = new ZCT(co.Z, co.C, co.T);
8310
8311 ome.units.quantity.Length fl = b.meta.getMaskFontSize(im, sc);
8312 if (fl != null)
8313 an.fontSize = fl.value().intValue();
8314 ome.xml.model.enums.FontFamily ff = b.meta.getMaskFontFamily(im, sc);
8315 if (ff != null)
8316 an.family = ff.name();
8317 ome.xml.model.primitives.Color col = b.meta.getMaskStrokeColor(im, sc);
8318 if (col != null)
8319 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
8320 ome.units.quantity.Length fw = b.meta.getMaskStrokeWidth(im, sc);
8321 if (fw != null)
8322 an.strokeWidth = (float)fw.value().floatValue();
8323 ome.xml.model.primitives.Color colf = b.meta.getMaskFillColor(im, sc);
8324 if (colf != null)
8325 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
8326 }
8327 b.Annotations.Add(an);
8328 }
8329 }
8330
8331 if (b.Annotations.Count == 0)
8332 {
8333 try
8334 {
8335 b.Annotations.AddRange(OpenOMEROIs(file, serie));
8336 }
8337 catch (Exception e)
8338 {
8339 Console.WriteLine("Fallback ROI import failed: " + e.Message);
8340 }
8341 }
8342
8343 List<string> serFiles = new List<string>();
8344 serFiles.AddRange(reader.getSeriesUsedFiles());
8345
8346 b.Buffers = new List<Bitmap>();
8347 if (b.Type == ImageType.pyramidal)
8348 {
8349
8350 try
8351 {
8352
8353 string st = OpenSlideImage.DetectVendor(file);
8354 if (st != null && !file.EndsWith("ome.tif") && useOpenSlide)
8355 {
8356 Status = "Opening file with OpenSlide.";
8357 Console.WriteLine("Opening file with OpenSlide.");
8358 b.openSlideImage = OpenSlideImage.Open(file);
8359 b.openslideBase = (OpenSlideBase)OpenSlideGTK.SlideSourceBase.Create(file, true);
8360 b.openslideBase.Image = b.openSlideImage;
8361 }
8362 else
8363 {
8364 Status = "Opening file with BioFormats.";
8365 Console.WriteLine("Opening file with BioFormats.");
8367 b.slideBase = (SlideBase)SlideBase.Create(b, sm);
8368 b.slideBase.Image = sm;
8369 }
8370 }
8371 catch (Exception e)
8372 {
8373 Console.WriteLine(e.Message.ToString());
8374 b.slideBase = (SlideBase)SlideBase.Create(b, SlideImage.Open(b));
8375 }
8376 tile = true;
8377 }
8378 // read the image data bytes
8379 reader.setSeries(serie);
8380 int pages = reader.getImageCount();
8381 bool inter = reader.isInterleaved();
8382 int z = 0;
8383 int c = 0;
8384 int t = 0;
8385 if (!tile)
8386 {
8387 Status = "Reading image planes.";
8388 try
8389 {
8390 int rgbBytesPerPixel =
8391 PixelFormat == PixelFormat.Format48bppRgb ? 6 :
8392 PixelFormat == PixelFormat.Format24bppRgb ? 3 : 1;
8393 for (int p = 0; p < pages; p++)
8394 {
8395 Progress = ((float)p / (float)pages) * 100;
8396 Bitmap bf;
8397 byte[] bytes = reader.openBytes(p);
8398 if (PixelFormat == PixelFormat.Format48bppRgb)
8399 {
8400 inter = true;
8401 }
8402 else if (PixelFormat == PixelFormat.Format24bppRgb &&
8403 bytes.Length > SizeX * SizeY * rgbBytesPerPixel)
8404 {
8405 inter = true;
8406 }
8407 bf = new Bitmap(file, SizeX, SizeY, PixelFormat, bytes, new ZCT(z, c, t), p, null, b.littleEndian, inter);
8408 b.Buffers.Add(bf);
8409 }
8410 }
8411 catch (Exception ex)
8412 {
8413 Console.WriteLine(ex.Message);
8414 }
8415
8416 }
8417 else
8418 {
8419 Status = "Reading tiles.";
8420 b.imRead = reader;
8421 for (int p = 0; p < pages; p++)
8422 {
8423 Progress = ((float)p / (float)pages) * 100;
8424 b.Buffers.Add(b.GetTile(p, b.Level, tilex, tiley, tileSizeX, tileSizeY).Result);
8425 }
8426 }
8427 int pls;
8428 try
8429 {
8430 pls = b.meta.getPlaneCount(serie);
8431 }
8432 catch (Exception)
8433 {
8434 pls = 0;
8435 }
8436 if (pls == b.Buffers.Count)
8437 for (int bi = 0; bi < b.Buffers.Count; bi++)
8438 {
8439 Plane pl = new Plane();
8440 pl.Coordinate = new ZCT();
8441 double px = 0; double py = 0; double pz = 0;
8442 if (b.meta.getPlanePositionX(serie, bi) != null)
8443 px = b.meta.getPlanePositionX(serie, bi).value().doubleValue();
8444 if (b.meta.getPlanePositionY(serie, bi) != null)
8445 py = b.meta.getPlanePositionY(serie, bi).value().doubleValue();
8446 if (b.meta.getPlanePositionZ(serie, bi) != null)
8447 pz = b.meta.getPlanePositionZ(serie, bi).value().doubleValue();
8448 pl.Location = new AForge.Point3D(px, py, pz);
8449 int cc = 0; int zc = 0; int tc = 0;
8450 if (b.meta.getPlaneTheC(serie, bi) != null)
8451 cc = b.meta.getPlaneTheC(serie, bi).getNumberValue().intValue();
8452 if (b.meta.getPlaneTheZ(serie, bi) != null)
8453 zc = b.meta.getPlaneTheZ(serie, bi).getNumberValue().intValue();
8454 if (b.meta.getPlaneTheT(serie, bi) != null)
8455 tc = b.meta.getPlaneTheT(serie, bi).getNumberValue().intValue();
8456 pl.Coordinate = new ZCT(zc, cc, tc);
8457 if (b.meta.getPlaneDeltaT(serie, bi) != null)
8458 pl.Delta = b.meta.getPlaneDeltaT(serie, bi).value().doubleValue();
8459 if (b.meta.getPlaneExposureTime(serie, bi) != null)
8460 pl.Exposure = b.meta.getPlaneExposureTime(serie, bi).value().doubleValue();
8461 b.Buffers[bi].Plane = pl;
8462 }
8463 //Bioformats gives a size of 3 for C when saved in ImageJ as RGB. We need to correct for this as C should be 1 for RGB.
8464 if (RGBChannelCount >= 3)
8465 {
8466 b.sizeC = sumSamples / b.Channels[0].SamplesPerPixel;
8467 }
8468 string ord = reader.getDimensionOrder();
8469 if (ord == "XYZCT")
8470 b.UpdateCoords(b.SizeZ, b.SizeC, b.SizeT, Order.ZCT);
8471 else if (ord == "XYCZT")
8472 b.UpdateCoords(b.SizeZ, b.SizeC, b.SizeT, Order.CZT);
8473 else if (ord == "XYTCZ")
8474 b.UpdateCoords(b.SizeZ, b.SizeC, b.SizeT, Order.TCZ);
8475 AutoThreshold(b, true);
8476 if (b.bitsPerPixel > 8)
8477 b.StackThreshold(true);
8478 else
8479 b.StackThreshold(false);
8480 try
8481 {
8482 if (b.Type == ImageType.stack)
8483 reader.close();
8484 if (addToImages)
8485 Images.AddImage(b);
8486 }
8487 catch (Exception e)
8488 {
8489 Console.WriteLine(e.Message);
8490 }
8491 b.Loading = false;
8492 Recorder.Record($"BioImage.OpenOME(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {serie}, {tab.ToString().ToLowerInvariant()}, {addToImages.ToString().ToLowerInvariant()}, {tile.ToString().ToLowerInvariant()}, {tilex}, {tiley}, {tileSizeX}, {tileSizeY});");
8493 return b;
8494 }
AForge.PointD PointD
Definition Bio.cs:60
AForge.Color Color
Definition Bio.cs:55
override string ToString()
This function returns the filename of the object, and the location of the object in the 3D space.
Definition Bio.cs:11736
AForge.RectangleD RectangleD
Definition Fiji.cs:27
static ROI CreateMask(ZCT coord, float[] mask, int width, int height, PointD loc, double physicalX, double physicalY, bool useFloatStorage=false, bool absolutePosition=false)
Definition ROI.cs:1175
void UpdateBoundingBox()
Definition ROI.cs:1763
PointD[] stringToPoints(string s)
Definition ROI.cs:1726
void AddPoints(PointD[] p, bool convertPixelSpace=false, double physX=1, double physY=1)
Definition ROI.cs:1652
void AddPoint(PointD p, bool convertPixelSpace=false, double physX=1, double physY=1)
Definition ROI.cs:1642
void UpdatePoint(PointD p, int i)
Definition ROI.cs:1592
Type
Definition ROI.cs:29
Definition SlideBase.cs:15
openslide wrapper
Definition SlideImage.cs:24
static SlideImage Open(BioImage b)
Open.
Definition SlideImage.cs:106

◆ OpenOMEROIs()

static List< ROI > OpenOMEROIs ( string file,
int series )
static

It reads the OME-XML file and converts the ROIs to a list of ROI objects.

Parameters
filethe path to the OME-TIFF file
seriesthe series number of the image you want to open
Returns
A list of ROI objects.
10838 {
10839 List<ROI> Annotations = new List<ROI>();
10840 // create OME-XML metadata store
10841 ServiceFactory factory = new ServiceFactory();
10842 OMEXMLService service = (OMEXMLService)factory.getInstance(typeof(OMEXMLService));
10843 loci.formats.ome.OMEXMLMetadata meta = service.createOMEXMLMetadata();
10844 // create format reader
10845 ImageReader imageReader = new ImageReader();
10846 imageReader.setMetadataStore(meta);
10847 imageReader.setOriginalMetadataPopulated(true);
10848 imageReader.setMetadataFiltered(false);
10849 // initialize file
10850 file = file.Replace("\\", "/");
10851 imageReader.setId(file);
10852 int imageCount = imageReader.getImageCount();
10853 int seriesCount = imageReader.getSeriesCount();
10854 double physicalSizeX = 0;
10855 double physicalSizeY = 0;
10856 double physicalSizeZ = 0;
10857 double stageSizeX = 0;
10858 double stageSizeY = 0;
10859 double stageSizeZ = 0;
10860 int SizeX = imageReader.getSizeX();
10861 int SizeY = imageReader.getSizeY();
10862 int SizeZ = imageReader.getSizeY();
10863 try
10864 {
10865 bool hasPhysical = false;
10866 if (meta.getPixelsPhysicalSizeX(series) != null)
10867 {
10868 physicalSizeX = meta.getPixelsPhysicalSizeX(series).value().doubleValue();
10869 hasPhysical = true;
10870 }
10871 if (meta.getPixelsPhysicalSizeY(series) != null)
10872 {
10873 physicalSizeY = meta.getPixelsPhysicalSizeY(series).value().doubleValue();
10874 }
10875 if (meta.getPixelsPhysicalSizeZ(series) != null)
10876 {
10877 physicalSizeZ = meta.getPixelsPhysicalSizeZ(series).value().doubleValue();
10878 }
10879 else
10880 {
10881 physicalSizeZ = 1;
10882 }
10883 if (meta.getStageLabelX(series) != null)
10884 stageSizeX = meta.getStageLabelX(series).value().doubleValue();
10885 if (meta.getStageLabelY(series) != null)
10886 stageSizeY = meta.getStageLabelY(series).value().doubleValue();
10887 if (meta.getStageLabelZ(series) != null)
10888 stageSizeZ = meta.getStageLabelZ(series).value().doubleValue();
10889 else
10890 stageSizeZ = 1;
10891 }
10892 catch (Exception e)
10893 {
10894 stageSizeX = 0;
10895 stageSizeY = 0;
10896 stageSizeZ = 1;
10897 }
10898 VolumeD volume = new VolumeD(new Point3D(stageSizeX, stageSizeY, stageSizeZ), new Point3D(physicalSizeX * SizeX, physicalSizeY * SizeY, physicalSizeZ * SizeZ));
10899 int rc = meta.getROICount();
10900 for (int im = 0; im < rc; im++)
10901 {
10902 string roiID = meta.getROIID(im);
10903 string roiName = meta.getROIName(im);
10904 ZCT co = new ZCT(0, 0, 0);
10905 int scount = 1;
10906 try
10907 {
10908 scount = meta.getShapeCount(im);
10909 }
10910 catch (Exception e)
10911 {
10912 Console.WriteLine(e.Message.ToString());
10913 }
10914 for (int sc = 0; sc < scount; sc++)
10915 {
10916 co = new ZCT(0, 0, 0);
10917 string type;
10918 try
10919 {
10920 type = meta.getShapeType(im, sc);
10921 }
10922 catch
10923 {
10924 type = null;
10925 }
10926 if (string.IsNullOrWhiteSpace(type))
10927 {
10928 try
10929 {
10930 byte[] maskProbe = meta.getMaskBinData(im, sc);
10931 if (maskProbe != null && maskProbe.Length > 0)
10932 type = "Mask";
10933 }
10934 catch
10935 {
10936 // ignore and fall through
10937 }
10938 }
10939 if (string.IsNullOrWhiteSpace(type))
10940 continue;
10941 ROI an = new ROI();
10942 an.roiID = roiID;
10943 an.roiName = roiName;
10944 an.shapeIndex = sc;
10945 if (type == "Point")
10946 {
10947 an.type = ROI.Type.Point;
10948 an.id = meta.getPointID(im, sc);
10949 double dx = meta.getPointX(im, sc).doubleValue();
10950 double dy = meta.getPointY(im, sc).doubleValue();
10951 an.AddPoint(ToStageSpace(new PointD(dx, dy), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y));
10952 an.coord = new ZCT();
10953 ome.xml.model.primitives.NonNegativeInteger nz = meta.getPointTheZ(im, sc);
10954 if (nz != null)
10955 an.coord.Z = nz.getNumberValue().intValue();
10956 ome.xml.model.primitives.NonNegativeInteger nc = meta.getPointTheC(im, sc);
10957 if (nc != null)
10958 an.coord.C = nc.getNumberValue().intValue();
10959 ome.xml.model.primitives.NonNegativeInteger nt = meta.getPointTheT(im, sc);
10960 if (nt != null)
10961 an.coord.T = nt.getNumberValue().intValue();
10962 an.Text = meta.getPointText(im, sc);
10963 ome.units.quantity.Length fl = meta.getPointFontSize(im, sc);
10964 if (fl != null)
10965 an.fontSize = fl.value().intValue();
10966 an.family = meta.getPointFontFamily(im, sc).name();
10967 ome.xml.model.primitives.Color col = meta.getPointStrokeColor(im, sc);
10968 if (col != null)
10969 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
10970 ome.units.quantity.Length fw = meta.getPointStrokeWidth(im, sc);
10971 if (fw != null)
10972 an.strokeWidth = (float)fw.value().floatValue();
10973 ome.xml.model.primitives.Color colf = meta.getPointStrokeColor(im, sc);
10974 if (colf != null)
10975 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
10976 }
10977 else
10978 if (type == "Line")
10979 {
10980 an.type = ROI.Type.Line;
10981 an.id = meta.getLineID(im, sc);
10982 double px1 = meta.getLineX1(im, sc).doubleValue();
10983 double py1 = meta.getLineY1(im, sc).doubleValue();
10984 double px2 = meta.getLineX2(im, sc).doubleValue();
10985 double py2 = meta.getLineY2(im, sc).doubleValue();
10986 an.AddPoint(ToStageSpace(new PointD(px1, py1), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y));
10987 an.AddPoint(ToStageSpace(new PointD(px2, py2), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y));
10988 ome.xml.model.primitives.NonNegativeInteger nz = meta.getLineTheZ(im, sc);
10989 if (nz != null)
10990 co.Z = nz.getNumberValue().intValue();
10991 ome.xml.model.primitives.NonNegativeInteger nc = meta.getLineTheC(im, sc);
10992 if (nc != null)
10993 co.C = nc.getNumberValue().intValue();
10994 ome.xml.model.primitives.NonNegativeInteger nt = meta.getLineTheT(im, sc);
10995 if (nt != null)
10996 co.T = nt.getNumberValue().intValue();
10997 an.coord = new ZCT(co.Z, co.C, co.T);
10998 an.Text = meta.getLineText(im, sc);
10999 ome.units.quantity.Length fl = meta.getLineFontSize(im, sc);
11000 if (fl != null)
11001 an.fontSize = fl.value().intValue();
11002 an.family = meta.getPointFontFamily(im, sc).name();
11003 ome.xml.model.primitives.Color col = meta.getLineStrokeColor(im, sc);
11004 if (col != null)
11005 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11006 ome.units.quantity.Length fw = meta.getLineStrokeWidth(im, sc);
11007 if (fw != null)
11008 an.strokeWidth = (float)fw.value().floatValue();
11009 ome.xml.model.primitives.Color colf = meta.getLineFillColor(im, sc);
11010 if (colf != null)
11011 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11012 }
11013 else
11014 if (type == "Rectangle")
11015 {
11016 an.type = ROI.Type.Rectangle;
11017 an.id = meta.getRectangleID(im, sc);
11018 double px = meta.getRectangleX(im, sc).doubleValue();
11019 double py = meta.getRectangleY(im, sc).doubleValue();
11020 double pw = meta.getRectangleWidth(im, sc).doubleValue();
11021 double ph = meta.getRectangleHeight(im, sc).doubleValue();
11022 an.BoundingBox = ToStageSpace(new RectangleD(px, py, pw, ph), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y);
11023 ome.xml.model.primitives.NonNegativeInteger nz = meta.getRectangleTheZ(im, sc);
11024 if (nz != null)
11025 co.Z = nz.getNumberValue().intValue();
11026 ome.xml.model.primitives.NonNegativeInteger nc = meta.getRectangleTheC(im, sc);
11027 if (nc != null)
11028 co.C = nc.getNumberValue().intValue();
11029 ome.xml.model.primitives.NonNegativeInteger nt = meta.getRectangleTheT(im, sc);
11030 if (nt != null)
11031 co.T = nt.getNumberValue().intValue();
11032 an.coord = new ZCT(co.Z, co.C, co.T);
11033
11034 an.Text = meta.getRectangleText(im, sc);
11035 ome.units.quantity.Length fl = meta.getRectangleFontSize(im, sc);
11036 if (fl != null)
11037 an.fontSize = fl.value().intValue();
11038 an.family = meta.getPointFontFamily(im, sc).name();
11039 ome.xml.model.primitives.Color col = meta.getRectangleStrokeColor(im, sc);
11040 if (col != null)
11041 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11042 ome.units.quantity.Length fw = meta.getRectangleStrokeWidth(im, sc);
11043 if (fw != null)
11044 an.strokeWidth = (float)fw.value().floatValue();
11045 ome.xml.model.primitives.Color colf = meta.getRectangleFillColor(im, sc);
11046 if (colf != null)
11047 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11048 ome.xml.model.enums.FillRule fr = meta.getRectangleFillRule(im, sc);
11049 }
11050 else
11051 if (type == "Ellipse")
11052 {
11053 an.type = ROI.Type.Ellipse;
11054 an.id = meta.getEllipseID(im, sc);
11055 double px = meta.getEllipseX(im, sc).doubleValue();
11056 double py = meta.getEllipseY(im, sc).doubleValue();
11057 double ew = meta.getEllipseRadiusX(im, sc).doubleValue();
11058 double eh = meta.getEllipseRadiusY(im, sc).doubleValue();
11059 //We convert the ellipse radius to Rectangle
11060 double w = ew * 2;
11061 double h = eh * 2;
11062 double x = px - ew;
11063 double y = py - eh;
11064 an.BoundingBox = ToStageSpace(new RectangleD(px, py, w, h), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y);
11065 ome.xml.model.primitives.NonNegativeInteger nz = meta.getEllipseTheZ(im, sc);
11066 if (nz != null)
11067 co.Z = nz.getNumberValue().intValue();
11068 ome.xml.model.primitives.NonNegativeInteger nc = meta.getEllipseTheC(im, sc);
11069 if (nc != null)
11070 co.C = nc.getNumberValue().intValue();
11071 ome.xml.model.primitives.NonNegativeInteger nt = meta.getEllipseTheT(im, sc);
11072 if (nt != null)
11073 co.T = nt.getNumberValue().intValue();
11074 an.coord = new ZCT(co.Z, co.C, co.T);
11075 an.Text = meta.getEllipseText(im, sc);
11076 ome.units.quantity.Length fl = meta.getEllipseFontSize(im, sc);
11077 if (fl != null)
11078 an.fontSize = fl.value().intValue();
11079 an.family = meta.getPointFontFamily(im, sc).name();
11080 ome.xml.model.primitives.Color col = meta.getEllipseStrokeColor(im, sc);
11081 if (col != null)
11082 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11083 ome.units.quantity.Length fw = meta.getEllipseStrokeWidth(im, sc);
11084 if (fw != null)
11085 an.strokeWidth = (float)fw.value().floatValue();
11086 ome.xml.model.primitives.Color colf = meta.getEllipseFillColor(im, sc);
11087 if (colf != null)
11088 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11089 }
11090 else
11091 if (type == "Polygon")
11092 {
11093 an.type = ROI.Type.Polygon;
11094 an.id = meta.getPolygonID(im, sc);
11095 an.closed = true;
11096 string pxs = meta.getPolygonPoints(im, sc);
11097 PointD[] pts = an.stringToPoints(pxs);
11098 pts = ToStageSpace(pts, physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y);
11099 if (pts.Length > 100)
11100 {
11101 an.type = ROI.Type.Freeform;
11102 }
11103 an.AddPoints(pts);
11104 ome.xml.model.primitives.NonNegativeInteger nz = meta.getPolygonTheZ(im, sc);
11105 if (nz != null)
11106 co.Z = nz.getNumberValue().intValue();
11107 ome.xml.model.primitives.NonNegativeInteger nc = meta.getPolygonTheC(im, sc);
11108 if (nc != null)
11109 co.C = nc.getNumberValue().intValue();
11110 ome.xml.model.primitives.NonNegativeInteger nt = meta.getPolygonTheT(im, sc);
11111 if (nt != null)
11112 co.T = nt.getNumberValue().intValue();
11113 an.coord = new ZCT(co.Z, co.C, co.T);
11114 an.Text = meta.getPolygonText(im, sc);
11115 ome.units.quantity.Length fl = meta.getPolygonFontSize(im, sc);
11116 if (fl != null)
11117 an.fontSize = fl.value().intValue();
11118 an.family = meta.getPointFontFamily(im, sc).name();
11119 ome.xml.model.primitives.Color col = meta.getPolygonStrokeColor(im, sc);
11120 if (col != null)
11121 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11122 ome.units.quantity.Length fw = meta.getPolygonStrokeWidth(im, sc);
11123 if (fw != null)
11124 an.strokeWidth = (float)fw.value().floatValue();
11125 ome.xml.model.primitives.Color colf = meta.getPolygonFillColor(im, sc);
11126 if (colf != null)
11127 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11128 }
11129 else
11130 if (type == "Polyline")
11131 {
11132 an.type = ROI.Type.Polyline;
11133 an.id = meta.getPolylineID(im, sc);
11134 string pxs = meta.getPolylinePoints(im, sc);
11135 PointD[] pts = an.stringToPoints(pxs);
11136 for (int pi = 0; pi < pts.Length; pi++)
11137 {
11138 pts[pi] = ToStageSpace(pts[pi], physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y);
11139 }
11140 an.AddPoints(an.stringToPoints(pxs));
11141 ome.xml.model.primitives.NonNegativeInteger nz = meta.getPolylineTheZ(im, sc);
11142 if (nz != null)
11143 co.Z = nz.getNumberValue().intValue();
11144 ome.xml.model.primitives.NonNegativeInteger nc = meta.getPolylineTheC(im, sc);
11145 if (nc != null)
11146 co.C = nc.getNumberValue().intValue();
11147 ome.xml.model.primitives.NonNegativeInteger nt = meta.getPolylineTheT(im, sc);
11148 if (nt != null)
11149 co.T = nt.getNumberValue().intValue();
11150 an.coord = new ZCT(co.Z, co.C, co.T);
11151 an.Text = meta.getPolylineText(im, sc);
11152 ome.units.quantity.Length fl = meta.getPolylineFontSize(im, sc);
11153 if (fl != null)
11154 an.fontSize = fl.value().intValue();
11155 an.family = meta.getPointFontFamily(im, sc).name();
11156 ome.xml.model.primitives.Color col = meta.getPolylineStrokeColor(im, sc);
11157 if (col != null)
11158 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11159 ome.units.quantity.Length fw = meta.getPolylineStrokeWidth(im, sc);
11160 if (fw != null)
11161 an.strokeWidth = (float)fw.value().floatValue();
11162 ome.xml.model.primitives.Color colf = meta.getPolylineFillColor(im, sc);
11163 if (colf != null)
11164 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11165 }
11166 else
11167 if (type == "Label")
11168 {
11169 an.type = ROI.Type.Label;
11170 an.id = meta.getLabelID(im, sc);
11171
11172 ome.xml.model.primitives.NonNegativeInteger nz = meta.getLabelTheZ(im, sc);
11173 if (nz != null)
11174 co.Z = nz.getNumberValue().intValue();
11175 ome.xml.model.primitives.NonNegativeInteger nc = meta.getLabelTheC(im, sc);
11176 if (nc != null)
11177 co.C = nc.getNumberValue().intValue();
11178 ome.xml.model.primitives.NonNegativeInteger nt = meta.getLabelTheT(im, sc);
11179 if (nt != null)
11180 co.T = nt.getNumberValue().intValue();
11181 an.coord = new ZCT(co.Z, co.C, co.T);
11182
11183 ome.units.quantity.Length fl = meta.getLabelFontSize(im, sc);
11184 if (fl != null)
11185 an.fontSize = fl.value().intValue();
11186 an.family = meta.getPointFontFamily(im, sc).name();
11187 ome.xml.model.primitives.Color col = meta.getLabelStrokeColor(im, sc);
11188 if (col != null)
11189 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11190 ome.units.quantity.Length fw = meta.getLabelStrokeWidth(im, sc);
11191 if (fw != null)
11192 an.strokeWidth = (float)fw.value().floatValue();
11193 ome.xml.model.primitives.Color colf = meta.getLabelFillColor(im, sc);
11194 if (colf != null)
11195 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11196 PointD p = new PointD(meta.getLabelX(im, sc).doubleValue(), meta.getLabelY(im, sc).doubleValue());
11197 an.AddPoint(ToStageSpace(p, physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y));
11198 an.Text = meta.getLabelText(im, sc);
11199 }
11200 else
11201 if (type == "Mask")
11202 {
11203 byte[] bts = meta.getMaskBinData(im, sc);
11204 bool end = meta.getMaskBinDataBigEndian(im, sc).booleanValue();
11205 double h = meta.getMaskHeight(im, sc).doubleValue();
11206 double w = meta.getMaskWidth(im, sc).doubleValue();
11207 double x = meta.getMaskX(im, sc).doubleValue();
11208 double y = meta.getMaskY(im, sc).doubleValue();
11209 var maskDimensions = GetMaskDimensionsForOmeMask(null, bts, w, h);
11210 int maskWidth = maskDimensions.Width;
11211 int maskHeight = maskDimensions.Height;
11212 if (maskWidth <= 0 || maskHeight <= 0)
11213 {
11214 continue;
11215 }
11216 double maskPhysX = w > 0 ? w / maskWidth : physicalSizeX;
11217 double maskPhysY = h > 0 ? h / maskHeight : physicalSizeY;
11218 PointD stageLoc = ToStageSpace(new PointD(x, y), physicalSizeX, physicalSizeY, volume.Location.X, volume.Location.Y);
11219 an = ROI.CreateMask(
11220 co,
11221 bts,
11222 maskWidth,
11223 maskHeight,
11224 stageLoc,
11225 maskPhysX,
11226 maskPhysY,
11227 preserveDimensions: true);
11228 an.Text = meta.getMaskText(im, sc);
11229 an.id = meta.getMaskID(im, sc);
11230 an.UpdateBoundingBox();
11231 ome.xml.model.primitives.NonNegativeInteger nz = meta.getMaskTheZ(im, sc);
11232 if (nz != null)
11233 co.Z = nz.getNumberValue().intValue();
11234 ome.xml.model.primitives.NonNegativeInteger nc = meta.getMaskTheC(im, sc);
11235 if (nc != null)
11236 co.C = nc.getNumberValue().intValue();
11237 ome.xml.model.primitives.NonNegativeInteger nt = meta.getMaskTheT(im, sc);
11238 if (nt != null)
11239 co.T = nt.getNumberValue().intValue();
11240 an.coord = new ZCT(co.Z, co.C, co.T);
11241
11242 ome.units.quantity.Length fl = meta.getMaskFontSize(im, sc);
11243 if (fl != null)
11244 an.fontSize = fl.value().intValue();
11245 ome.xml.model.enums.FontFamily ff = meta.getMaskFontFamily(im, sc);
11246 if (ff != null)
11247 an.family = ff.name();
11248 ome.xml.model.primitives.Color col = meta.getMaskStrokeColor(im, sc);
11249 if (col != null)
11250 an.strokeColor = Color.FromArgb(col.getAlpha(), col.getRed(), col.getGreen(), col.getBlue());
11251 ome.units.quantity.Length fw = meta.getMaskStrokeWidth(im, sc);
11252 if (fw != null)
11253 an.strokeWidth = (float)fw.value().floatValue();
11254 ome.xml.model.primitives.Color colf = meta.getMaskFillColor(im, sc);
11255 if (colf != null)
11256 an.fillColor = Color.FromArgb(colf.getAlpha(), colf.getRed(), colf.getGreen(), colf.getBlue());
11257 }
11258 Annotations.Add(an);
11259 }
11260 }
11261
11262 imageReader.close();
11263 Recorder.Record($"BioImage.OpenOMEROIs(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {series});");
11264 return Annotations;
11265 }

◆ OpenOMESeries()

static BioImage[] OpenOMESeries ( string file,
bool tab,
bool addToImages )
static

It opens a file, checks if it's tiled, if it is, it opens it as a tiled image, if not, it opens it as a normal image.

Parameters
filethe file path
tabopen in new tab
addToImagesadd to images list.
Returns
An array of BioImage objects.
9868 {
9869 //We wait incase OME has not initialized yet.
9870 if (!initialized)
9871 do
9872 {
9873 Thread.Sleep(100);
9874 //Application.DoEvents();
9875 } while (!Initialized);
9876 var meta = (IMetadata)((OMEXMLService)new ServiceFactory().getInstance(typeof(OMEXMLService))).createOMEXMLMetadata();
9877 if (reader.getCurrentFile() != null)
9878 reader.close();
9879 reader.setMetadataStore((MetadataStore)meta);
9880 file = file.Replace("\\", "/");
9881 try
9882 {
9883 if (reader.getCurrentFile() != file)
9884 {
9885 Status = "Opening OME Image: " + file;
9886 file = file.Replace("\\", "/");
9887 reader.setId(file);
9888 }
9889 }
9890 catch (Exception e)
9891 {
9892 return null;
9893 }
9894 int count = reader.getSeriesCount();
9895 BioImage[] bs = new BioImage[count];
9896 for (int i = 0; i < count; i++)
9897 {
9898 bs[i] = OpenOME(file, i, tab, addToImages, false, 0, 0, 0, 0);
9899 if (bs[i] == null)
9900 return null;
9901 }
9902 Recorder.Record($"BioImage.OpenOMESeries(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {tab.ToString().ToLowerInvariant()}, {addToImages.ToString().ToLowerInvariant()});");
9903 return bs;
9904 }

◆ OpenSeries()

static BioImage[] OpenSeries ( string file,
bool tab )
static

It opens a tiff file, reads the number of pages, reads the number of channels, and then reads each page into a BioImage object.

Parameters
filethe path to the file
tabopen image in new tab.
Returns
An array of BioImage objects.
5113 {
5114 Tiff image = Tiff.Open(file, "r");
5115 int pages = image.NumberOfDirectories();
5116 FieldValue[] f = image.GetField(TiffTag.IMAGEDESCRIPTION);
5117 int sp = image.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToInt();
5118 ImageJDesc imDesc = new ImageJDesc();
5119 int count = 1;
5120 if (f != null)
5121 {
5122 string desc = f[0].ToString();
5123 if (desc.StartsWith("ImageJ"))
5124 {
5125 imDesc.SetString(desc);
5126 if (imDesc.channels != 0)
5127 count = imDesc.channels;
5128 }
5129 }
5130 int scount = (pages * sp) / count;
5131 BioImage[] bs = new BioImage[pages];
5132 image.Close();
5133 for (int i = 0; i < pages; i++)
5134 {
5135 bs[i] = OpenFileAsync(file, i, tab, add).Result;
5136 }
5137 Recorder.Record($"BioImage.OpenSeries(\"{file}\", {tab.ToString().ToLower()});");
5138 return bs;
5139 }

◆ OpenThread()

static void OpenThread ( )
static
10661 {
10662 if (omes)
10663 OpenOME(openfile, serie, tab, add, false, 0, 0, 0, 0);
10664 else
10665 OpenFileAsync(openfile, serie, tab, add).Wait();
10666 }

◆ OpenURL()

static async Task< BioImage > OpenURL ( string url,
ZCT coord,
int originX,
int originY,
int tileWidth,
int tileHeight )
static
5813 {
5814 Progress = 0;
5815 Status = "Opening remote Zarr";
5816 // -----------------------------------------------------------------
5817 // Input cleanup
5818 // -----------------------------------------------------------------
5819
5820 if (url.EndsWith("/"))
5821 url = url.TrimEnd('/');
5822
5823 if (!url.StartsWith("https://") && !url.StartsWith("s3://") && !url.Contains(".zarr"))
5824 throw new Exception("URL must start with https://, s3://, or contain .zarr ending.");
5825
5826 // Store the full URL as the filename so GetTile can re-open the reader
5827 // if needed. Using only Path.GetFileName loses the host/path and makes
5828 // OmeZarrReader.OpenAsync fail with a local-path lookup.
5829 var b = new BioImage(url);
5830 Progress = 10;
5831 Status = "Opening Zarr reader";
5832
5833 // -----------------------------------------------------------------
5834 // Open reader
5835 // -----------------------------------------------------------------
5836
5837 b.zarrReader ??= await OmeZarrReader.OpenAsync(url).ConfigureAwait(false);
5838 Progress = 25;
5839 Status = "Resolving multiscale metadata";
5840
5841 if (b.imagef == null)
5842 {
5843 var (ms, plateNode) = await ResolveMultiscaleAsync(b.zarrReader, url).ConfigureAwait(false);
5844 b.imagef = ms;
5845 b.multiscale = ms; // keep multiscale in sync — GetTile checks this field
5846 b.zarrPlate = plateNode;
5847 Progress = 45;
5848 Status = "Loading resolution levels";
5849
5850 if (plateNode != null)
5851 {
5852 Status = "Building well plate";
5853 await BuildZarrWellPlate(b, plateNode, url).ConfigureAwait(false);
5854 Status = "Loading resolution levels";
5855 }
5856 }
5857
5858 Status = "Loading resolution levels";
5859 b.levels = (await b.imagef
5860 .OpenAllResolutionLevelsAsync().ConfigureAwait(false))
5861 .ToList();
5862 Progress = 65;
5863 Status = "Reading tile region";
5864
5865 if (b.levels.Count == 0)
5866 throw new Exception("OME-Zarr contains no resolution levels.");
5867
5868 // -----------------------------------------------------------------
5869 // Determine datatype and logical channel layout from the highest
5870 // resolution level.
5871 //
5872 // RGB Zarrs are stored as 3 channels in the array, but the viewer
5873 // should treat them as one logical RGB image, matching TIFF RGB.
5874 // -----------------------------------------------------------------
5875
5876 var firstLevel = b.levels[0];
5877 string dtype = firstLevel.DataType;
5878 int channelCount = GetAxisSize(firstLevel, "c", 1);
5879 bool isRgbZarr = channelCount == 3;
5880 int bitsPerChannel = dtype switch
5881 {
5882 "uint8" => 8,
5883 "uint16" => 16,
5884 _ => throw new UnknownFormatException($"Unsupported datatype {dtype}")
5885 };
5886
5887 PixelFormat pixelFormat = dtype switch
5888 {
5889 "uint8" => isRgbZarr ? PixelFormat.Format24bppRgb : PixelFormat.Format8bppIndexed,
5890 "uint16" => isRgbZarr ? PixelFormat.Format48bppRgb : PixelFormat.Format16bppGrayScale,
5891 _ => throw new UnknownFormatException($"Unsupported datatype {dtype}")
5892 };
5893
5894 // -----------------------------------------------------------------
5895 // Load pyramid metadata from all resolution levels
5896 // -----------------------------------------------------------------
5897
5898 for (int i = 0; i < b.levels.Count; i++)
5899 {
5900 Status = $"Loading pyramid metadata ({i + 1}/{b.levels.Count})";
5901 var level = b.levels[i];
5902 int width = GetAxisSize(level, "x");
5903 int height = GetAxisSize(level, "y");
5904 double physicalSizeX = 1.0;
5905 double physicalSizeY = 1.0;
5906 double physicalSizeZ = 1.0;
5907 double stageSizeX = 0.0;
5908 double stageSizeY = 0.0;
5909 double stageSizeZ = 0.0;
5910
5911 var levelTransforms = await TryReadZarrLevelTransformsAsync(url, b.levels.Count).ConfigureAwait(false);
5912 if (i < levelTransforms.Length)
5913 {
5914 physicalSizeX = levelTransforms[i].ScaleX;
5915 physicalSizeY = levelTransforms[i].ScaleY;
5916 physicalSizeZ = levelTransforms[i].ScaleZ;
5917 stageSizeX = levelTransforms[i].StageX;
5918 stageSizeY = levelTransforms[i].StageY;
5919 stageSizeZ = levelTransforms[i].StageZ;
5920 }
5921
5922 b.Resolutions.Add(new Resolution(
5923 width, height, pixelFormat,
5924 physicalSizeX, physicalSizeY, physicalSizeZ,
5925 stageSizeX, stageSizeY, stageSizeZ));
5926 }
5927
5928 // -----------------------------------------------------------------
5929 // Dimension sizes from the full-resolution array shape
5930 // -----------------------------------------------------------------
5931
5932 b.sizeZ = GetAxisSize(firstLevel, "z", 1);
5933 b.sizeC = isRgbZarr ? 1 : channelCount;
5934 b.sizeT = GetAxisSize(firstLevel, "t", 1);
5935
5936 // -----------------------------------------------------------------
5937 // Build a clamped 5D PixelRegion for the tile request.
5938 //
5939 // The array axes are declared in the multiscale metadata (typically
5940 // [t, c, z, y, x]). We iterate them by name so the code is correct
5941 // regardless of axis order or count.
5942 //
5943 // Every axis gets clamped to [0, shape) and guaranteed end > start
5944 // so the PixelRegion constructor can never throw.
5945 // -----------------------------------------------------------------
5946
5947 var axes = firstLevel.EffectiveAxes;
5948 var shape = firstLevel.Shape;
5949
5950 int clampedZ = Math.Clamp(coord.Z, 0, Math.Max(0, b.sizeZ - 1));
5951 int clampedC = Math.Clamp(coord.C, 0, Math.Max(0, b.sizeC - 1));
5952 int clampedT = Math.Clamp(coord.T, 0, Math.Max(0, b.sizeT - 1));
5953
5954 var start = new long[axes.Length];
5955 var end = new long[axes.Length];
5956
5957 for (int i = 0; i < axes.Length; i++)
5958 {
5959 var axisName = axes[i].Name.ToLowerInvariant();
5960
5961 switch (axisName)
5962 {
5963 case "t":
5964 start[i] = clampedT;
5965 end[i] = clampedT + 1;
5966 break;
5967
5968 case "c":
5969 start[i] = clampedC;
5970 end[i] = clampedC + 1;
5971 break;
5972
5973 case "z":
5974 start[i] = clampedZ;
5975 end[i] = clampedZ + 1;
5976 break;
5977
5978 case "y":
5979 start[i] = Math.Clamp(originY, 0, Math.Max(0, shape[i] - 1));
5980 end[i] = Math.Min(shape[i], Math.Max(start[i] + 1, (long)originY + tileHeight));
5981 break;
5982
5983 case "x":
5984 start[i] = Math.Clamp(originX, 0, Math.Max(0, shape[i] - 1));
5985 end[i] = Math.Min(shape[i], Math.Max(start[i] + 1, (long)originX + tileWidth));
5986 break;
5987
5988 default:
5989 start[i] = 0;
5990 end[i] = shape[i];
5991 break;
5992 }
5993 }
5994
5995 var region = new PixelRegion(start, end);
5996
5997 // -----------------------------------------------------------------
5998 // Read the tile
5999 // -----------------------------------------------------------------
6000
6001 Status = "Reading tile pixels";
6002
6003 // Recover actual tile dimensions from the clamped region.
6004 int actualTileW = GetRegionAxisSize(axes, start, end, "x");
6005 int actualTileH = GetRegionAxisSize(axes, start, end, "y");
6006 int xAxisIndex = Array.FindIndex(axes, a => a.Name.Equals("x", StringComparison.OrdinalIgnoreCase));
6007 int yAxisIndex = Array.FindIndex(axes, a => a.Name.Equals("y", StringComparison.OrdinalIgnoreCase));
6008
6009 byte[] tileBytes;
6010 if (isRgbZarr)
6011 {
6012 var red = await firstLevel.ReadTileAsync((int)start[xAxisIndex],
6013 (int)start[yAxisIndex],
6014 actualTileW, actualTileH, z: clampedZ, c: 0, t: clampedT).ConfigureAwait(false);
6015 var green = await firstLevel.ReadTileAsync((int)start[xAxisIndex],
6016 (int)start[yAxisIndex],
6017 actualTileW, actualTileH, z: clampedZ, c: 1, t: clampedT).ConfigureAwait(false);
6018 var blue = await firstLevel.ReadTileAsync((int)start[xAxisIndex],
6019 (int)start[yAxisIndex],
6020 actualTileW, actualTileH, z: clampedZ, c: 2, t: clampedT).ConfigureAwait(false);
6021
6022 if (bitsPerChannel == 8)
6023 {
6024 int pixelCount = actualTileW * actualTileH;
6025 tileBytes = new byte[pixelCount * 3];
6026 int copyCount = Math.Min(pixelCount,
6027 Math.Min(red?.Data?.Length ?? 0, Math.Min(green?.Data?.Length ?? 0, blue?.Data?.Length ?? 0)));
6028 for (int i = 0; i < copyCount; i++)
6029 {
6030 int d = i * 3;
6031 tileBytes[d + 0] = blue.Data[i];
6032 tileBytes[d + 1] = green.Data[i];
6033 tileBytes[d + 2] = red.Data[i];
6034 }
6035 }
6036 else
6037 {
6038 int pixelCount = actualTileW * actualTileH;
6039 tileBytes = new byte[pixelCount * 6];
6040 int copyCount = Math.Min(pixelCount,
6041 Math.Min(red?.Data?.Length ?? 0, Math.Min(green?.Data?.Length ?? 0, blue?.Data?.Length ?? 0)) / 2);
6042 for (int i = 0; i < copyCount; i++)
6043 {
6044 int d = i * 6;
6045 int s = i * 2;
6046 tileBytes[d + 0] = blue.Data[s + 0];
6047 tileBytes[d + 1] = blue.Data[s + 1];
6048 tileBytes[d + 2] = green.Data[s + 0];
6049 tileBytes[d + 3] = green.Data[s + 1];
6050 tileBytes[d + 4] = red.Data[s + 0];
6051 tileBytes[d + 5] = red.Data[s + 1];
6052 }
6053 }
6054 }
6055 else
6056 {
6057 RegionResult tileResult = await firstLevel
6058 .ReadPixelRegionAsync(region)
6059 .ConfigureAwait(false);
6060 tileBytes = tileResult.Data;
6061 }
6062
6063 Progress = 85;
6064
6065 var bm = CreateAForgeBitmap(
6066 actualTileW,
6067 actualTileH,
6068 pixelFormat,
6069 tileBytes,
6070 coord,
6071 BitConverter.IsLittleEndian,
6072 true);
6073
6074 b.Buffers.Add(bm.GetImageRGBA());
6075
6076 // -----------------------------------------------------------------
6077 // Slide image support for pyramidal datasets
6078 // -----------------------------------------------------------------
6079
6080 //if (b.Resolutions.Count > 1)
6081 //{
6082 var si = new SlideImage { BioImage = b };
6083 b.SlideBase = new SlideBase(b, si);
6084 b.openslideBase = null;
6085
6086 //}
6087
6088 b.Volume = new VolumeD(
6089 new Point3D(b.StageSizeX, b.StageSizeY, b.StageSizeZ),
6090 new Point3D(
6091 b.SizeX * b.PhysicalSizeX,
6092 b.SizeY * b.PhysicalSizeY,
6093 b.SizeZ * b.PhysicalSizeZ));
6094
6095 // -----------------------------------------------------------------
6096 // Channels
6097 // -----------------------------------------------------------------
6098
6099 b.Channels.Add(pixelFormat switch
6100 {
6101 PixelFormat.Format8bppIndexed => new Channel(0, 8, 1),
6102 PixelFormat.Format16bppGrayScale => new Channel(0, 16, 1),
6103 PixelFormat.Format24bppRgb => new Channel(0, bitsPerChannel, 3),
6104 PixelFormat.Format48bppRgb => new Channel(0, bitsPerChannel, 3),
6105 _ => throw new NotSupportedException()
6106 });
6107
6108 // -----------------------------------------------------------------
6109 // Coordinate bookkeeping
6110 // -----------------------------------------------------------------
6111
6112 b.StackOrder = Order.ZCT;
6113 b.UpdateCoords(b.sizeZ, b.sizeC, b.sizeT, Order.ZCT);
6114 b.Coordinate = coord;
6115
6116 AutoThreshold(b, false);
6117
6118 if (b.bitsPerPixel > 8)
6119 b.StackThreshold(true);
6120 else
6121 b.StackThreshold(false);
6122
6123 // -----------------------------------------------------------------
6124 // Pick the best pyramid level for the requested tile size.
6125 // Walk from lowest-res upward; stop at the first level whose
6126 // dimensions are at least as large as the tile.
6127 // -----------------------------------------------------------------
6128
6129 b.Level = 0;
6130 for (int i = b.Resolutions.Count - 1; i >= 0; i--)
6131 {
6132 Resolution res = b.Resolutions[i];
6133 if (res.SizeX >= tileWidth && res.SizeY >= tileHeight)
6134 {
6135 b.Level = i;
6136 break;
6137 }
6138 }
6139
6140 Progress = 100;
6141 Status = "Remote Zarr open complete";
6142 Recorder.Record($"BioImage.OpenURL(\"{url}\", {coord}, {originX}, {originY}, {tileWidth}, {tileHeight});");
6143 return b;
6144 }
static AForge.Bitmap CreateAForgeBitmap(int width, int height, PixelFormat px, byte[] data, ZCT coord, bool littleEndian, bool interleaved)
Definition Bio.cs:6359
int SizeY
Definition Bio.cs:214
int SizeX
Definition Bio.cs:209

◆ OpenVips()

static void OpenVips ( BioImage b)
static

The function "OpenVips" takes a BioImage object and an integer representing the number of pages, and adds each page of the image file to the BioImage's vipPages list using the NetVips library.

Parameters
BioImageThe BioImage parameter is an object that represents a bio image. It likely contains information about the image file, such as the file path and other metadata.
pagecountThe parameter "pagecount" represents the number of pages in the TIFF file that needs to be loaded into the "vipPages" list of the "BioImage" object.
7445 {
7446 try
7447 {
7448 List<NetVips.Image> ims = new List<NetVips.Image>();
7449 for (int i = 0; i < b.seriesCount; i++)
7450 {
7451 b.vipPages.Add(NetVips.Image.Tiffload(b.file, i));
7452 }
7453 b.vipPages = SortImagesBySizeDescending(b.vipPages.ToArray()).ToList();
7454 }
7455 catch (Exception e)
7456 {
7457 Console.WriteLine(e.Message);
7458 }
7459
7460 }

◆ OpenXML()

static string OpenXML ( string file)
static

‍Open the file, get the image description field, and return it as a string

Parameters
filethe path to the file
Returns
The image description of the tiff file.
10823 {
10824 if (!file.EndsWith(".tif"))
10825 return null;
10826 Tiff image = Tiff.Open(file, "r");
10827 FieldValue[] f = image.GetField(TiffTag.IMAGEDESCRIPTION);
10828 Recorder.Record($"BioImage.OpenXML(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\");");
10829 return f[0].ToString();
10830 }

◆ OpenZarrAsync()

static async Task< BioImage > OpenZarrAsync ( string source,
int multiscaleIndex = 0,
int datasetIndex = 0,
string? id = null,
CancellationToken ct = default )
static

Opens an OME-Zarr store and converts it into a BioImage. This is the direct entry point for callers that want a BioLib image instead of working with ZarrNET nodes.

5803 {
5804 return await ZarrConversion.OpenAsBioImageAsync(source, multiscaleIndex, datasetIndex, id, ct).ConfigureAwait(false);
5805 }
Definition ZarrConversion.cs:11
static async Task< BioImage > OpenAsBioImageAsync(string source, int multiscaleIndex=0, int datasetIndex=0, string? id=null, CancellationToken ct=default)
Definition ZarrConversion.cs:12

◆ operator*() [1/2]

static BioImage operator* ( BioImage a,
ColorS b )
static

This function takes a BioImage object and a ColorS object and returns a BioImage object.

Parameters
BioImagea class that contains a list of ColorS objects.
ColorSa struct that contains a byte for each color channel (R, G, B, A)
Returns
A BioImage object
11833 {
11834 for (int i = 0; i < a.Buffers.Count; i++)
11835 {
11836 a.Buffers[i] = a.Buffers[i] * b;
11837 }
11838 return a;
11839 }

◆ operator*() [2/2]

static BioImage operator* ( BioImage a,
float b )
static
11775 {
11776 for (int i = 0; i < a.Buffers.Count; i++)
11777 {
11778 a.Buffers[i] = a.Buffers[i] / b;
11779 }
11780 return a;
11781 }

◆ operator+() [1/2]

static BioImage operator+ ( BioImage a,
ColorS b )
static

It takes a BioImage object and a ColorS object and adds the ColorS object to each buffer in the BioImage object.

Parameters
BioImagea class that contains a list of ColorS objects.
ColorSa struct that contains a byte for each color channel (R, G, B, A)
Returns
A BioImage object
11848 {
11849 for (int i = 0; i < a.Buffers.Count; i++)
11850 {
11851 a.Buffers[i] = a.Buffers[i] + b;
11852 }
11853 return a;
11854 }

◆ operator+() [2/2]

static BioImage operator+ ( BioImage a,
float b )
static

This function adds a constant value to each pixel in the image.

Parameters
BioImagea class that contains a list of buffers (float[])
bthe value to add to the image
Returns
The image itself.
11789 {
11790 for (int i = 0; i < a.Buffers.Count; i++)
11791 {
11792 a.Buffers[i] = a.Buffers[i] + b;
11793 }
11794 return a;
11795 }

◆ operator-() [1/2]

static BioImage operator- ( BioImage a,
ColorS b )
static

The function subtracts a color from each pixel in the image.

Parameters
BioImagea class that contains a list of ColorS objects.
ColorSa struct that contains a byte for each color channel (R, G, B, A)
Returns
The image itself.
11862 {
11863 for (int i = 0; i < a.Buffers.Count; i++)
11864 {
11865 a.Buffers[i] = a.Buffers[i] - b;
11866 }
11867 return a;
11868 }

◆ operator-() [2/2]

static BioImage operator- ( BioImage a,
float b )
static

Subtracts a scalar value from each pixel in the image.

Parameters
BioImagea class that contains a list of buffers (which are 2D arrays of floats)
bthe value to subtract from the image
Returns
The BioImage object is being returned.
11803 {
11804 for (int i = 0; i < a.Buffers.Count; i++)
11805 {
11806 a.Buffers[i] = a.Buffers[i] - b;
11807 }
11808 return a;
11809 }

◆ operator/() [1/2]

static BioImage operator/ ( BioImage a,
ColorS b )
static

This function divides each pixel in the image by the value of the color.

Parameters
BioImagea class that contains a list of ColorS objects.
ColorSa struct that contains a byte for each color channel (R, G, B, A)
Returns
A BioImage object.
11819 {
11820 for (int i = 0; i < a.Buffers.Count; i++)
11821 {
11822 a.Buffers[i] = a.Buffers[i] / b;
11823 }
11824 return a;
11825 }

◆ operator/() [2/2]

static BioImage operator/ ( BioImage a,
float b )
static

This function divides each pixel in the image by a constant value.

Parameters
BioImagea class that contains a list of buffers (which are 2D arrays of floats)
bthe value to divide by
Returns
The image is being returned.
11767 {
11768 for (int i = 0; i < a.Buffers.Count; i++)
11769 {
11770 a.Buffers[i] = a.Buffers[i] / b;
11771 }
11772 return a;
11773 }

◆ Rename()

void Rename ( string name)
10652 {
10653 this.Filename = Path.GetFileName(name);
10654 this.ID = name;
10655 Recorder.Record($"{this}.Rename(\"{name.Replace("\\", "\\\\").Replace("\"", "\\\"")}\");");
10656 }

◆ ROIsToString()

static string ROIsToString ( List< ROI > Annotations)
static

It takes a list of ROI objects and returns a string of all the ROI objects in the list.

Parameters
AnnotationsList of ROI objects
Returns
A string of the ROI's
11272 {
11273 string s = "";
11274 for (int i = 0; i < Annotations.Count; i++)
11275 {
11276 s += ROIToString(Annotations[i]);
11277 }
11278 return s;
11279 }
static string ROIToString(ROI an)
This function takes an ROI object and returns a string that contains all the information about the RO...
Definition Bio.cs:11286

◆ ROIToString()

static string ROIToString ( ROI an)
static

This function takes an ROI object and returns a string that contains all the information about the ROI.

Parameters
ROIThe ROI object
Returns
A string
11287 {
11288 PointD[] points = an.GetPoints();
11289 string pts = "";
11290 for (int j = 0; j < points.Length; j++)
11291 {
11292 if (j == points.Length - 1)
11293 pts += points[j].X.ToString(CultureInfo.InvariantCulture) + "," + points[j].Y.ToString(CultureInfo.InvariantCulture);
11294 else
11295 pts += points[j].X.ToString(CultureInfo.InvariantCulture) + "," + points[j].Y.ToString(CultureInfo.InvariantCulture) + " ";
11296 }
11297 char sep = (char)34;
11298 string sColor = sep.ToString() + an.strokeColor.A.ToString() + ',' + an.strokeColor.R.ToString() + ',' + an.strokeColor.G.ToString() + ',' + an.strokeColor.B.ToString() + sep.ToString();
11299 string bColor = sep.ToString() + an.fillColor.A.ToString() + ',' + an.fillColor.R.ToString() + ',' + an.fillColor.G.ToString() + ',' + an.fillColor.B.ToString() + sep.ToString();
11300
11301 string line = an.roiID + ',' + an.roiName + ',' + an.type.ToString() + ',' + an.id + ',' + an.shapeIndex.ToString() + ',' +
11302 an.Text + ',' + an.serie + ',' + an.coord.Z.ToString() + ',' + an.coord.C.ToString() + ',' + an.coord.T.ToString() + ',' + an.X.ToString(CultureInfo.InvariantCulture) + ',' + an.Y.ToString(CultureInfo.InvariantCulture) + ',' +
11303 an.W.ToString(CultureInfo.InvariantCulture) + ',' + an.H.ToString(CultureInfo.InvariantCulture) + ',' + sep.ToString() + pts + sep.ToString() + ',' + sColor + ',' + an.strokeWidth.ToString(CultureInfo.InvariantCulture) + ',' + bColor + ',' + an.fontSize.ToString(CultureInfo.InvariantCulture) + ',' + NewLine;
11304 return line;
11305 }
const char NewLine
Definition Bio.cs:10814
ZCT coord
Definition ROI.cs:184
double X
Definition ROI.cs:46
int shapeIndex
Definition ROI.cs:196
PointD[] GetPoints()
Definition ROI.cs:1613
double H
Definition ROI.cs:82
Type type
Definition ROI.cs:105
double Y
Definition ROI.cs:57
double strokeWidth
Definition ROI.cs:195
Color strokeColor
Definition ROI.cs:185
float fontSize
Definition ROI.cs:180
Color fillColor
Definition ROI.cs:186
double W
Definition ROI.cs:68

◆ SaveAsync()

static async Task SaveAsync ( string file,
string id,
int serie,
bool ome )
static

The SaveAsync function saves data to a file asynchronously.

Parameters
fileThe file parameter is a string that represents the file path or name where the data will be saved.
idThe "id" parameter is a string that represents an identifier for the save operation. It could be used to uniquely identify the saved data or to specify a specific location or format for the saved file.
serieThe "serie" parameter is an integer that represents a series or sequence number. It is used as a parameter in the SaveAsync method.
omeThe "ome" parameter is a boolean value that determines whether or not to perform a specific action in the saving process.
10690 {
10691 savefile = file;
10692 saveid = id;
10693 some = ome;
10694 await Task.Run(SaveThread);
10695 Recorder.Record($"BioImage.SaveAsync(\"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", \"{id.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {serie}, {ome.ToString().ToLowerInvariant()});");
10696 }
static string savefile
Definition Bio.cs:10667
static string saveid
Definition Bio.cs:10667
static void SaveThread()
Definition Bio.cs:10670
static bool some
Definition Bio.cs:10668

◆ SaveOME() [1/2]

static void SaveOME ( BioImage image,
string file )
static
6477 {
6478 BioImage[] sts = new BioImage[1];
6479 sts[0] = image;
6481 Recorder.Record($"BioImage.SaveOME({image}, \"{file}\");");
6482 }
static void SaveOMESeries(BioImage[] files, string f, bool planes)
This function takes a list of image files and saves them as a single OME-TIFF file.
Definition Bio.cs:6500
static bool Planes
Definition Bio.cs:2896

◆ SaveOME() [2/2]

static void SaveOME ( string file,
string ID )
static
6469 {
6470 BioImage[] sts = new BioImage[1];
6471 sts[0] = Images.GetImage(ID);
6473 Recorder.Record($"BioImage.SaveOME(\"{file}\", \"{ID}\");");
6474 }
static BioImage GetImage(string ids)
Definition Bio.cs:73

◆ SaveOMEPyramidal()

static void SaveOMEPyramidal ( BioImage[] bms,
string file,
Enums.ForeignTiffCompression compression,
int compressionLevel )
static

The function SaveOMEPyramidal saves a collection of BioImages as a pyramidal OME-TIFF file.

Parameters
bmsAn array of BioImage objects representing the images to be saved.
fileThe file parameter is a string that represents the file path where the OME Pyramidal TIFF file will be saved.
compressionThe compression parameter is of type Enums.ForeignTiffCompression and it specifies the compression method to be used when saving the TIFF file.
6988 {
6989 if (File.Exists(file))
6990 File.Delete(file);
6991 Status = "Saving OME Pyramidal";
6992 //We need to go through the images and find the ones belonging to each resolution.
6993 //As well we need to determine the dimensions of the tiles.
6994 Dictionary<double, List<BioImage>> bis = new Dictionary<double, List<BioImage>>();
6995 Dictionary<double, Point3D> min = new Dictionary<double, Point3D>();
6996 Dictionary<double, Point3D> max = new Dictionary<double, Point3D>();
6997 for (int i = 0; i < bms.Length; i++)
6998 {
6999 Resolution res = bms[i].Resolutions[bms[i].Level];
7000 if (bis.ContainsKey(res.PhysicalSizeX))
7001 {
7002 bis[res.PhysicalSizeX].Add(bms[i]);
7003 if (bms[i].StageSizeX < min[res.PhysicalSizeX].X || bms[i].StageSizeY < min[res.PhysicalSizeX].Y)
7004 {
7005 min[res.PhysicalSizeX] = bms[i].Volume.Location;
7006 }
7007 if (bms[i].StageSizeX > max[res.PhysicalSizeX].X || bms[i].StageSizeY > max[res.PhysicalSizeX].Y)
7008 {
7009 max[res.PhysicalSizeX] = bms[i].Volume.Location;
7010 }
7011 }
7012 else
7013 {
7014 bis.Add(res.PhysicalSizeX, new List<BioImage>());
7015 min.Add(res.PhysicalSizeX, new Point3D(double.MaxValue, double.MaxValue, double.MaxValue));
7016 max.Add(res.PhysicalSizeX, new Point3D(double.MinValue, double.MinValue, double.MinValue));
7017 if (bms[i].StageSizeX < min[res.PhysicalSizeX].X || bms[i].StageSizeY < min[res.PhysicalSizeX].Y)
7018 {
7019 min[res.PhysicalSizeX] = bms[i].Volume.Location;
7020 }
7021 if (bms[i].StageSizeX > max[res.PhysicalSizeX].X || bms[i].StageSizeY > max[res.PhysicalSizeX].Y)
7022 {
7023 max[res.PhysicalSizeX] = bms[i].Volume.Location;
7024 }
7025 bis[res.PhysicalSizeX].Add(bms[i]);
7026 }
7027 }
7028 int s = 0;
7029 //We determine the sizes of each resolution.
7030 Dictionary<double, AForge.Size> ss = new Dictionary<double, AForge.Size>();
7031 int minx = int.MaxValue;
7032 int miny = int.MaxValue;
7033 double last = 0;
7034 foreach (double px in bis.Keys)
7035 {
7036 int xs = (1 + (int)Math.Ceiling((max[px].X - min[px].X) / bis[px][0].Resolutions[0].VolumeWidth)) * bis[px][0].SizeX;
7037 int ys = (1 + (int)Math.Ceiling((max[px].Y - min[px].Y) / bis[px][0].Resolutions[0].VolumeHeight)) * bis[px][0].SizeY;
7038 if (minx > xs)
7039 minx = xs;
7040 if (miny > ys)
7041 miny = ys;
7042 ss.Add(px, new AForge.Size(xs, ys));
7043 last = px;
7044 }
7045 s = 0;
7046 string met = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" +
7047 "<OME xmlns=\"http://www.openmicroscopy.org/Schemas/OME/2016-06\" " +
7048 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
7049 "xsi:schemaLocation=\"http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd\">";
7050 NetVips.Image img = null;
7051 int ib = 0;
7052
7053 foreach (double px in bis.Keys)
7054 {
7055 int c = bis[px][s].SizeC;
7056 if (bis[px][s].Buffers[0].isRGB)
7057 c = 3;
7058 string endian = (bis[px][s].Buffers[0].LittleEndian).ToString().ToLower();
7059 met +=
7060 "<Image ID=\"Image:\"" + ib + "\">" +
7061 "<Pixels BigEndian=\"" + endian + "\" DimensionOrder= \"XYCZT\" ID= \"Pixels:0\" Interleaved=\"true\" " +
7062 "PhysicalSizeX=\"" + bis[px][s].PhysicalSizeX + "\" PhysicalSizeXUnit=\"µm\" PhysicalSizeY=\"" + bis[px][s].PhysicalSizeY + "\" PhysicalSizeYUnit=\"µm\" SignificantBits=\"" + bis[px][s].bitsPerPixel + "\" " +
7063 "SizeC = \"" + c + "\" SizeT = \"" + bis[px][s].SizeT + "\" SizeX =\"" + ss[px].Width +
7064 "\" SizeY= \"" + ss[px].Height + "\" SizeZ=\"" + bis[px][s].SizeZ;
7065 if (bis[px][s].bitsPerPixel > 8) met += "\" Type= \"uint16\">";
7066 else met += "\" Type= \"uint8\">";
7067 int i = 0;
7068 foreach (Channel ch in bis[px][s].Channels)
7069 {
7070 met += "<Channel ID=\"Channel:" + ib + ":" + i + "\" SamplesPerPixel=\"1\"></Channel>";
7071 i++;
7072 }
7073 met += "</Pixels></Image>";
7074 ib++;
7075 }
7076 met += "</OME>";
7077 foreach (double px in bis.Keys)
7078 {
7079 PixelFormat pf = bis[px][0].Buffers[0].PixelFormat;
7080 int bands = GetBands(pf);
7081 long firstBytes = bis[px][0].Buffers[0].Length;
7082 long expected8 = (long)bis[px][0].Buffers[0].Width * bis[px][0].Buffers[0].Height * bands;
7083 long expected16 = expected8 * 2;
7084 bool levelIs16Bit = firstBytes >= expected16;
7085 NetVips.Image canvas = NetVips.Image.Black(ss[px].Width, ss[px].Height, bands: bands);
7086 img = levelIs16Bit ? canvas.Cast(Enums.BandFormat.Ushort) : canvas;
7087 int i = 0;
7088 foreach (BioImage b in bis[px])
7089 {
7090 Progress = ((float)i / (float)bis[px].Count) * 100;
7091 AForge.Size si = ss[px];
7092 double xs = (-(min[px].X - bis[px][i].Volume.Location.X) / bis[px][i].Resolutions[0].VolumeWidth) * bis[px][i].SizeX;
7093 double ys = (-(min[px].Y - bis[px][i].Volume.Location.Y) / bis[px][i].Resolutions[0].VolumeHeight) * bis[px][i].SizeY;
7094 NetVips.Image tile;
7095 long tileBytes = bis[px][i].Buffers[0].Length;
7096 long tileExpected8 = (long)bis[px][i].Buffers[0].Width * bis[px][i].Buffers[0].Height * bands;
7097 long tileExpected16 = tileExpected8 * 2;
7098 bool tileIs16Bit = tileBytes >= tileExpected16;
7099 tile = NetVips.Image.NewFromMemory(
7100 bis[px][i].Buffers[0].Data,
7101 (ulong)tileBytes,
7102 bis[px][i].Buffers[0].Width,
7103 bis[px][i].Buffers[0].Height,
7104 bands,
7105 tileIs16Bit ? Enums.BandFormat.Ushort : Enums.BandFormat.Uchar);
7106 img = img.Insert(tile, (int)xs, (int)ys);
7107 i++;
7108 };
7109 using var mutated = img.Mutate(mutable =>
7110 {
7111 // Set the ImageDescription tag
7112 mutable.Set(GValue.GStrType, "image-description", met);
7113 mutable.Set(GValue.GIntType, "page-height", ss[last].Height);
7114 });
7115 if (levelIs16Bit)
7116 mutated.Tiffsave(file, compression, 1, Enums.ForeignTiffPredictor.None, true, ss[px].Width, ss[px].Height, true, false, 16,
7117 Enums.ForeignTiffResunit.Cm, 1000 * bis[px][0].PhysicalSizeX, 1000 * bis[px][0].PhysicalSizeY, true, null, Enums.RegionShrink.Nearest,
7118 compressionLevel, true, Enums.ForeignDzDepth.One, true, false, null, null, ss[px].Height);
7119 else
7120 mutated.Tiffsave(file, compression, 1, Enums.ForeignTiffPredictor.None, true, ss[px].Width, ss[px].Height, true, false, 8,
7121 Enums.ForeignTiffResunit.Cm, 1000 * bis[px][0].PhysicalSizeX, 1000 * bis[px][0].PhysicalSizeY, true, null, Enums.RegionShrink.Nearest,
7122 compressionLevel, true, Enums.ForeignDzDepth.One, true, false, null, null, ss[px].Height);
7123 s++;
7124 }
7125
7126 Recorder.Record($"BioImage.SaveOMEPyramidal(new BioImage[] {{ {string.Join(", ", bms.Select(bm => bm.ToString()))} }}, \"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", Enums.ForeignTiffCompression.{compression}, {compressionLevel});");
7127 }
static int GetBands(PixelFormat format)
The function "GetBands" returns the number of color bands for a given pixel format in the AForge libr...
Definition Bio.cs:6960
static BioImage[] bms
Definition Bio.cs:10737
double StageSizeY
Definition Bio.cs:2615
List< Resolution > Resolutions
Definition Bio.cs:1979
VolumeD Volume
Definition Bio.cs:2009
int Level
Definition Bio.cs:1987
double PhysicalSizeX
Definition Bio.cs:219

◆ SaveOMESeries()

static void SaveOMESeries ( BioImage[] files,
string f,
bool planes )
static

This function takes a list of image files and saves them as a single OME-TIFF file.

Parameters
filesan array of file paths to the images to be saved
fthe file name to save to
planesif true, the planes will be saved as well.
6501 {
6502 if (File.Exists(f))
6503 File.Delete(f);
6504 // helper to ensure strictly positive physical lengths
6505 double SafePositive(double v) => (v > 0.0 && !double.IsNaN(v)) ? v : 1.0;
6506
6507 // Create a single metadata store for all series
6508 loci.formats.meta.IMetadata omexml = service.createOMEXMLMetadata();
6509 Status = "Saving OME Image Metadata.";
6510
6511 // Create one Instrument + one LightSource (must exist and have IDs)
6512 string instrumentID = "Instrument:0";
6513 string lightSourceID = "LightSource:0";
6514 omexml.setInstrumentID(instrumentID, 0);
6515 omexml.setLightEmittingDiodeID(lightSourceID, 0, 0); // Fixed: use 0 instead of undefined 'serie'
6516 omexml.setLightEmittingDiodeModel("ModelName", 0, 0); // Fixed: use 0 instead of undefined 'serie'
6517
6518 // Build metadata for each series
6519 for (int serie = 0; serie < files.Length; serie++)
6520 {
6521 BioImage b = files[serie];
6522
6523 // Required Image / Pixels IDs
6524 omexml.setImageID($"Image:{serie}", serie);
6525 omexml.setPixelsID($"Pixels:{serie}", serie);
6526
6527 PixelFormat saveFormat =
6528 b.Buffers.Count > 0 ? b.Buffers[0].PixelFormat :
6529 (b.Resolutions.Count > 0 ? b.Resolutions[Math.Clamp(b.Level, 0, b.Resolutions.Count - 1)].PixelFormat : PixelFormat.Format8bppIndexed);
6530 bool packedRgb =
6531 saveFormat == PixelFormat.Format24bppRgb ||
6532 saveFormat == PixelFormat.Format48bppRgb ||
6533 saveFormat == PixelFormat.Format32bppArgb ||
6534 saveFormat == PixelFormat.Format32bppPArgb ||
6535 saveFormat == PixelFormat.Format32bppRgb ||
6536 saveFormat == PixelFormat.Format64bppArgb ||
6537 saveFormat == PixelFormat.Format64bppPArgb;
6538 int bytesPerPixel = GetBytesPerPixel(saveFormat);
6539 bool sixteenBit = bytesPerPixel == 2 || bytesPerPixel == 6 || bytesPerPixel == 8;
6540
6541 // Pixel layout and type must match the actual plane bytes we hand to Bio-Formats.
6542 omexml.setPixelsDimensionOrder(ome.xml.model.enums.DimensionOrder.XYCZT, serie);
6543 omexml.setPixelsInterleaved(java.lang.Boolean.valueOf(packedRgb), serie);
6544 omexml.setPixelsType(
6545 sixteenBit ? ome.xml.model.enums.PixelType.UINT16 : ome.xml.model.enums.PixelType.UINT8,
6546 serie);
6547
6548 // Sizes (must be PositiveInteger)
6549 omexml.setPixelsSizeX(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(b.SizeX)), serie);
6550 omexml.setPixelsSizeY(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(b.SizeY)), serie);
6551 omexml.setPixelsSizeZ(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(b.SizeZ)), serie);
6552 omexml.setPixelsSizeT(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(b.SizeT)), serie);
6553 omexml.setPixelsSizeC(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(b.SizeC)), serie);
6554 // Endianness
6555 omexml.setPixelsBigEndian(java.lang.Boolean.valueOf(!BitConverter.IsLittleEndian), serie);
6556
6557 // Physical sizes (must be > 0)
6558 omexml.setPixelsPhysicalSizeX(new ome.units.quantity.Length(java.lang.Double.valueOf(SafePositive(b.PhysicalSizeX)), ome.units.UNITS.MICROMETER), serie);
6559 omexml.setPixelsPhysicalSizeY(new ome.units.quantity.Length(java.lang.Double.valueOf(SafePositive(b.PhysicalSizeY)), ome.units.UNITS.MICROMETER), serie);
6560 double psz = (b.PhysicalSizeZ == 0.0 ? 1.0 : b.PhysicalSizeZ);
6561 omexml.setPixelsPhysicalSizeZ(new ome.units.quantity.Length(java.lang.Double.valueOf(SafePositive(psz)), ome.units.UNITS.MICROMETER), serie);
6562
6563 // Stage label / positions
6564 omexml.setStageLabelName($"StageLabel:{serie}", serie);
6565 omexml.setStageLabelX(new ome.units.quantity.Length(java.lang.Double.valueOf(b.Volume.Location.X), ome.units.UNITS.MICROMETER), serie);
6566 omexml.setStageLabelY(new ome.units.quantity.Length(java.lang.Double.valueOf(b.Volume.Location.Y), ome.units.UNITS.MICROMETER), serie);
6567 omexml.setStageLabelZ(new ome.units.quantity.Length(java.lang.Double.valueOf(b.Volume.Location.Z), ome.units.UNITS.MICROMETER), serie);
6568
6569 // Ensure Image references the Instrument - MOVED INSIDE LOOP
6570 omexml.setImageInstrumentRef(instrumentID, serie);
6571
6572 // Build channel list (split RGB-packed channel into three if needed)
6573 List<Channel> chs = new List<Channel>();
6574 if (b.Channels.Count == 1 && b.Channels[0].range.Length == 3)
6575 {
6576 for (int cind = 0; cind < 3; cind++)
6577 {
6578 Channel ch = b.Channels[0].Copy();
6579 ch.SamplesPerPixel = 1;
6580 ch.range = new IntRange[] { b.Channels[0].range[cind] };
6581 chs.Add(ch);
6582 }
6583 }
6584 else
6585 {
6586 chs.AddRange(b.Channels);
6587 }
6588
6589 // Channels metadata — channel index is c, image index is serie
6590 for (int c = 0; c < chs.Count; c++)
6591 {
6592 Channel ch = chs[c];
6593
6594 string channelId = $"Channel:{c}:{serie}";
6595 omexml.setChannelID(channelId, serie, c); // Fixed: swapped parameters - should be (id, imageIndex, channelIndex)
6596
6597 // Samples per pixel must reflect the serialized channel layout, not stale copied metadata.
6598 int spp = 1;
6599 omexml.setChannelSamplesPerPixel(new ome.xml.model.primitives.PositiveInteger(java.lang.Integer.valueOf(spp)), serie, c);
6600
6601 omexml.setChannelName(ch.Name ?? channelId, serie, c);
6602
6603 // Channel color (nullable)
6604 if (ch.Color != null)
6605 omexml.setChannelColor(new ome.xml.model.primitives.Color(ch.Color.Value.R, ch.Color.Value.G, ch.Color.Value.B, ch.Color.Value.A), serie, c);
6606 else
6607 omexml.setChannelColor(null, serie, c);
6608
6609 // Emission/excitation must be null if zero or absent (can't be 0.0)
6610 if (ch.Emission != 0.0)
6611 omexml.setChannelEmissionWavelength(new ome.units.quantity.Length(java.lang.Double.valueOf(ch.Emission), ome.units.UNITS.NANOMETER), serie, c);
6612 else
6613 omexml.setChannelEmissionWavelength(null, serie, c);
6614
6615 if (ch.Excitation != 0.0)
6616 omexml.setChannelExcitationWavelength(new ome.units.quantity.Length(java.lang.Double.valueOf(ch.Excitation), ome.units.UNITS.NANOMETER), serie, c);
6617 else
6618 omexml.setChannelExcitationWavelength(null, serie, c);
6619
6620 // Fluor
6621 omexml.setChannelFluor(ch.Fluor, serie, c);
6622
6623 // Light source settings
6624 string lightSourceSettingsID = $"LightSourceSettings:{c}:{serie}";
6625 omexml.setChannelLightSourceSettingsID(lightSourceSettingsID, serie, c);
6626 }
6627 int i = 0;
6628 int shapeIndex = 0; // Single unified counter for all shapes
6629 ROI[] d = SortByType(b.Annotations.ToArray());
6630
6631 foreach (ROI an in d)
6632 {
6633 string roiId = an.roiID == "" ? "ROI:" + i.ToString() + ":" + serie : an.roiID;
6634 omexml.setROIID(roiId, i);
6635 omexml.setImageROIRef(roiId, serie, i);
6636 if (an.roiName != "")
6637 omexml.setROIName(an.roiName, i);
6638 else
6639 omexml.setROIName("ROI:" + i.ToString() + ":" + serie, i);
6640 if (an.type == ROI.Type.Point)
6641 {
6642 if (an.id != "")
6643 omexml.setPointID(an.id, shapeIndex, serie);
6644 else
6645 omexml.setPointID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6646 omexml.setPointX(java.lang.Double.valueOf(b.ToImageSpaceX(an.X)), shapeIndex, serie);
6647 omexml.setPointY(java.lang.Double.valueOf(b.ToImageSpaceY(an.Y)), shapeIndex, serie);
6648 omexml.setPointTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6649 omexml.setPointTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6650 omexml.setPointTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6651 if (an.Text != "")
6652 omexml.setPointText(an.Text, shapeIndex, serie);
6653 else
6654 omexml.setPointText(i.ToString(), shapeIndex, serie);
6655 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6656 omexml.setPointFontSize(fl, shapeIndex, serie);
6657 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6658 omexml.setPointStrokeColor(col, shapeIndex, serie);
6659 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6660 omexml.setPointStrokeWidth(sw, shapeIndex, serie);
6661 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6662 omexml.setPointFillColor(colf, shapeIndex, serie);
6663 shapeIndex++;
6664 }
6665 else
6666 if (an.type == ROI.Type.Polygon || an.type == ROI.Type.Freeform)
6667 {
6668 if (an.id != "")
6669 omexml.setPolygonID(an.id, shapeIndex, serie);
6670 else
6671 omexml.setPolygonID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6672 omexml.setPolygonPoints(an.PointsToString(b), shapeIndex, serie);
6673 omexml.setPolygonTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6674 omexml.setPolygonTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6675 omexml.setPolygonTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6676 if (an.Text != "")
6677 omexml.setPolygonText(an.Text, shapeIndex, serie);
6678 else
6679 omexml.setPolygonText(shapeIndex.ToString(), shapeIndex, serie);
6680 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6681 omexml.setPolygonFontSize(fl, shapeIndex, serie);
6682 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6683 omexml.setPolygonStrokeColor(col, shapeIndex, serie);
6684 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6685 omexml.setPolygonStrokeWidth(sw, shapeIndex, serie);
6686 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6687 omexml.setPolygonFillColor(colf, shapeIndex, serie);
6688 shapeIndex++;
6689 }
6690 else
6691 if (an.type == ROI.Type.Polyline)
6692 {
6693 if (an.id != "")
6694 omexml.setPolylineID(an.id, shapeIndex, serie);
6695 else
6696 omexml.setPolylineID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6697 omexml.setPolylinePoints(an.PointsToString(b), shapeIndex, serie);
6698 omexml.setPolylineTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6699 omexml.setPolylineTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6700 omexml.setPolylineTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6701 if (an.Text != "")
6702 omexml.setPolylineText(an.Text, shapeIndex, serie);
6703 else
6704 omexml.setPolylineText(i.ToString(), shapeIndex, serie);
6705 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6706 omexml.setPolylineFontSize(fl, shapeIndex, serie);
6707 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6708 omexml.setPolylineStrokeColor(col, shapeIndex, serie);
6709 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6710 omexml.setPolylineStrokeWidth(sw, shapeIndex, serie);
6711 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6712 omexml.setPolylineFillColor(colf, shapeIndex, serie);
6713 shapeIndex++;
6714 }
6715 else
6716 if (an.type == ROI.Type.Rectangle)
6717 {
6718 if (an.id != "")
6719 omexml.setRectangleID(an.id, shapeIndex, serie);
6720 else
6721 omexml.setRectangleID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6722 omexml.setRectangleWidth(java.lang.Double.valueOf(b.ToImageSizeX(an.W)), shapeIndex, serie);
6723 omexml.setRectangleHeight(java.lang.Double.valueOf(b.ToImageSizeY(an.H)), shapeIndex, serie);
6724 omexml.setRectangleX(java.lang.Double.valueOf(b.ToImageSpaceX(an.BoundingBox.X)), shapeIndex, serie);
6725 omexml.setRectangleY(java.lang.Double.valueOf(b.ToImageSpaceY(an.BoundingBox.Y)), shapeIndex, serie);
6726 omexml.setRectangleTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6727 omexml.setRectangleTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6728 omexml.setRectangleTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6729 if (an.Text != "")
6730 omexml.setRectangleText(an.Text, shapeIndex, serie);
6731 else
6732 omexml.setRectangleText(shapeIndex.ToString(), shapeIndex, serie);
6733 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6734 omexml.setRectangleFontSize(fl, shapeIndex, serie);
6735 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6736 omexml.setRectangleStrokeColor(col, shapeIndex, serie);
6737 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6738 omexml.setRectangleStrokeWidth(sw, shapeIndex, serie);
6739 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6740 omexml.setRectangleFillColor(colf, shapeIndex, serie);
6741 shapeIndex++;
6742 }
6743 else
6744 if (an.type == ROI.Type.Line)
6745 {
6746 if (an.id != "")
6747 omexml.setLineID(an.id, shapeIndex, serie);
6748 else
6749 omexml.setLineID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6750 omexml.setLineX1(java.lang.Double.valueOf(b.ToImageSpaceX(an.GetPoint(0).X)), shapeIndex, serie);
6751 omexml.setLineY1(java.lang.Double.valueOf(b.ToImageSpaceY(an.GetPoint(0).Y)), shapeIndex, serie);
6752 omexml.setLineX2(java.lang.Double.valueOf(b.ToImageSpaceX(an.GetPoint(1).X)), shapeIndex, serie);
6753 omexml.setLineY2(java.lang.Double.valueOf(b.ToImageSpaceY(an.GetPoint(1).Y)), shapeIndex, serie);
6754 omexml.setLineTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6755 omexml.setLineTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6756 omexml.setLineTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6757 if (an.Text != "")
6758 omexml.setLineText(an.Text, shapeIndex, serie);
6759 else
6760 omexml.setLineText(shapeIndex.ToString(), shapeIndex, serie);
6761 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6762 omexml.setLineFontSize(fl, shapeIndex, serie);
6763 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6764 omexml.setLineStrokeColor(col, shapeIndex, serie);
6765 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6766 omexml.setLineStrokeWidth(sw, shapeIndex, serie);
6767 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6768 omexml.setLineFillColor(colf, shapeIndex, serie);
6769 shapeIndex++;
6770 }
6771 else
6772 if (an.type == ROI.Type.Ellipse)
6773 {
6774
6775 //We need to change System.Drawing.Rectangle to ellipse radius;
6776 double w = (double)an.W / 2;
6777 double h = (double)an.H / 2;
6778 if (an.id != "")
6779 omexml.setEllipseID(an.id, shapeIndex, serie);
6780 else
6781 omexml.setEllipseID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6782 omexml.setEllipseRadiusX(java.lang.Double.valueOf(b.ToImageSizeX(w)), shapeIndex, serie);
6783 omexml.setEllipseRadiusY(java.lang.Double.valueOf(b.ToImageSizeY(h)), shapeIndex, serie);
6784
6785 double x = an.Points[0].X + w;
6786 double y = an.Points[0].Y + h;
6787 omexml.setEllipseX(java.lang.Double.valueOf(b.ToImageSpaceX(x)), shapeIndex, serie);
6788 omexml.setEllipseY(java.lang.Double.valueOf(b.ToImageSpaceY(y)), shapeIndex, serie);
6789 omexml.setEllipseTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6790 omexml.setEllipseTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6791 omexml.setEllipseTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6792 if (an.Text != "")
6793 omexml.setEllipseText(an.Text, shapeIndex, serie);
6794 else
6795 omexml.setEllipseText(i.ToString(), shapeIndex, serie);
6796 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6797 omexml.setEllipseFontSize(fl, shapeIndex, serie);
6798 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6799 omexml.setEllipseStrokeColor(col, shapeIndex, serie);
6800 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6801 omexml.setEllipseStrokeWidth(sw, shapeIndex, serie);
6802 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6803 omexml.setEllipseFillColor(colf, shapeIndex, serie);
6804 shapeIndex++;
6805 }
6806 else
6807 if (an.type == ROI.Type.Label)
6808 {
6809 if (an.id != "")
6810 omexml.setLabelID(an.id, shapeIndex, serie);
6811 else
6812 omexml.setLabelID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6813 omexml.setLabelX(java.lang.Double.valueOf(b.ToImageSpaceX(an.BoundingBox.X)), shapeIndex, serie);
6814 omexml.setLabelY(java.lang.Double.valueOf(b.ToImageSpaceY(an.BoundingBox.Y)), shapeIndex, serie);
6815 omexml.setLabelTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6816 omexml.setLabelTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6817 omexml.setLabelTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6818 if (an.Text != "")
6819 omexml.setLabelText(an.Text, shapeIndex, serie);
6820 else
6821 omexml.setLabelText(shapeIndex.ToString(), shapeIndex, serie);
6822 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6823 omexml.setLabelFontSize(fl, shapeIndex, serie);
6824 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6825 omexml.setLabelStrokeColor(col, shapeIndex, serie);
6826 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6827 omexml.setLabelStrokeWidth(sw, shapeIndex, serie);
6828 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6829 omexml.setLabelFillColor(colf, shapeIndex, serie);
6830 shapeIndex++;
6831 }
6832 else
6833 if (an.type == ROI.Type.Mask || an.roiMask != null)
6834 {
6835 double physX = an.roiMask.PhysicalSizeX > 0 ? an.roiMask.PhysicalSizeX : (b.PhysicalSizeX > 0 ? b.PhysicalSizeX : 1.0);
6836 double physY = an.roiMask.PhysicalSizeY > 0 ? an.roiMask.PhysicalSizeY : (b.PhysicalSizeY > 0 ? b.PhysicalSizeY : 1.0);
6837 byte[] bts = an.roiMask.GetBytesCropped(b.SizeX, b.SizeY, out int cropWidth, out int cropHeight, out int startX, out int startY);
6838 if (bts == null || bts.Length == 0)
6839 continue;
6840
6841 double maskX = an.X + (startX * physX);
6842 double maskY = an.Y + (startY * physY);
6843
6844 if (an.id != "")
6845 omexml.setMaskID(an.id, shapeIndex, serie);
6846 else
6847 omexml.setMaskID("Shape:" + shapeIndex + ":" + serie, shapeIndex, serie);
6848 omexml.setMaskX(java.lang.Double.valueOf(maskX), shapeIndex, serie);
6849 omexml.setMaskY(java.lang.Double.valueOf(maskY), shapeIndex, serie);
6850 omexml.setMaskTheZ(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.Z)), shapeIndex, serie);
6851 omexml.setMaskTheC(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.C)), shapeIndex, serie);
6852 omexml.setMaskTheT(new NonNegativeInteger(java.lang.Integer.valueOf(an.coord.T)), shapeIndex, serie);
6853 omexml.setMaskWidth(new java.lang.Double(cropWidth * physX), shapeIndex, serie);
6854 omexml.setMaskHeight(new java.lang.Double(cropHeight * physY), shapeIndex, serie);
6855 if (an.Text != "")
6856 omexml.setMaskText(an.Text, shapeIndex, serie);
6857 else
6858 omexml.setMaskText(shapeIndex.ToString(), shapeIndex, serie);
6859
6860 ome.units.quantity.Length fl = new ome.units.quantity.Length(java.lang.Double.valueOf(an.fontSize), ome.units.UNITS.PIXEL);
6861 omexml.setMaskFontSize(fl, shapeIndex, serie);
6862 ome.xml.model.primitives.Color col = new ome.xml.model.primitives.Color(an.strokeColor.R, an.strokeColor.G, an.strokeColor.B, an.strokeColor.A);
6863 omexml.setMaskStrokeColor(col, shapeIndex, serie);
6864 ome.units.quantity.Length sw = new ome.units.quantity.Length(java.lang.Double.valueOf(an.strokeWidth), ome.units.UNITS.PIXEL);
6865 omexml.setMaskStrokeWidth(sw, shapeIndex, serie);
6866 ome.xml.model.primitives.Color colf = new ome.xml.model.primitives.Color(an.fillColor.R, an.fillColor.G, an.fillColor.B, an.fillColor.A);
6867 omexml.setMaskFillColor(colf, shapeIndex, serie);
6868 omexml.setMaskBinData(bts, shapeIndex, serie);
6869 omexml.setMaskBinDataBigEndian(new java.lang.Boolean(!BitConverter.IsLittleEndian), shapeIndex, serie);
6870 omexml.setMaskBinDataLength(new NonNegativeLong(new java.lang.Long(bts.Length)), shapeIndex, serie);
6871 omexml.setMaskBinDataCompression(ome.xml.model.enums.Compression.NONE, shapeIndex, serie);
6872 shapeIndex++;
6873 }
6874 i++;
6875 }
6876
6877 } // end metadata build loop
6878
6879 // ---------- Create writer once and attach the metadata ----------
6880 loci.formats.ImageWriter writer = null;
6881 try
6882 {
6883 writer = new loci.formats.ImageWriter();
6884 writer.setMetadataRetrieve(omexml);
6885
6886 // setId only once (initializes writer for this output file)
6887 writer.setId(f);
6888
6889 // Write every series / plane
6890 for (int serie = 0; serie < files.Length; serie++)
6891 {
6892 BioImage b = files[serie];
6893 writer.setSeries(serie);
6894
6895 for (int bu = 0; bu < b.Buffers.Count; bu++)
6896 {
6897 byte[] bts = b.Buffers[bu].GetSaveBytes(BitConverter.IsLittleEndian);
6898 writer.saveBytes(bu, bts);
6899 Progress = ((float)bu / (float)b.Buffers.Count) * 100f;
6900 }
6901 }
6902
6903 writer.close();
6904 }
6905 catch (Exception ex)
6906 {
6907 // Dump OME-XML for debugging
6908 try
6909 {
6910 string xmlDump = ((loci.formats.ome.OMEXMLMetadata)omexml).dumpXML();
6911 Console.WriteLine("OME-XML dump:\n" + xmlDump);
6912 }
6913 catch
6914 {
6915 // ignore dump failure
6916 }
6917
6918 Console.WriteLine("SaveOMESeries exception: " + ex.ToString());
6919 throw;
6920 }
6921 finally
6922 {
6923 if (writer != null)
6924 {
6925 try { writer.close(); } catch { }
6926 }
6927 }
6928
6929 Recorder.Record($"BioImage.SaveOMESeries(new string[] {{ {string.Join(", ", files.Select(file => $"\"{file}\""))} }}, \"{f}\", BioImage.Planes);");
static ROI[] SortByType(ROI[] annotations)
Sorts an array of ROIs by their Type property (alphabetically by default).
Definition Bio.cs:6486
byte[] GetBytesCropped()
Definition ROI.cs:551
string roiName
Definition ROI.cs:190
RectangleD BoundingBox
Definition ROI.cs:42
string id
Definition ROI.cs:188
Mask roiMask
Definition ROI.cs:212
string Text
Definition ROI.cs:962
string roiID
Definition ROI.cs:189
PointD GetPoint(int i)
Definition ROI.cs:1604
string PointsToString(BioImage b)
Definition ROI.cs:1748
List< PointD > Points
Definition ROI.cs:122

◆ SaveOMEZarr()

static async Task SaveOMEZarr ( BioImage[] bms,
string outputDir )
static

Saves already-processed pyramid levels as an OME-Zarr multiscale dataset.

6944 {
6945 await Zarr.SavePyramidalZarr(bms, outputDir).ConfigureAwait(false);
6946 Recorder.Record($"BioImage.SaveOMEZarr(new BioImage[] {{ {string.Join(", ", bms.Select(bm => bm.ToString()))} }}, \"{outputDir}\");");
6947 }
Bridges BioLib's BioImage to the OmeZarrWriter, exporting a full 5D image as an OME-Zarr v3 dataset o...
Definition Zarr.cs:34
static async Task SavePyramidalZarr(BioImage[] levels, string outputDir)
Saves already-processed pyramid levels as an OME-Zarr multiscale dataset. Each entry in levels is wr...
Definition Zarr.cs:289

◆ SavePyramidalAsync()

static async Task SavePyramidalAsync ( BioImage[] imgs,
string file,
Enums.ForeignTiffCompression com,
int compLevel )
static

The function SavePyramidalAsync saves an array of BioImage objects as a pyramidal TIFF file asynchronously.

Parameters
imgsimgs is an array of BioImage objects.
fileThe "file" parameter is a string that represents the file path where the pyramidal image will be saved.
comThe parameter "com" is of type Enums.ForeignTiffCompression, which is an enumeration representing different compression options for the TIFF file.
compLevelThe compLevel parameter is an integer that represents the compression level for the TIFF file. It is used to specify the level of compression to be applied to the image data when saving the pyramidal image. The higher the compression level, the smaller the file size but potentially lower image quality.
10756 {
10757 bms = imgs;
10758 savefile = file;
10759 comp = com;
10760 compLev = compLevel;
10761 await Task.Run(SavePyramidalThread);
10762 Recorder.Record($"BioImage.SavePyramidalAsync(new BioImage[] {{ {string.Join(", ", imgs.Select(img => img.ToString()))} }}, \"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", Enums.ForeignTiffCompression.{com}, {compLevel});");
10763 }
static int compLev
Definition Bio.cs:10736
static void SavePyramidalThread()
Definition Bio.cs:10738
static Enums.ForeignTiffCompression comp
Definition Bio.cs:10735

◆ SavePyramidalThread()

static void SavePyramidalThread ( )
static
10739 {
10741 }
static void SaveOMEPyramidal(BioImage[] bms, string file, Enums.ForeignTiffCompression compression, int compressionLevel)
The function SaveOMEPyramidal saves a collection of BioImages as a pyramidal OME-TIFF file.
Definition Bio.cs:6987

◆ SaveSeriesAsync()

static async Task SaveSeriesAsync ( BioImage[] imgs,
string file,
bool ome )
static

The function SaveSeriesAsync saves a series of BioImage objects to a file asynchronously.

Parameters
imgsimgs is an array of BioImage objects.
fileThe "file" parameter is a string that represents the file path where the series of BioImages will be saved.
omeThe "ome" parameter is a boolean flag that indicates whether the images should be saved in OME-TIFF format or not. If "ome" is set to true, the images will be saved in OME-TIFF format. If "ome" is set to false, the images will be
10724 {
10725 sts.Clear();
10726 foreach (BioImage item in imgs)
10727 {
10728 sts.Add(item.ID);
10729 }
10730 savefile = file;
10731 some = ome;
10732 await Task.Run(SaveSeriesThread);
10733 Recorder.Record($"BioImage.SaveSeriesAsync(new BioImage[] {{ {string.Join(", ", imgs.Select(img => img.ToString()))} }}, \"{file.Replace("\\", "\\\\").Replace("\"", "\\\"")}\", {ome.ToString().ToLowerInvariant()});");
10734 }
static void SaveSeriesThread()
Definition Bio.cs:10699
string ID
Definition Bio.cs:2544

◆ SaveSeriesThread()

static void SaveSeriesThread ( )
static
10700 {
10701 if (omes)
10702 {
10703 BioImage[] bms = new BioImage[sts.Count];
10704 int i = 0;
10705 foreach (string st in sts)
10706 {
10707 bms[i] = Images.GetImage(st);
10708 }
10710 }
10711 else
10712 SaveSeries(sts.ToArray(), savefile);
10713 }
BioImage. Planes
Definition Bio.cs:6929

◆ SaveThread()

static void SaveThread ( )
static
10671 {
10672 if (some)
10674 else
10675 SaveFile(savefile, saveid);
10676 }
static void SaveOME(string file, string ID)
Definition Bio.cs:6468

◆ SaveZarr()

static void SaveZarr ( BioImage b,
string outputFile )
static
6933 {
6934 Zarr.SaveZarr(b, outputFile);
6935 Zarr.SaveROIs(b, outputFile);
6936 Recorder.Record($"BioImage.SaveZarr({b}, \"{outputFile}\");");
6937 }
static void SaveROIs(BioImage b, string outputDir)
Writes all annotations on b as a JSON sidecar file at <outputDir>/rois.json.
Definition Zarr.cs:914
static async void SaveZarr(BioImage b, string outputDir)
Saves the full 5D content of a BioImage as an OME-Zarr v3 dataset at outputDir . All pyramid resoluti...
Definition Zarr.cs:118

◆ SetFrameIndex()

void SetFrameIndex ( int z,
int c,
int t,
int val )
8518 {
8519 Coords[z, c, t] = val;
8520 }

◆ SortByType()

static ROI[] SortByType ( ROI[] annotations)
static

Sorts an array of ROIs by their Type property (alphabetically by default).

6487 {
6488 if (annotations == null || annotations.Length == 0)
6489 return Array.Empty<ROI>();
6490
6491 return annotations
6492 .OrderBy<ROI, string>(r => r.type.ToString(), StringComparer.OrdinalIgnoreCase)
6493 .ToArray();
6494 }

◆ StackThreshold()

void StackThreshold ( bool bit16)

This function sets the minimum and maximum values of the image to the minimum and maximum values of the stack.

Parameters
bit16true = 16 bit, false = 8 bit
9670 {
9671 try
9672 {
9673 if (bit16)
9674 {
9675 for (int ch = 0; ch < Channels.Count; ch++)
9676 {
9677 for (int i = 0; i < Channels[ch].range.Length; i++)
9678 {
9679 Channels[ch].range[i].Min = (int)Channels[ch].stats[i].StackMin;
9680 Channels[ch].range[i].Max = (int)Channels[ch].stats[i].StackMax;
9681 }
9682 Channels[ch].BitsPerPixel = 16;
9683 }
9684 }
9685 else
9686 {
9687 for (int ch = 0; ch < Channels.Count; ch++)
9688 {
9689 for (int i = 0; i < Channels[ch].range.Length; i++)
9690 {
9691 Channels[ch].range[i].Min = (int)Channels[ch].stats[i].StackMin;
9692 Channels[ch].range[i].Max = (int)Channels[ch].stats[i].StackMax;
9693 }
9694 Channels[ch].BitsPerPixel = 8;
9695 }
9696 }
9697 }
9698 catch (Exception e)
9699 {
9700
9701 }
9702 Recorder.AddLine("BioLib.Images.GetImage(\"" + id + "\").StackThreshold(" + bit16.ToString().ToLower() + ");", true);
9703 }

◆ StringToROI()

static ROI StringToROI ( string sts)
static

It takes a string and returns an ROI object.

Parameters
ststhe string that contains the ROI data
Returns
A ROI object.
11312 {
11313 //Works with either comma or tab separated values.
11314 if (sts.StartsWith("<?xml") || sts.StartsWith("{"))
11315 return null;
11316 ROI an = new ROI();
11317 string val = "";
11318 bool inSep = false;
11319 int col = 0;
11320 double x = 0;
11321 double y = 0;
11322 double w = 0;
11323 double h = 0;
11324 string line = sts;
11325 bool points = false;
11326 char sep = '"';
11327 for (int i = 0; i < line.Length; i++)
11328 {
11329 char c = line[i];
11330 if (c == sep)
11331 {
11332 if (!inSep)
11333 {
11334 inSep = true;
11335 }
11336 else
11337 inSep = false;
11338 continue;
11339 }
11340
11341 if ((c == ',' || c == '\t') && (!inSep || points))
11342 {
11343 //ROIID,ROINAME,TYPE,ID,SHAPEINDEX,TEXT,C,Z,T,X,Y,W,H,POINTS,STROKECOLOR,STROKECOLORW,FILLCOLOR,FONTSIZE
11344 if (col == 0)
11345 {
11346 //ROIID
11347 an.roiID = val;
11348 }
11349 else
11350 if (col == 1)
11351 {
11352 //ROINAME
11353 an.roiName = val;
11354 }
11355 else
11356 if (col == 2)
11357 {
11358 //TYPE
11359 an.type = (ROI.Type)Enum.Parse(typeof(ROI.Type), val);
11360 if (an.type == ROI.Type.Freeform || an.type == ROI.Type.Polygon)
11361 an.closed = true;
11362 }
11363 else
11364 if (col == 3)
11365 {
11366 //ID
11367 an.id = val;
11368 }
11369 else
11370 if (col == 4)
11371 {
11372 //SHAPEINDEX/
11373 an.shapeIndex = int.Parse(val);
11374 }
11375 else
11376 if (col == 5)
11377 {
11378 //TEXT/
11379 an.Text = val;
11380 }
11381 else
11382 if (col == 6)
11383 {
11384 an.serie = int.Parse(val);
11385 }
11386 else
11387 if (col == 7)
11388 {
11389 an.coord.Z = int.Parse(val);
11390 }
11391 else
11392 if (col == 8)
11393 {
11394 an.coord.C = int.Parse(val);
11395 }
11396 else
11397 if (col == 9)
11398 {
11399 an.coord.T = int.Parse(val);
11400 }
11401 else
11402 if (col == 10)
11403 {
11404 x = double.Parse(val, CultureInfo.InvariantCulture);
11405 }
11406 else
11407 if (col == 11)
11408 {
11409 y = double.Parse(val, CultureInfo.InvariantCulture);
11410 }
11411 else
11412 if (col == 12)
11413 {
11414 w = double.Parse(val, CultureInfo.InvariantCulture);
11415 }
11416 else
11417 if (col == 13)
11418 {
11419 h = double.Parse(val, CultureInfo.InvariantCulture);
11420 }
11421 else
11422 if (col == 14)
11423 {
11424 //POINTS
11425 an.AddPoints(an.stringToPoints(val));
11426 points = false;
11427 an.BoundingBox = new RectangleD(x, y, w, h);
11428 }
11429 else
11430 if (col == 15)
11431 {
11432 //STROKECOLOR
11433 string[] st = val.Split(',');
11434 an.strokeColor = Color.FromArgb(int.Parse(st[0]), int.Parse(st[1]), int.Parse(st[2]), int.Parse(st[3]));
11435 }
11436 else
11437 if (col == 16)
11438 {
11439 //STROKECOLORW
11440 an.strokeWidth = double.Parse(val, CultureInfo.InvariantCulture);
11441 }
11442 else
11443 if (col == 17)
11444 {
11445 //FILLCOLOR
11446 string[] st = val.Split(',');
11447 an.fillColor = Color.FromArgb(int.Parse(st[0]), int.Parse(st[1]), int.Parse(st[2]), int.Parse(st[3]));
11448 }
11449 else
11450 if (col == 18)
11451 {
11452 //FONTSIZE
11453 double s = double.Parse(val, CultureInfo.InvariantCulture);
11454 an.fontSize = (float)s;
11455 an.family = "Times New Roman";
11456 }
11457 col++;
11458 val = "";
11459 }
11460 else
11461 val += c;
11462 }
11463
11464 return an;
11465 }

◆ ToImageSpace()

static PointD[] ToImageSpace ( List< PointD > p,
double stageSizeX,
double stageSizeY,
double physicalSizeX,
double physicalSizeY )
static

The function takes a list of points in stage space and converts them to image space using the provided stage and physical size parameters.

Parameters
pA list of PointD objects representing points in stage space.
stageSizeXThe width of the stage or canvas in pixels.
stageSizeYThe stageSizeY parameter represents the size of the stage or canvas in the Y-axis direction. It is used to calculate the Y-coordinate of each point in the p list in image space.
physicalSizeXThe physical size of the X-axis in the image space.
physicalSizeYThe physical size of the Y-axis in the coordinate system of the stage or image.
Returns
The method is returning an array of PointD objects.
9788 {
9789 PointD[] ps = new PointD[p.Count];
9790 for (int i = 0; i < p.Count; i++)
9791 {
9792 PointD pp = new PointD();
9793 pp.X = ((p[i].X - stageSizeX) / physicalSizeX);
9794 pp.Y = ((p[i].Y - stageSizeY) / physicalSizeY);
9795 ps[i] = pp;
9796 }
9797 return ps;
9798 }

◆ ToString()

override string ToString ( )

This function returns the filename of the object, and the location of the object in the 3D space.

Returns
The filename, and the location of the volume.
11737 {
11738 return "BioLib.Images.GetImage(\"" + id + "\")";
11739 }

◆ Update() [1/2]

void Update ( )

‍Update() is a function that calls the Update() function of the parent class

10647 {
10648 Update(this);
10649 }
void Update()
Definition Bio.cs:10646

◆ Update() [2/2]

static void Update ( BioImage b)
static

The function takes a BioImage object, opens the file, and returns a updated BioImage object.

Parameters
BioImageThis is the class that contains the image data.
9994 {
9995 b = OpenFile(b.file,b.Coordinate).Result;
9996 }

◆ UpdateBuffersPyramidal()

async Task UpdateBuffersPyramidal ( int level)

Updates the Buffers based on current pyramidal origin and resolution.

10020 {
10021 try
10022 {
10023 if (Type != ImageType.pyramidal)
10024 return;
10025 for (int i = 0; i < Buffers.Count; i++)
10026 {
10027 Buffers[i].Dispose();
10028 }
10029 Buffers.Clear();
10030 for (int z = 0; z < SizeZ; z++)
10031 {
10032 for (int c = 0; c < SizeC; c++)
10033 {
10034 for (int t = 0; t < SizeT; t++)
10035 {
10036 ZCT co = new ZCT(z, c, t);
10037 if (openSlideImage != null)
10038 {
10039 startos:
10040 double minX = PyramidalOrigin.X * Resolution;
10041 double worldY = -PyramidalOrigin.Y * Resolution; // Top of viewport in world coords (negative)
10042 double width = PyramidalSize.Width * Resolution;
10043 double height = PyramidalSize.Height * Resolution;
10044 // In OSM, extent goes from bottom-Y to top-Y, so:
10045 var extent = new Extent(minX, worldY - height, minX + width, worldY);
10046 var slicep = new OpenSlideGTK.SliceParame();
10047 slicep.DstPixelWidth = PyramidalSize.Width;
10048 slicep.DstPixelHeight = PyramidalSize.Height;
10049 slicep.Quality = 100;
10050 var sliceInfo = new OpenSlideGTK.SliceInfo
10051 {
10052 Extent = extent,
10054 Parame = slicep,
10055 };
10056 byte[] bts = await openslideBase.GetSliceAsync(sliceInfo, level, Coordinate);
10057 if (bts == null)
10058 {
10059 pyramidalOrigin = new PointD(0, 0);
10060 resolution = this.OpenSlideBase.Schema.Resolutions.Last().Value.UnitsPerPixel;
10061 goto startos;
10062 }
10063 Bitmap bmp = new Bitmap((int)Math.Round(OpenSlideBase.destExtent.Width), (int)Math.Round(OpenSlideBase.destExtent.Height), PixelFormat.Format32bppArgb, bts, co, "");
10064 Buffers.Add(bmp);
10065 }
10066 else
10067 {
10068 start:
10069 double minX = PyramidalOrigin.X;
10070 double minY = PyramidalOrigin.Y;
10071 double width = PyramidalSize.Width * Resolution;
10072 double height = PyramidalSize.Height * Resolution;
10073 double maxX = minX + width;
10074 double maxY = minY + height;
10075 var extent = new Extent(minX, minY, maxX, maxY);
10076 var slicep = new SliceParame();
10077 slicep.DstPixelWidth = PyramidalSize.Width;
10078 slicep.DstPixelHeight = PyramidalSize.Height;
10079 slicep.Quality = 100;
10080 var sliceInfo = new SliceInfo
10081 {
10082 Extent = extent,
10083 Resolution = resolution,
10084 Coordinate = co,
10085 Parame = slicep,
10086 };
10087 byte[] bts = await SlideBase.GetSlice(sliceInfo, PyramidalOrigin, PyramidalSize);
10088 if (bts == null)
10089 {
10090 pyramidalOrigin = new PointD(0, 0);
10091 resolution = this.OpenSlideBase.Schema.Resolutions.Last().Value.UnitsPerPixel;
10092 goto start;
10093 }
10094 //SlideBase.SetSliceInfo(level, this.Resolutions[0].PixelFormat, Coordinate);
10095 Bitmap bmp = new Bitmap((int)Math.Round(SlideBase.destExtent.Width), (int)Math.Round(SlideBase.destExtent.Height), PixelFormat.Format32bppArgb, bts, co, "");
10096 Buffers.Add(bmp);
10097 }
10098 }
10099 }
10100 }
10101 BioImage.AutoThreshold(this, false);
10102 if (bitsPerPixel > 8)
10103 StackThreshold(true);
10104 else
10105 StackThreshold(false);
10106 }
10107 catch (Exception e)
10108 {
10109 Console.WriteLine(e.Message);
10110 }
10111 }
OpenSlideImage openSlideImage
Definition Bio.cs:8523
void StackThreshold(bool bit16)
This function sets the minimum and maximum values of the image to the minimum and maximum values of t...
Definition Bio.cs:9669

◆ UpdateBuffersWells()

void UpdateBuffersWells ( )
10606 {
10607 try
10608 {
10609 if (Type != ImageType.well)
10610 return;
10611 for (int i = 0; i < Buffers.Count; i++)
10612 {
10613 Buffers[i].Dispose();
10614 }
10615 Buffers.Clear();
10616 if (imRead.getSeries() != Level)
10617 this.imRead.setSeries(Level);
10618 int w = imRead.getSizeX();
10619 int h = imRead.getSizeY();
10620 for (int z = 0; z < SizeZ; z++)
10621 {
10622 for (int c = 0; c < SizeC; c++)
10623 {
10624 for (int t = 0; t < SizeT; t++)
10625 {
10626 byte[] bm = this.imRead.openBytes(Coords[z, c, t]);
10627 Bitmap bmp = new Bitmap(w, h, Resolutions[Level].PixelFormat, bm, new ZCT(z, c, t), "");
10628 try { bmp.Stats = Statistics.FromBytes(bmp); } catch { }
10629 Buffers.Add(bmp);
10630 }
10631 }
10632 }
10633 BioImage.AutoThreshold(this, false);
10634 if (bitsPerPixel > 8)
10635 StackThreshold(true);
10636 else
10637 StackThreshold(false);
10638 }
10639 catch (Exception e)
10640 {
10641 Console.WriteLine(e.Message);
10642 }
10643 }

◆ VipsSupport()

static bool VipsSupport ( string file)
static

The function checks if a given file is supported by the Vips library and returns true if it is, false otherwise.

Parameters
fileThe "file" parameter is a string that represents the file path of the TIFF image that you want to load using the NetVips library.
Returns
The method is returning a boolean value. If the try block is executed successfully, it will return true. If an exception is caught, it will return false.
10786 {
10787
10788 try
10789 {
10790 if (isOME(file))
10791 {
10792 return true;
10793 }
10794 netim = NetVips.Image.Tiffload(file);
10795 Settings.AddSettings("VipsSupport", "true");
10796 netim.Close();
10797 netim.Dispose();
10798 }
10799 catch (Exception e)
10800 {
10801 Console.WriteLine(e.ToString());
10802 return false;
10803 }
10804 return true;
10805 }
static NetVips.Image netim
Definition Bio.cs:10776
Definition Settings.cs:11
static void AddSettings(string name, string val)
The function "AddSettings" adds a name-value pair to the "Default" dictionary.
Definition Settings.cs:52

Variable Documentation

◆ add

bool add
static

◆ b

return b

◆ bm

Bitmap bm
static

◆ bms

BioImage [] bms
static

◆ columns

const string columns = "ROIID,ROINAME,TYPE,ID,SHAPEINDEX,TEXT,S,C,Z,T,X,Y,W,H,POINTS,STROKECOLOR,STROKECOLORW,FILLCOLOR,FONTSIZE\n"
static

◆ comp

Enums.ForeignTiffCompression comp
static

◆ compLev

int compLev = 0
static

◆ FijiNet

alias FijiNet
extern

◆ imagef

MultiscaleNode imagef

◆ imRead

ImageReader imRead

◆ levelf

ResolutionLevelNode levelf

◆ levels

List<ResolutionLevelNode> levels

◆ multiscale

MultiscaleNode multiscale

◆ netim

NetVips.Image netim
static

◆ NewLine

const char NewLine = '\n'
static

◆ omes

bool omes
static

◆ openfile

string openfile
static

◆ openSlideImage

OpenSlideImage openSlideImage

◆ Planes

BioImage. Planes

◆ planes

List<PlaneResult> planes = new List<PlaneResult>()

◆ rng

Random rng = new Random()
static

◆ savefile

string savefile
static

◆ saveid

string saveid
static

◆ serie

int serie
static

◆ some

bool some
static

◆ sserie

int sserie
static

◆ sts

List<string> sts = new List<string>()
static

◆ tab

bool tab
static

◆ tifRead

Tiff tifRead

◆ useGPU

bool useGPU = true
static

◆ useVips

bool useVips = true
static

◆ vips

bool vips = true
static

◆ WellIndex

int WellIndex
getset

Gets or sets the currently active well-field index for HCS OME-Zarr plates. This is the zero-based index into ZarrWellLevels that identifies which well-field is displayed and fetched by GetTile and GetWellFieldBitmap.

For zarr plate images with a non-empty ZarrWellLevels, this property maps directly to the private level field — the same mechanism PlateTool and other callers already use via Level to switch wells. For non-plate images the getter returns 0 and the setter is a no-op.

5289 {
5290 get
5291 {
5292 if (ZarrWellLevels.Count > 0)
5293 return Math.Clamp(wellIndex, 0, ZarrWellLevels.Count - 1);
5294 return 0;
5295 }
5296 set
5297 {
5298 if (ZarrWellLevels.Count == 0)
5299 return;
5300 wellIndex = Math.Clamp(value, 0, ZarrWellLevels.Count - 1);
5301 }
5302 }

◆ ZarrDisplayMax

int ZarrDisplayMax
getset
5304{ get; set; }

◆ ZarrDisplayMin

ushort ZarrDisplayMin
getset
5305{ get; set; }

◆ zarrPlate

PlateMetadata zarrPlate

◆ zarrReader

OmeZarrReader zarrReader

◆ ZarrWellLevels

List<List<ResolutionLevelNode> > ZarrWellLevels = new()

For HCS Plate Zarr stores: one entry per well-field, each containing the resolution levels for that field. Indexed by WellPlate.Well.Sample.Index.