From 83e49aaa2b3718215e92e930bca455b5b2bec4ae Mon Sep 17 00:00:00 2001 From: RepoErik <84872500+BiologyTools@users.noreply.github.com> Date: Sun, 28 Apr 2024 19:54:05 +0300 Subject: [PATCH] OME Slides Fix. --- BioCore/Source/Bio.cs | 107 ++++----- BioCore/Source/Bio/ISlideSource.cs | 78 ++++-- BioCore/Source/Bio/SlideImage.cs | 4 +- BioCore/Source/Bio/Utilities.cs | 367 +++++++++++++++++++++++++++++ BioCore/Source/ImageView.cs | 26 +- 5 files changed, 477 insertions(+), 105 deletions(-) create mode 100644 BioCore/Source/Bio/Utilities.cs diff --git a/BioCore/Source/Bio.cs b/BioCore/Source/Bio.cs index 329af50..cb7ffd3 100644 --- a/BioCore/Source/Bio.cs +++ b/BioCore/Source/Bio.cs @@ -5969,20 +5969,20 @@ public async void UpdateBuffersPyramidal() } else { - start: + int l = LevelFromResolution(resolution); byte[] bts = await slideBase.GetSlice(new SliceInfo(PyramidalOrigin.X, PyramidalOrigin.Y, PyramidalSize.Width, PyramidalSize.Height, resolution, new ZCT())); - if (bts == null) + if (Resolutions[l].PixelFormat == PixelFormat.Format24bppRgb) { - if (PyramidalOrigin.X == 0 && PyramidalOrigin.Y == 0) - { - Resolution = 1; - } - pyramidalOrigin = new PointD(0, 0); - goto start; + BufferInfo bf = new BufferInfo((int)Math.Round(SlideBase.destExtent.Width), (int)Math.Round(SlideBase.destExtent.Height), PixelFormat.Format24bppRgb, bts, new ZCT(), ""); + bf.Stats = Statistics.FromBytes(bf); + Buffers.Add(bf); + } + else + { + BufferInfo bf = new BufferInfo((int)Math.Round(SlideBase.destExtent.Width), (int)Math.Round(SlideBase.destExtent.Height), PixelFormat.Format16bppGrayScale, bts, new ZCT(), ""); + bf.Stats = Statistics.FromBytes(bf); + Buffers.Add(bf); } - BufferInfo bf = new BufferInfo((int)Math.Round(SlideBase.destExtent.Width), (int)Math.Round(SlideBase.destExtent.Height), PixelFormat.Format24bppRgb, bts, new ZCT(), ""); - bf.Stats = Statistics.FromBytes(bf); - Buffers.Add(bf); } } BioImage.AutoThreshold(this, false); @@ -10169,24 +10169,24 @@ public static BioImage OpenOME(string file, int serie, bool tab, bool addToImage b.Buffers = new List(); if (b.Type == ImageType.pyramidal) - try + try + { + string st = OpenSlideImage.DetectVendor(file); + if (st != null && !file.EndsWith("ome.tif") && useOpenSlide) { - string st = OpenSlideImage.DetectVendor(file); - if (st != null && !file.EndsWith("ome.tif") && useOpenSlide) - { - b.openSlideImage = OpenSlideImage.Open(file); - b.openSlideBase = (OpenSlideBase)OpenSlideGTK.SlideSourceBase.Create(file); - } - else - { - b.slideBase = new SlideBase(b,SlideImage.Open(b)); - } + b.openSlideImage = OpenSlideImage.Open(file); + b.openSlideBase = (OpenSlideBase)OpenSlideGTK.SlideSourceBase.Create(file); } - catch (Exception e) + else { - Console.WriteLine(e.Message.ToString()); b.slideBase = new SlideBase(b,SlideImage.Open(b)); } + } + catch (Exception e) + { + Console.WriteLine(e.Message.ToString()); + b.slideBase = new SlideBase(b,SlideImage.Open(b)); + } // read the image data bytes int pages = reader.getImageCount(); @@ -10195,52 +10195,39 @@ public static BioImage OpenOME(string file, int serie, bool tab, bool addToImage int c = 0; int t = 0; pr.Status = "Reading Image Data"; - if (!tile) + try { - try + for (int p = 0; p < pages; p++) { - for (int p = 0; p < pages; p++) - { - BufferInfo bf; - pr.UpdateProgressF((float)p / (float)pages); - byte[] bytes = reader.openBytes(p); - bf = new BufferInfo(file, SizeX, SizeY, PixelFormat, bytes, new ZCT(z, c, t), p, null, b.littleEndian, inter); - b.Buffers.Add(bf); - } + BufferInfo bf; + pr.UpdateProgressF((float)p / (float)pages); + byte[] bytes = reader.openBytes(p); + bf = new BufferInfo(file, SizeX, SizeY, PixelFormat, bytes, new ZCT(z, c, t), p, null, b.littleEndian, inter); + b.Buffers.Add(bf); } - catch (Exception) + } + catch (Exception ex) + { + //If we failed to read an entire plane it is likely too large so we open as pyramidal. + b.Type = ImageType.pyramidal; + try { - //If we failed to read an entire plane it is likely too large so we open as pyramidal. - b.Type = ImageType.pyramidal; - try + string st = OpenSlideImage.DetectVendor(file); + if (st != null && !file.EndsWith("ome.tif") && useOpenSlide) { - string st = OpenSlideImage.DetectVendor(file); - if (st != null && !file.EndsWith("ome.tif") && useOpenSlide) - { - b.openSlideImage = OpenSlideImage.Open(file); - b.openSlideBase = (OpenSlideBase)OpenSlideGTK.SlideSourceBase.Create(file); - } - else - { - b.slideBase = new SlideBase(b, SlideImage.Open(b)); - } + b.openSlideImage = OpenSlideImage.Open(file); + b.openSlideBase = (OpenSlideBase)OpenSlideGTK.SlideSourceBase.Create(file); } - catch (Exception e) + else { - Console.WriteLine(e.Message.ToString()); b.slideBase = new SlideBase(b, SlideImage.Open(b)); } - b.imRead = reader; - for (int p = 0; p < pages; p++) - { - b.Buffers.Add(GetTile(b, p, serie, tilex, tiley, tileSizeX, tileSizeY)); - Statistics.CalcStatistics(b.Buffers.Last()); - } } - - } - else - { + catch (Exception e) + { + Console.WriteLine(e.Message.ToString()); + b.slideBase = new SlideBase(b, SlideImage.Open(b)); + } b.imRead = reader; for (int p = 0; p < pages; p++) { diff --git a/BioCore/Source/Bio/ISlideSource.cs b/BioCore/Source/Bio/ISlideSource.cs index 84996a6..db3e228 100644 --- a/BioCore/Source/Bio/ISlideSource.cs +++ b/BioCore/Source/Bio/ISlideSource.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using AForge; +using Image = SixLabors.ImageSharp.Image; namespace BioCore { public class LruCache @@ -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 { @@ -110,6 +118,10 @@ private async Task LoadTile(TileInformation tileId) return null; } } + public void Dispose() + { + cache.Dispose(); + } } public class TileInformation @@ -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; @@ -194,9 +205,12 @@ public async Task 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 == System.Drawing.Imaging.PixelFormat.Format16bppGrayScale) + im = ImageUtil.JoinVips16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight)); + else if(this.Image.BioImage.Resolutions[curLevel].PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb) + im = ImageUtil.JoinVipsRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight)); + return im.WriteToMemory(); } catch (Exception e) { @@ -207,16 +221,28 @@ public async Task GetSlice(SliceInfo sliceInfo) } try { - Image 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 == System.Drawing.Imaging.PixelFormat.Format16bppGrayScale) + { + im = ImageUtil.Join16(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight)); + byte[] bts = Get16Bytes((Image)im); + im.Dispose(); + return bts; + } + else if (this.Image.BioImage.Resolutions[curLevel].PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb) + { + im = ImageUtil.JoinRGB24(tiles, srcPixelExtent, new Extent(0, 0, dstPixelWidth, dstPixelHeight)); + byte[] bts = GetRgb24Bytes((Image)im); + im.Dispose(); + return bts; + } } catch (Exception er) { Console.WriteLine(er.Message); return null; } - return LastSlice; + return null; } public byte[] GetRgb24Bytes(Image image) { @@ -230,14 +256,34 @@ public byte[] GetRgb24Bytes(Image 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 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; } @@ -291,11 +337,7 @@ public async Task 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 GetTileAsync(BruTile.TileInfo tileInfo) { @@ -309,11 +351,7 @@ public async Task 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) { diff --git a/BioCore/Source/Bio/SlideImage.cs b/BioCore/Source/Bio/SlideImage.cs index c459dce..a5eba84 100644 --- a/BioCore/Source/Bio/SlideImage.cs +++ b/BioCore/Source/Bio/SlideImage.cs @@ -224,7 +224,7 @@ public int GetBestLevelForDownsample(double downsample) /// 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; } /// @@ -239,7 +239,7 @@ public unsafe byte[] ReadRegion(int level, long x, long y, long width, long heig /// public unsafe bool TryReadRegion(int level, long x, long y, long width, long height, out byte[] data, ZCT zct) { - data = BioImage.GetTile(BioImage, zct, level, (int)x, (int)y, (int)width, (int)height).RGBBytes; + data = BioImage.GetTile(BioImage, zct, level, (int)x, (int)y, (int)width, (int)height).Bytes; if (data == null) return false; else diff --git a/BioCore/Source/Bio/Utilities.cs b/BioCore/Source/Bio/Utilities.cs new file mode 100644 index 0000000..1799a57 --- /dev/null +++ b/BioCore/Source/Bio/Utilities.cs @@ -0,0 +1,367 @@ +using BruTile; +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System.IO; +using NetVips; +using System.Drawing.Imaging; +using SharpDX.Direct2D1.Effects; + +namespace BioCore +{ + public class ImageUtil + { + /// + /// Join by and cut by then scale to (only height an width is useful). + /// + /// tile with tile extent collection + /// canvas extent + /// jpeg output size + /// + public static Image JoinRGB24(IEnumerable> srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent) + { + if (srcPixelTiles == null || srcPixelTiles.Count() == 0) + return null; + srcPixelExtent = srcPixelExtent.ToIntegerExtent(); + dstPixelExtent = dstPixelExtent.ToIntegerExtent(); + int canvasWidth = (int)srcPixelExtent.Width; + int canvasHeight = (int)srcPixelExtent.Height; + var dstWidth = (int)dstPixelExtent.Width; + var dstHeight = (int)dstPixelExtent.Height; + Image canvas = new Image(canvasWidth, canvasHeight); + foreach (var tile in srcPixelTiles) + { + try + { + var tileExtent = tile.Item1.ToIntegerExtent(); + var intersect = srcPixelExtent.Intersect(tileExtent); + if (intersect.Width == 0 || intersect.Height == 0) + continue; + if(tile.Item2 == null) + continue; + Image tileRawData = (Image)CreateImageFromBytes(tile.Item2, (int)tileExtent.Width, (int)tileExtent.Height,PixelFormat.Format24bppRgb); + var tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX); + var tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY); + var canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX); + var canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY); + //We copy the tile region to the canvas. + for (int y = 0; y < intersect.Height; y++) + { + for (int x = 0; x < intersect.Width; x++) + { + int indx = canvasOffsetPixelX + x; + int indy = canvasOffsetPixelY + y; + int tindx = tileOffsetPixelX + x; + int tindy = tileOffsetPixelY + y; + canvas[indx, indy] = tileRawData[tindx, tindy]; + } + } + tileRawData.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + + } + if (dstWidth != canvasWidth || dstHeight != canvasHeight) + { + try + { + canvas.Mutate(x => x.Resize(dstWidth, dstHeight)); + return canvas; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + + } + return canvas; + } + + /// + /// Join by and cut by then scale to (only height an width is useful). + /// + /// tile with tile extent collection + /// canvas extent + /// jpeg output size + /// + public static ImageJoin16(IEnumerable> srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent) + { + if (srcPixelTiles == null || srcPixelTiles.Count() == 0) + return null; + srcPixelExtent = srcPixelExtent.ToIntegerExtent(); + dstPixelExtent = dstPixelExtent.ToIntegerExtent(); + int canvasWidth = (int)srcPixelExtent.Width; + int canvasHeight = (int)srcPixelExtent.Height; + var dstWidth = (int)dstPixelExtent.Width; + var dstHeight = (int)dstPixelExtent.Height; + Image canvas = new Image(canvasWidth, canvasHeight); + foreach (var tile in srcPixelTiles) + { + try + { + var tileExtent = tile.Item1.ToIntegerExtent(); + var intersect = srcPixelExtent.Intersect(tileExtent); + if (intersect.Width == 0 || intersect.Height == 0) + continue; + if (tile.Item2 == null) + continue; + Image tileRawData = (Image)CreateImageFromBytes(tile.Item2, (int)tileExtent.Width, (int)tileExtent.Height, PixelFormat.Format16bppGrayScale); + var tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX); + var tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY); + var canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX); + var canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY); + //We copy the tile region to the canvas. + for (int y = 0; y < intersect.Height; y++) + { + for (int x = 0; x < intersect.Width; x++) + { + int indx = canvasOffsetPixelX + x; + int indy = canvasOffsetPixelY + y; + int tindx = tileOffsetPixelX + x; + int tindy = tileOffsetPixelY + y; + canvas[indx, indy] = tileRawData[tindx, tindy]; + } + } + tileRawData.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + + } + if (dstWidth != canvasWidth || dstHeight != canvasHeight) + { + try + { + canvas.Mutate(x => x.Resize(dstWidth, dstHeight)); + return canvas; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + + } + return canvas; + } + + /// + /// Join by and cut by then scale to (only height an width is useful). + /// + /// tile with tile extent collection + /// canvas extent + /// jpeg output size + /// + public static unsafe NetVips.Image JoinVipsRGB24(IEnumerable> srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent) + { + if (srcPixelTiles == null || !srcPixelTiles.Any()) + return null; + + srcPixelExtent = srcPixelExtent.ToIntegerExtent(); + dstPixelExtent = dstPixelExtent.ToIntegerExtent(); + int canvasWidth = (int)srcPixelExtent.Width; + int canvasHeight = (int)srcPixelExtent.Height; + + // Create a base canvas. Adjust as necessary, for example, using a transparent image if needed. + NetVips.Image canvas = NetVips.Image.Black(canvasWidth, canvasHeight, bands: 3); + + foreach (var tile in srcPixelTiles) + { + if (tile.Item2 == null) + continue; + + fixed (byte* pTileData = tile.Item2) + { + var tileExtent = tile.Item1.ToIntegerExtent(); + NetVips.Image tileImage = NetVips.Image.NewFromMemory((IntPtr)pTileData, (ulong)tile.Item2.Length, (int)tileExtent.Width, (int)tileExtent.Height, 3, Enums.BandFormat.Uchar); + + // Calculate positions and sizes for cropping and inserting + var intersect = srcPixelExtent.Intersect(tileExtent); + if (intersect.Width == 0 || intersect.Height == 0) + continue; + + int tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX); + int tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY); + int canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX); + int canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY); + + using (var croppedTile = tileImage.Crop(tileOffsetPixelX, tileOffsetPixelY, (int)intersect.Width, (int)intersect.Height)) + { + // Instead of inserting directly, we composite over the base canvas + canvas = canvas.Composite2(croppedTile, Enums.BlendMode.Over, canvasOffsetPixelX, canvasOffsetPixelY); + } + } + } + + // Resize if the destination extent differs from the source canvas size + if ((int)dstPixelExtent.Width != canvasWidth || (int)dstPixelExtent.Height != canvasHeight) + { + double scaleX = (double)dstPixelExtent.Width / canvasWidth; + double scaleY = (double)dstPixelExtent.Height / canvasHeight; + canvas = canvas.Resize(scaleX, vscale: scaleY, kernel: Enums.Kernel.Nearest); + } + + return canvas; + } + + /// + /// Join by and cut by then scale to (only height an width is useful). + /// + /// tile with tile extent collection + /// canvas extent + /// jpeg output size + /// + public static unsafe NetVips.Image JoinVips16(IEnumerable> srcPixelTiles, Extent srcPixelExtent, Extent dstPixelExtent) + { + if (srcPixelTiles == null || !srcPixelTiles.Any()) + return null; + + srcPixelExtent = srcPixelExtent.ToIntegerExtent(); + dstPixelExtent = dstPixelExtent.ToIntegerExtent(); + int canvasWidth = (int)srcPixelExtent.Width; + int canvasHeight = (int)srcPixelExtent.Height; + + // Create a base canvas. Adjust as necessary, for example, using a transparent image if needed. + BufferInfo bf = new BufferInfo(canvasWidth, canvasHeight, PixelFormat.Format16bppGrayScale); + NetVips.Image canvas = NetVips.Image.NewFromMemory(bf.Bytes, bf.SizeX, bf.SizeX, 1, Enums.BandFormat.Ushort); + + foreach (var tile in srcPixelTiles) + { + if (tile.Item2 == null) + continue; + + fixed (byte* pTileData = tile.Item2) + { + var tileExtent = tile.Item1.ToIntegerExtent(); + NetVips.Image tileImage = NetVips.Image.NewFromMemory((IntPtr)pTileData, (ulong)tile.Item2.Length, (int)tileExtent.Width, (int)tileExtent.Height, 1, Enums.BandFormat.Ushort); + + // Calculate positions and sizes for cropping and inserting + var intersect = srcPixelExtent.Intersect(tileExtent); + if (intersect.Width == 0 || intersect.Height == 0) + continue; + + int tileOffsetPixelX = (int)Math.Ceiling(intersect.MinX - tileExtent.MinX); + int tileOffsetPixelY = (int)Math.Ceiling(intersect.MinY - tileExtent.MinY); + int canvasOffsetPixelX = (int)Math.Ceiling(intersect.MinX - srcPixelExtent.MinX); + int canvasOffsetPixelY = (int)Math.Ceiling(intersect.MinY - srcPixelExtent.MinY); + + using (var croppedTile = tileImage.Crop(tileOffsetPixelX, tileOffsetPixelY, (int)intersect.Width, (int)intersect.Height)) + { + // Instead of inserting directly, we composite over the base canvas + canvas = canvas.Composite2(croppedTile, Enums.BlendMode.Over, canvasOffsetPixelX, canvasOffsetPixelY); + } + } + } + + // Resize if the destination extent differs from the source canvas size + if ((int)dstPixelExtent.Width != canvasWidth || (int)dstPixelExtent.Height != canvasHeight) + { + double scaleX = (double)dstPixelExtent.Width / canvasWidth; + double scaleY = (double)dstPixelExtent.Height / canvasHeight; + canvas = canvas.Resize(scaleX, vscale: scaleY, kernel: Enums.Kernel.Nearest); + } + + return canvas; + } + + public static SixLabors.ImageSharp.Image CreateImageFromBytes(byte[] rgbBytes, int width, int height, PixelFormat px) + { + if (px == PixelFormat.Format24bppRgb) + { + if (rgbBytes.Length != width * height * 3) + { + throw new ArgumentException("Byte array size does not match the dimensions of the image"); + } + + // Create a new image of the specified size + Image image = new Image(width, height); + + // Index for the byte array + int byteIndex = 0; + + // Iterate over the image pixels + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // Create a color from the next three bytes + Rgb24 color = new Rgb24(rgbBytes[byteIndex], rgbBytes[byteIndex + 1], rgbBytes[byteIndex + 2]); + byteIndex += 3; + // Set the pixel + image[x, y] = color; + } + } + + return image; + } + else + if (px == PixelFormat.Format16bppGrayScale) + { + if (rgbBytes.Length != width * height * 2) + { + throw new ArgumentException("Byte array size does not match the dimensions of the image"); + } + + // Create a new image of the specified size + Image image = new Image(width, height); + + // Index for the byte array + int byteIndex = 0; + + // Iterate over the image pixels + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // Create a color from the next three bytes + L16 color = new L16(BitConverter.ToUInt16(rgbBytes, byteIndex)); + byteIndex += 2; + // Set the pixel + image[x, y] = color; + } + } + + return image; + } + else + if (px == PixelFormat.Format32bppArgb) + { + if (rgbBytes.Length != width * height * 4) + { + throw new ArgumentException("Byte array size does not match the dimensions of the image"); + } + + // Create a new image of the specified size + Image image = new Image(width, height); + + // Index for the byte array + int byteIndex = 0; + + // Iterate over the image pixels + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // Create a color from the next three bytes + Bgra32 color = new Bgra32(rgbBytes[byteIndex], rgbBytes[byteIndex + 1], rgbBytes[byteIndex + 2], rgbBytes[byteIndex + 3]); + byteIndex += 4; + // Set the pixel + image[x, y] = color; + } + } + + return image; + } + return null; + } + + } + +} diff --git a/BioCore/Source/ImageView.cs b/BioCore/Source/ImageView.cs index 1b409e1..0fa6213 100644 --- a/BioCore/Source/ImageView.cs +++ b/BioCore/Source/ImageView.cs @@ -274,28 +274,8 @@ private void InitPreview() } else { - Resolution res = SelectedImage.Resolutions.Last(); - double aspx = (double)res.SizeX / (double)res.SizeY; - double aspy = (double)res.SizeY / (double)res.SizeX; - overview = new Rectangle(0, 0, (int)(aspx * 120), (int)(aspy * 120)); - Bitmap bm; - ResizeNearestNeighbor re = new ResizeNearestNeighbor(overview.Width, overview.Height); - byte[] bts; - BufferInfo bf; - if (_openSlideBase != null) - { - bts = _openSlideBase.GetSlice(new OpenSlideGTK.SliceInfo(PyramidalOrigin.X, PyramidalOrigin.Y, SelectedImage.PyramidalSize.Width, SelectedImage.PyramidalSize.Height, SelectedImage.GetUnitPerPixel(Level))); - bf = new BufferInfo((int)Math.Round(OpenSlideBase.destExtent.Width), (int)Math.Round(OpenSlideBase.destExtent.Height), PixelFormat.Format24bppRgb, bts, new ZCT(), ""); - } - else - { - bts = _slideBase.GetSlice(new SliceInfo(PyramidalOrigin.X, PyramidalOrigin.Y, SelectedImage.PyramidalSize.Width, SelectedImage.PyramidalSize.Height, SelectedImage.GetUnitPerPixel(Level), GetCoordinate())).Result; - bf = new BufferInfo((int)Math.Round(SlideBase.destExtent.Width), (int)Math.Round(SlideBase.destExtent.Height), PixelFormat.Format24bppRgb, bts, new ZCT(), ""); - } - bm = re.Apply((Bitmap)bf.ImageRGB); - overviewBitmap = bm; + ShowOverview = false; } - ShowOverview = true; Console.WriteLine("Preview Initialized."); } public bool ShowOverview { get; set; } @@ -593,8 +573,6 @@ public double Resolution set { if (value < 0) return; - if (!openSlide && value >= SelectedImage.Resolutions.Count) - return; if (SelectedImage.Type == BioImage.ImageType.well && value > SelectedImage.Resolutions.Count - 1) return; double dp = Resolution / value; @@ -3279,6 +3257,8 @@ private void goToImageToolStripMenuItem_DropDownItemClicked(object sender, ToolS if (dBitmaps[i] != null) dBitmaps[i].Dispose(); } + if(SelectedImage.slideBase!=null) + SelectedImage.slideBase.cache.Dispose(); } /// The function is called when the user clicks on the "Go To" menu item in the context menu.