Skip to content

Commit

Permalink
4.0.0 OME type slide fix.
Browse files Browse the repository at this point in the history
Support for 16 bit slides.
  • Loading branch information
BiologyTools committed Apr 30, 2024
1 parent 3a96680 commit 7fc849c
Show file tree
Hide file tree
Showing 6 changed files with 435 additions and 41 deletions.
4 changes: 2 additions & 2 deletions BioImager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
<PackageTags>Microscopy;Biology;</PackageTags>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<Description>A .NET microscopy imaging application based on Bio library. Supports various microscopes by using imported libraries &amp; GUI automation. Supported libraries include Prior® &amp; Zeiss® &amp; all devices supported by Micromanager 2.0 and python-microscope.</Description>
<Version>3.9.1</Version>
<Version>4.0.0</Version>
<PackageProjectUrl></PackageProjectUrl>
<RepositoryUrl>https://github.com/BiologyTools/BioImager</RepositoryUrl>
<PackageLicenseFile></PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>Accelerated graphics fix.</PackageReleaseNotes>
<PackageReleaseNotes>OME type slides fix. Support for 16 bit slides.</PackageReleaseNotes>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>
<ItemGroup>
Expand Down
78 changes: 58 additions & 20 deletions Source/Bio/ISlideSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using AForge;
using Image = SixLabors.ImageSharp.Image;
namespace BioImager
{
public class LruCache<TileInformation, TValue>
Expand Down Expand Up @@ -62,6 +63,13 @@ public void Add(Info key, TValue value)
lruList.AddLast(newNode);
cacheMap[key] = newNode;
}
public void Dispose()
{
foreach (LinkedListNode<(Info key, TValue value)> item in cacheMap.Values)
{
lruList.Remove(item);
}
}
}
public class TileCache
{
Expand Down Expand Up @@ -110,6 +118,10 @@ private async Task<byte[]> LoadTile(TileInformation tileId)
return null;
}
}
public void Dispose()
{
cache.Dispose();
}
}

public class TileInformation
Expand Down Expand Up @@ -160,7 +172,6 @@ public static ISlideSource Create(BioImage source, SlideImage im, bool enableCac
}
#endregion
public double MinUnitsPerPixel { get; protected set; }
public static byte[] LastSlice;
public static Extent destExtent;
public static Extent sourceExtent;
public static double curUnitsPerPixel = 1;
Expand Down Expand Up @@ -194,9 +205,12 @@ public async Task<byte[]> GetSlice(SliceInfo sliceInfo)
{
try
{
NetVips.Image im = OpenSlideGTK.ImageUtil.JoinVips(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
LastSlice = im.WriteToMemory();
return LastSlice;
NetVips.Image im = null;
if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format16bppGrayScale)
im = ImageUtil.JoinVips16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
else if(this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format24bppRgb)
im = ImageUtil.JoinVipsRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
return im.WriteToMemory();
}
catch (Exception e)
{
Expand All @@ -207,16 +221,28 @@ public async Task<byte[]> GetSlice(SliceInfo sliceInfo)
}
try
{
Image<Rgb24> im = OpenSlideGTK.ImageUtil.Join(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
LastSlice = GetRgb24Bytes(im);
im.Dispose();
Image im = null;
if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format16bppGrayScale)
{
im = ImageUtil.Join16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
byte[] bts = Get16Bytes((Image<L16>)im);
im.Dispose();
return bts;
}
else if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == PixelFormat.Format24bppRgb)
{
im = ImageUtil.JoinRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight));
byte[] bts = GetRgb24Bytes((Image<Rgb24>)im);
im.Dispose();
return bts;
}
}
catch (Exception er)
{
Console.WriteLine(er.Message);
return null;
}
return LastSlice;
return null;
}
public byte[] GetRgb24Bytes(Image<Rgb24> image)
{
Expand All @@ -230,14 +256,34 @@ public byte[] GetRgb24Bytes(Image<Rgb24> image)
for (int x = 0; x < width; x++)
{
Rgb24 pixel = image[x, y];
rgbBytes[byteIndex++] = pixel.R;
rgbBytes[byteIndex++] = pixel.G;
rgbBytes[byteIndex++] = pixel.B;
rgbBytes[byteIndex++] = pixel.G;
rgbBytes[byteIndex++] = pixel.R;
}
}

return rgbBytes;
}
public byte[] Get16Bytes(Image<L16> image)
{
int width = image.Width;
int height = image.Height;
byte[] bytes = new byte[width * height * 2];

int byteIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
L16 pixel = image[x, y];
byte[] bts = BitConverter.GetBytes(pixel.PackedValue);
bytes[byteIndex++] = bts[0];
bytes[byteIndex++] = bts[1];
}
}

return bytes;
}

public SlideImage Image { get; set; }

Expand Down Expand Up @@ -291,11 +337,7 @@ public async Task<byte[]> GetTileAsync(TileInformation tileInfo)
var curTileWidth = (int)(tileInfo.Extent.MaxX > Schema.Extent.Width ? tileWidth - (tileInfo.Extent.MaxX - Schema.Extent.Width) / r : tileWidth);
var curTileHeight = (int)(-tileInfo.Extent.MinY > Schema.Extent.Height ? tileHeight - (-tileInfo.Extent.MinY - Schema.Extent.Height) / r : tileHeight);
var bgraData = await Image.ReadRegionAsync(tileInfo.Index.Level, (long)curLevelOffsetXPixel, (long)curLevelOffsetYPixel, curTileWidth, curTileHeight,tileInfo.Coordinate);
//We check to see if the data is valid.
if (bgraData.Length != curTileWidth * curTileHeight * 4)
return null;
byte[] bm = ConvertRgbaToRgb(bgraData);
return bm;
return bgraData;
}
public async Task<byte[]> GetTileAsync(BruTile.TileInfo tileInfo)
{
Expand All @@ -309,11 +351,7 @@ public async Task<byte[]> GetTileAsync(BruTile.TileInfo tileInfo)
var curTileWidth = (int)(tileInfo.Extent.MaxX > Schema.Extent.Width ? tileWidth - (tileInfo.Extent.MaxX - Schema.Extent.Width) / r : tileWidth);
var curTileHeight = (int)(-tileInfo.Extent.MinY > Schema.Extent.Height ? tileHeight - (-tileInfo.Extent.MinY - Schema.Extent.Height) / r : tileHeight);
var bgraData = await Image.ReadRegionAsync(tileInfo.Index.Level, (long)curLevelOffsetXPixel, (long)curLevelOffsetYPixel, curTileWidth, curTileHeight, new ZCT());
//We check to see if the data is valid.
if (bgraData.Length != curTileWidth * curTileHeight * 4)
return null;
byte[] bm = ConvertRgbaToRgb(bgraData);
return bm;
return bgraData;
}
public static byte[] ConvertRgbaToRgb(byte[] rgbaArray)
{
Expand Down
1 change: 0 additions & 1 deletion Source/Bio/SlideBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.PixelFormats;
using AForge;

namespace BioImager
{
Expand Down
24 changes: 7 additions & 17 deletions Source/Bio/SlideImage.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using AForge;
using OpenSlideGTK;
using OpenSlideGTK.Interop;
using org.checkerframework.common.returnsreceiver.qual;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -224,7 +223,7 @@ public int GetBestLevelForDownsample(double downsample)
/// <exception cref="OpenSlideException"/>
public unsafe byte[] ReadRegion(int level, long x, long y, long width, long height)
{
return BioImage.GetTile(BioImage, App.viewer.GetCoordinate(), level, (int)x, (int)y, (int)width, (int)height).RGBBytes;
return BioImage.GetTile(BioImage, App.viewer.GetCoordinate(), level, (int)x, (int)y, (int)width, (int)height).Bytes;
}

/// <summary>
Expand All @@ -237,22 +236,13 @@ public unsafe byte[] ReadRegion(int level, long x, long y, long width, long heig
/// <param name="height">The height of the region. Must be non-negative.</param>
/// <param name="data">The BGRA pixel data of this region.</param>
/// <returns></returns>
public unsafe bool TryReadRegion(int level, long x, long y, long width, long height, ZCT coord, out byte[] data)
public unsafe bool TryReadRegion(int level, long x, long y, long width, long height, out byte[] data, ZCT zct)
{
try
{
data = BioImage.GetTile(BioImage, coord, level, (int)x, (int)y, (int)width, (int)height).RGBBytes;
if (data == null)
return false;
else
return true;
}
catch (Exception e)
{
data = null;
data = BioImage.GetTile(BioImage, zct, level, (int)x, (int)y, (int)width, (int)height).Bytes;
if (data == null)
return false;
}

else
return true;
}

///<summary>
Expand Down Expand Up @@ -308,7 +298,7 @@ public async Task<byte[]> ReadRegionAsync(int level, long curLevelOffsetXPixel,
try
{
byte[] bts;
TryReadRegion(level, curLevelOffsetXPixel, curLevelOffsetYPixel, curTileWidth, curTileHeight, coord,out bts);
TryReadRegion(level, curLevelOffsetXPixel, curLevelOffsetYPixel, curTileWidth, curTileHeight,out bts,coord);
return bts;
}
catch (Exception e)
Expand Down
2 changes: 1 addition & 1 deletion Source/Bio/SlideTileLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public SlideTileLayer(
int minExtraTiles = -1,
int maxExtraTiles = -1,
Func<TileInfo, Task<IFeature>> fetchTileAsFeature = null)
: base(source, minTiles, maxTiles, dataFetchStrategy, renderFetchStrategy, minExtraTiles, maxExtraTiles, fetchTileAsFeature)
: base(source, minTiles, maxTiles, dataFetchStrategy, renderFetchStrategy, minExtraTiles, maxExtraTiles, (Func<BruTile.TileInfo, Task<IFeature>>)fetchTileAsFeature)
{
Name = "TileLayer";
_slideSource = source;
Expand Down
Loading

0 comments on commit 7fc849c

Please sign in to comment.