From df7a8dc78e75968180cbdce51142fae9f10399d1 Mon Sep 17 00:00:00 2001 From: RepoErik <84872500+BiologyTools@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:54:21 +0300 Subject: [PATCH] 4.2.0 ImageJ.NET Implementation. --- BioCore.sln | 14 - BioCore/BioCore.csproj | 4 +- BioCore/Source/App.cs | 6 +- BioCore/Source/BioConsole.cs | 6 +- BioCore/Source/ColorTool.cs | 12 +- BioCore/Source/Fiji.cs | 1769 +++++++++++++++++++++++++++++++++ BioCore/Source/ImageJ.cs | 1799 ++++------------------------------ BioCore/Source/ImageView.cs | 4 + BioCore/Source/TabsView.cs | 14 +- 9 files changed, 2007 insertions(+), 1621 deletions(-) create mode 100644 BioCore/Source/Fiji.cs diff --git a/BioCore.sln b/BioCore.sln index 604e07f..c8c5c52 100644 --- a/BioCore.sln +++ b/BioCore.sln @@ -9,8 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BioCoreApp", "..\BioCoreApp EndProject Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "BioCoreSetup", "..\BioCoreSetup\BioCoreSetup\BioCoreSetup.vdproj", "{9F71BCBA-FB42-48F3-8CE2-B492365008A6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BioLib", "..\BioLib\BioLib.csproj", "{C7A4FE0F-B632-48C8-AFD4-573973CC7E92}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,18 +55,6 @@ Global {9F71BCBA-FB42-48F3-8CE2-B492365008A6}.Release|x64.Build.0 = Release {9F71BCBA-FB42-48F3-8CE2-B492365008A6}.Release|x86.ActiveCfg = Release {9F71BCBA-FB42-48F3-8CE2-B492365008A6}.Release|x86.Build.0 = Release - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|x64.ActiveCfg = Debug|x64 - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|x64.Build.0 = Debug|x64 - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|x86.ActiveCfg = Debug|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Debug|x86.Build.0 = Debug|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|Any CPU.Build.0 = Release|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|x64.ActiveCfg = Release|x64 - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|x64.Build.0 = Release|x64 - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|x86.ActiveCfg = Release|Any CPU - {C7A4FE0F-B632-48C8-AFD4-573973CC7E92}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BioCore/BioCore.csproj b/BioCore/BioCore.csproj index ed95f5a..00f3c7d 100644 --- a/BioCore/BioCore.csproj +++ b/BioCore/BioCore.csproj @@ -63,6 +63,7 @@ + @@ -121,9 +122,6 @@ \ - - - Bitmap diff --git a/BioCore/Source/App.cs b/BioCore/Source/App.cs index 19774e4..af27a1c 100644 --- a/BioCore/Source/App.cs +++ b/BioCore/Source/App.cs @@ -49,7 +49,7 @@ public static void Initialize(bool requireImageJ = false) { BioImage.Initialize(); Microscope.Initialize(); - ImageJ.Initialize(); + Fiji.Initialize(requireImageJ); tabsView = new TabsView(); viewer = new ImageView(); stackTools = new StackTools(); @@ -384,8 +384,8 @@ private static void ItemClicked(object sender, EventArgs e) } else if (ts.Text.EndsWith(".ijm")) { - string s = File.ReadAllText(Path.GetDirectoryName(ImageJ.ImageJPath) + "/macros/" + ts.Text); - ImageJ.RunOnImage(s, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); + string s = File.ReadAllText(Path.GetDirectoryName(Fiji.ImageJPath) + "/macros/" + ts.Text); + Fiji.RunOnImage(s, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); } else { diff --git a/BioCore/Source/BioConsole.cs b/BioCore/Source/BioConsole.cs index 164ee3b..c9eb9b9 100644 --- a/BioCore/Source/BioConsole.cs +++ b/BioCore/Source/BioConsole.cs @@ -43,7 +43,7 @@ private void imagejBut_Click(object sender, EventArgs e) { if (ImageView.SelectedImage == null) return; - ImageJ.RunOnImage(textBox.Text, headlessBox.Checked, tabRadioBut.Checked, biofBox.Checked, newTabBox.Checked); + Fiji.RunOnImage(textBox.Text, headlessBox.Checked, tabRadioBut.Checked, biofBox.Checked, newTabBox.Checked); consoleBox.Text += textBox.Text + Environment.NewLine; textBox.Text = ""; string filename = ""; @@ -169,12 +169,12 @@ private void textBox_TextChanged(object sender, EventArgs e) preds.Clear(); i = 0; ind = -1; - foreach (var cs in ImageJ.Macro.Commands) + foreach (var cs in Fiji.Macro.Commands) { if (cs.Value.Name.StartsWith(pred)) preds.Add(cs.Value.Name); } - foreach (var cs in ImageJ.Macro.Functions) + foreach (var cs in Fiji.Macro.Functions) { if (cs.Value[0].Name.StartsWith(pred)) preds.Add(cs.Value[0].Name); diff --git a/BioCore/Source/ColorTool.cs b/BioCore/Source/ColorTool.cs index ea219c1..7da24dc 100644 --- a/BioCore/Source/ColorTool.cs +++ b/BioCore/Source/ColorTool.cs @@ -26,12 +26,12 @@ public ColorS Color colors = value; } } -/* + /// It updates the GUI public void UpdateGUI() { - color = new ColorS((ushort)redBox.Value, (ushort)greenBox.Value, (ushort)blueBox.Value); - colorPanel.BackColor = System.Drawing.Color.FromArgb(color.R / ushort.MaxValue,color.G / ushort.MaxValue,color.B / ushort.MaxValue); + colors = new ColorS((ushort)redBox.Value, (ushort)greenBox.Value, (ushort)blueBox.Value); + colorPanel.BackColor = System.Drawing.Color.FromArgb(colors.R / ushort.MaxValue,colors.G / ushort.MaxValue,colors.B / ushort.MaxValue); if (rBar.Value != redBox.Value) redBox.Value = rBar.Value; if (gBar.Value != greenBox.Value) @@ -39,7 +39,7 @@ public void UpdateGUI() if (bBar.Value != blueBox.Value) blueBox.Value = bBar.Value; } - */ + /* A constructor. */ public ColorTool() { @@ -50,8 +50,8 @@ public ColorTool() public ColorTool(ColorS col, int bitPerPixel) { InitializeComponent(); - this.bitsPerPixel = bitPerPixel; - if(bitsPerPixel == 8) + this.bitsPerPx = bitPerPixel; + if(bitsPerPx == 8) { rBar.Maximum = 255; gBar.Maximum = 255; diff --git a/BioCore/Source/Fiji.cs b/BioCore/Source/Fiji.cs new file mode 100644 index 0000000..4453b1a --- /dev/null +++ b/BioCore/Source/Fiji.cs @@ -0,0 +1,1769 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; +using System.IO; +using CSScripting; +using AForge; +using System.Runtime.InteropServices; +using BioLib; +using ij; +using RectangleD = AForge.RectangleD; +namespace BioCore +{ + public static class Fiji + { + public static class Macro + { + public class Command + { + public string Name { get; set; } + public string Arguments { get; set; } + public string Description { get; set; } + public Command(string name, string args, string description) + { + Name = name; + Arguments = args; + Description = description; + } + public override string ToString() + { + return Name.ToString(); + } + } + public class Function + { + public string Name { get; set; } + public string Arguments { get; set; } + public string Description { get; set; } + public Function(string name, string args, string description) + { + Name = name; + Arguments = args; + Description = description; + } + public override string ToString() + { + return Name.ToString(); + } + } + public static Dictionary Commands = new Dictionary(); + public static Dictionary> Functions = new Dictionary>(); + internal static void Initialize() + { + string[] sts = File.ReadAllLines(System.IO.Path.GetDirectoryName(Environment.ProcessPath) + "/macro-functions.txt"); + foreach (string s in sts) + { + string com = ""; + string args = ""; + string doc = ""; + bool indoc = false, inargs = false; + if (!s.StartsWith('#')) + { + for (int i = 0; i < s.Length; i++) + { + if (!inargs) + { + if (s[i] != '(') + com += s[i]; + else + inargs = true; + if (s[i] == ' ') + { + inargs = true; + indoc = true; + } + } + else + if (!indoc) + if (s[i] != ')') + args += s[i]; + else + indoc = true; + else + doc += s[i]; + } + if (!Functions.ContainsKey(com)) + Functions.Add(com, new List() { new Function(com,args, doc) }); + else + Functions[com].Add(new Function(com, args, doc)); + } + } + string[] cs = File.ReadAllLines(System.IO.Path.GetDirectoryName(Environment.ProcessPath) + "/macro-commands.csv"); + foreach (string s in cs) + { + string[] v = s.Split(','); + Commands.Add(v[0], new Command(v[0], v[1], "")); + } + } + } + static bool init = false; + public static bool Initialized { get { return init; } private set { init = true; } } + public static string ImageJPath; + public static bool UseFiji = false; + public static string ImageJMacroPath + { + get + { + if(System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + string p = System.IO.Path.GetDirectoryName(ImageJPath); + p = p.Remove(p.IndexOf("Fiji.app"), p.Length - p.IndexOf("Fiji.app")); + return p + "/Fiji.app/macros"; + } + else + return System.IO.Path.GetDirectoryName(ImageJPath) + "/macros/"; + } + } + public static List processes = new List(); + public static List Macros = new List(); + private static Random rng = new Random(); + public static void RunImageJ(string file, string param) + { + Console.WriteLine("RunImageJ(" + file + "," + param); + ImagePlus ip = ImageJ.GetImagePlus(ImageView.SelectedImage); + Console.WriteLine("ImageJ.GetImagePlus(" + ImageView.SelectedImage.ToString() + ")"); + WindowManager.setTempCurrentImage(ip); + Console.WriteLine("WindowManager.setTempCurrentImage(" +ip.ToString() + ")"); + IJ.runMacroFile(file, param); + ImageView.SelectedImage = ImageJ.GetBioImage(ip, ImageView.SelectedImage.Volume, ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.PhysicalSizeZ); + + } + /// It runs a macro in ImageJ + /// + /// @param file the path to the macro file + /// @param param The parameter to pass to the macro. + /// + /// @return The macro is being returned. + public static void RunMacro(string file, string param, bool useFiji = false) + { + if (UseFiji || useFiji) + { + if (!Initialized) + { + if (!Initialize(true)) + return; + } + file.Replace("/", "\\"); + Process pr = new Process(); + pr.StartInfo.FileName = ImageJPath; + pr.StartInfo.Arguments = "-macro " + file + " " + param; + pr.Start(); + processes.Add(pr); + } + else + { + RunImageJ(file, param); + } + BioLib.Recorder.Record(BioLib.Recorder.GetCurrentMethodInfo(),false, file, param); + } + /// It runs a macro in ImageJ + /// + /// @param con The macro code + /// @param param The parameters to pass to the macro. + /// @param headless Whether or not to run ImageJ in headless mode. + /// + /// @return Nothing. + public static void RunString(string con, string param, bool headless) + { + if (Fiji.UseFiji) + { + if (!Initialized) + { + if (!Initialize(true)) + return; + } + Process pr = new Process(); + pr.StartInfo.FileName = ImageJPath; + string te = rng.Next(0, 9999999).ToString(); + string p = Path.GetDirectoryName(Environment.ProcessPath) + "/" + te; + if (OperatingSystem.IsMacOS()) + { + Console.WriteLine(p); + pr.StartInfo.UseShellExecute = true; + } + File.WriteAllText(p, con); + if (headless) + pr.StartInfo.Arguments = "--headless -macro " + p + " " + param; + else + pr.StartInfo.Arguments = "-macro " + p + " " + param; + pr.Start(); + string donedir = Path.GetDirectoryName(Environment.ProcessPath); + donedir = donedir.Replace("\\", "/"); + File.Delete(Path.GetDirectoryName(Environment.ProcessPath) + "/done.txt"); + processes.Add(pr); + do + { + if (File.Exists(donedir + "/done.txt")) + { + do + { + try + { + File.Delete(donedir + "/done.txt"); + } + catch (Exception) + { + + } + } while (File.Exists(donedir + "/done.txt")); + pr.Kill(); + break; + } + } while (!pr.HasExited); + File.Delete(p); + } + else + { + ImagePlus ip = ImageJ.GetImagePlus(ImageView.SelectedImage); + WindowManager.setTempCurrentImage(ip); + IJ.runMacro(con, param); + ImageView.SelectedImage = ImageJ.GetBioImage(ip, ImageView.SelectedImage.Volume,ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.PhysicalSizeZ); + } + } + /// It runs a macro on the current image, saves the result as a new image, and then opens the + /// new image in a new tab + /// + /// @param con The ImageJ macro to run on the image. + /// @param headless Whether to run ImageJ in headless mode. + /// @param onTab If true, the imacro will be run on tab. If false, the macro will be run on selected image. + /// @param bioformats If true, the image is opened using the bioformats plugin. If false, the + /// image is opened using the default imagej open command. + /// + /// @return The image is being returned as a new tab. + public static void RunOnImage(string con, int index, bool headless, bool onTab, bool bioformats, bool resultInNewTab) + { + if (Fiji.UseFiji) + { + if (!Initialized) + { + if (!Initialize(true)) + return; + } + string filename = ""; + string dir = Path.GetDirectoryName(ImageView.SelectedImage.file); + dir = dir.Replace("\\", "/"); + if (ImageView.SelectedImage.ID.EndsWith(".ome.tif")) + { + filename = Path.GetFileNameWithoutExtension(ImageView.SelectedImage.ID); + filename = filename.Remove(filename.Length - 4, 4); + } + else + filename = Path.GetFileNameWithoutExtension(ImageView.SelectedImage.ID); + string file = dir + "/" + filename + "-temp.ome.tif"; + if (!bioformats) + file = dir + "/" + filename + ".tif"; + string donepath = Path.GetDirectoryName(Environment.ProcessPath); + donepath = donepath.Replace("\\", "/"); + string op = dir + "/" + ImageView.SelectedImage.ID.Replace("\\", "/"); + if (!File.Exists(op)) + { + BioImage.SaveOME(ImageView.SelectedImage, op); + } + string st = + "run(\"Bio-Formats Importer\", \"open=" + op + " autoscale color_mode=Default open_all_series display_rois rois_import=[ROI manager] view=Hyperstack stack_order=XYCZT\"); " + con + + "run(\"Bio-Formats Exporter\", \"save=" + file + " export compression=Uncompressed\"); " + + "dir = \"" + donepath + "\"" + + "File.saveString(\"done\", dir + \"/done.txt\");"; + if (!bioformats) + st = + "open(getArgument); " + con + + "saveAs(\"Tiff\",\"" + file + "\"); " + + "dir = \"" + donepath + "\"" + + "File.saveString(\"done\", dir + \"/done.txt\");"; + if (File.Exists(file) && bioformats) + File.Delete(file); + RunString(st, dir + "/" + ImageView.SelectedImage.ID, headless); + if (!File.Exists(file)) + return; + //If not in images we add it to a new tab. + if (Images.GetImage(file) == null) + { + BioImage bm = BioImage.OpenFile(file, index, false, false); + bm.ID = Path.GetFileName(file).Replace("-temp", ""); + bm.Filename = bm.ID; + bm.file = file; + Images.AddImage(bm, true); + } + else + { + BioImage b = BioImage.OpenFile(file, index, onTab, false); + b.ID = ImageView.SelectedImage.ID; + b.Filename = ImageView.SelectedImage.Filename; + b.file = dir + "/" + ImageView.SelectedImage.ID; + Images.UpdateImage(b); + App.viewer.Images[App.viewer.SelectedIndex] = b; + } + //If using bioformats we delete the temp file. + if (bioformats) + File.Delete(file); + // update image on main UI thread + App.viewer.UpdateImage(); + App.viewer.UpdateView(); + BioLib.Recorder.Record(BioLib.Recorder.GetCurrentMethodInfo(), false,con, index, headless, onTab, bioformats, resultInNewTab); + } + else + { + Console.WriteLine("RunOnImage(" + con + "," + index + "," + headless + "," + onTab + "," + bioformats + "," + resultInNewTab + ");"); + ImagePlus ip = ImageJ.GetImagePlus(ImageView.SelectedImage); + Console.WriteLine("ImageJ.GetImagePlus(" + ImageView.SelectedImage + ");"); + WindowManager.setTempCurrentImage(ip); + Console.WriteLine("WindowManager.setTempCurrentImage(" + ip + ");"); + Console.WriteLine("IJ.runMacro(" + con + ");"); + try + { + IJ.runMacro(con); + ImageView.SelectedImage = ImageJ.GetBioImage(ip, ImageView.SelectedImage.Volume, ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.PhysicalSizeZ); + } + catch (Exception e) + { + Console.WriteLine(e.Message.ToString()); + } + } + } + public static void RunOnImage(string s) + { + RunOnImage(s, 0, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, true); + } + public static void RunOnImage(string con, bool headless, bool onTab, bool bioformats, bool resultInNewTab) + { + RunOnImage(con,0,headless,onTab,bioformats,resultInNewTab); + } + /// This function is used to initialize the path of the ImageJ.exe file + /// + /// @param path The path to the ImageJ executable. + public static bool Initialize(bool imagej) + { + if (!imagej) + return false; + if (!SetImageJPath()) + return false; + Macro.Initialize(); + string[] ds = Directory.GetFiles(ImageJMacroPath); + foreach (string s in ds) + { + if(s.EndsWith(".ijm") || s.EndsWith(".txt")) + Macros.Add(new Macro.Command(Path.GetFileName(s), "", "")); + } + return true; + } + + /// This function creates a file chooser dialog that allows the user to select the location of + /// the ImageJ executable + /// + /// @return A boolean value. + public static bool SetImageJPath() + { + string st = BioLib.Settings.GetSettings("ImageJPath"); + if(st!="") + { + Initialized = true; + ImageJPath = BioLib.Settings.GetSettings("ImageJPath"); + return true; + } + string title = "Select ImageJ Executable Location"; + if (OperatingSystem.IsMacOS()) + title = "Select ImageJ Executable Location (Fiji.app/Contents/MacOS/ImageJ-macosx)"; + OpenFileDialog filechooser = new OpenFileDialog(); + filechooser.FileName = System.IO.Path.GetDirectoryName(Environment.ProcessPath); + if (filechooser.ShowDialog()!= DialogResult.OK) + return false; + Fiji.ImageJPath = filechooser.FileName; + BioLib.Settings.AddSettings("ImageJPath", filechooser.FileName); + BioLib.Settings.Save(); + Initialized = true; + return true; + } + + public class RoiDecoder + { + #region Params + // offsets + public static int VERSION_OFFSET = 4; + public static int TYPE = 6; + public static int TOP = 8; + public static int LEFT = 10; + public static int BOTTOM = 12; + public static int RIGHT = 14; + public static int N_COORDINATES = 16; + public static int X1 = 18; + public static int Y1 = 22; + public static int X2 = 26; + public static int Y2 = 30; + public static int XD = 18; + public static int YD = 22; + public static int WIDTHD = 26; + public static int HEIGHTD = 30; + public static int SIZE = 18; + public static int STROKE_WIDTH = 34; + public static int SHAPE_ROI_SIZE = 36; + public static int STROKE_COLOR = 40; + public static int FILL_COLOR = 44; + public static int SUBTYPE = 48; + public static int OPTIONS = 50; + public static int ARROW_STYLE = 52; + public static int FLOAT_PARAM = 52; //ellipse ratio or rotated rect width + public static int POINT_TYPE = 52; + public static int ARROW_HEAD_SIZE = 53; + public static int ROUNDED_RECT_ARC_SIZE = 54; + public static int POSITION = 56; + public static int HEADER2_OFFSET = 60; + public static int COORDINATES = 64; + // header2 offsets + public static int C_POSITION = 4; + public static int Z_POSITION = 8; + public static int T_POSITION = 12; + public static int NAME_OFFSET = 16; + public static int NAME_LENGTH = 20; + public static int OVERLAY_LABEL_COLOR = 24; + public static int OVERLAY_FONT_SIZE = 28; //short + public static int GROUP = 30; //byte + public static int IMAGE_OPACITY = 31; //byte + public static int IMAGE_SIZE = 32; //int + public static int FLOAT_STROKE_WIDTH = 36; //float + public static int ROI_PROPS_OFFSET = 40; + public static int ROI_PROPS_LENGTH = 44; + public static int COUNTERS_OFFSET = 48; + + // subtypes + public static int TEXT = 1; + public static int ARROW = 2; + public static int ELLIPSE = 3; + public static int IMAGE = 4; + public static int ROTATED_RECT = 5; + + // options + public static int SPLINE_FIT = 1; + public static int DOUBLE_HEADED = 2; + public static int OUTLINE = 4; + public static int OVERLAY_LABELS = 8; + public static int OVERLAY_NAMES = 16; + public static int OVERLAY_BACKGROUNDS = 32; + public static int OVERLAY_BOLD = 64; + public static int SUB_PIXEL_RESOLUTION = 128; + public static int DRAW_OFFSET = 256; + public static int ZERO_TRANSPARENT = 512; + public static int SHOW_LABELS = 1024; + public static int SCALE_LABELS = 2048; + public static int PROMPT_BEFORE_DELETING = 4096; //points + public static int SCALE_STROKE_WIDTH = 8192; + + // types + private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, + freehand = 7, traced = 8, angle = 9, point = 10; + + private byte[] data; + private string path; + private MemoryStream ins; + private string name; + private int size; + #endregion + + /** Constructs an RoiDecoder using a file path. */ + public RoiDecoder(string path) + { + this.path = path; + } + + /** Constructs an RoiDecoder using a byte array. */ + public RoiDecoder(byte[] bytes, string name) + { + ins = new MemoryStream(bytes); + this.name = name; + this.size = bytes.Length; + } + + /** Opens the ROI at the specified path. Returns null if there is an error. */ + public static ROI open(string path) + { + ROI roi = null; + RoiDecoder rd = new RoiDecoder(path); + roi = rd.getRoi(); + return roi; + } + + /** Returns the ROI. */ + public ROI getRoi() + { + ROI roi = new ROI(); + data = File.ReadAllBytes(path); + size = data.Length; + if (getByte(0) != 73 || getByte(1) != 111) //"Iout" + throw new IOException("This is not an ImageJ ROI"); + int version = getShort(VERSION_OFFSET); + int type = getByte(TYPE); + int subtype = getShort(SUBTYPE); + int top = getShort(TOP); + int left = getShort(LEFT); + int bottom = getShort(BOTTOM); + int right = getShort(RIGHT); + int width = right - left; + int height = bottom - top; + int n = getUnsignedShort(N_COORDINATES); + if (n == 0) + n = getInt(SIZE); + int options = getShort(OPTIONS); + int position = getInt(POSITION); + int hdr2Offset = getInt(HEADER2_OFFSET); + int channel = 0, slice = 0, frame = 0; + int overlayLabelColor = 0; + int overlayFontSize = 0; + int group = 0; + int imageOpacity = 0; + int imageSize = 0; + bool subPixelResolution = (options & SUB_PIXEL_RESOLUTION) != 0 && version >= 222; + bool drawOffset = subPixelResolution && (options & DRAW_OFFSET) != 0; + bool scaleStrokeWidth = true; + if (version >= 228) + scaleStrokeWidth = (options & SCALE_STROKE_WIDTH) != 0; + + bool subPixelRect = version >= 223 && subPixelResolution && (type == rect || type == oval); + double xd = 0.0, yd = 0.0, widthd = 0.0, heightd = 0.0; + if (subPixelRect) { + xd = getFloat(XD); + yd = getFloat(YD); + widthd = getFloat(WIDTHD); + heightd = getFloat(HEIGHTD); + roi.subPixel = true; + } + + if (hdr2Offset > 0 && hdr2Offset + IMAGE_SIZE + 4 <= size) + { + channel = getInt(hdr2Offset + C_POSITION); + slice = getInt(hdr2Offset + Z_POSITION); + frame = getInt(hdr2Offset + T_POSITION); + overlayLabelColor = getInt(hdr2Offset + OVERLAY_LABEL_COLOR); + overlayFontSize = getShort(hdr2Offset + OVERLAY_FONT_SIZE); + imageOpacity = getByte(hdr2Offset + IMAGE_OPACITY); + imageSize = getInt(hdr2Offset + IMAGE_SIZE); + group = getByte(hdr2Offset + GROUP); + } + + if (name != null && name.EndsWith(".roi")) + name = name.Substring(0, name.Length - 4); + bool isComposite = getInt(SHAPE_ROI_SIZE) > 0; + + + /* + if (isComposite) + { + roi = getShapeRoi(); + if (version >= 218) + getStrokeWidthAndColor(roi, hdr2Offset, scaleStrokeWidth); + roi.coord.Z = position; + if (channel > 0 || slice > 0 || frame > 0) + { + roi.coord.C = channel; roi.coord.Z = slice; roi.coord.T = frame; + } + decodeOverlayOptions(roi, version, options, overlayLabelColor, overlayFontSize); + if (version >= 224) + { + string props = getRoiProps(); + if (props != null) + roi.properties = props; + } + if (version >= 228 && group > 0) + roi.serie = group; + return roi; + } + */ + switch (type) + { + case 1: //Rect + if (subPixelRect) + roi = ROI.CreateRectangle(new AForge.ZCT(slice-1, channel - 1, frame - 1), xd, yd, widthd, heightd); + else + roi = ROI.CreateRectangle(new AForge.ZCT(slice - 1, channel - 1, frame - 1), left, top, width, height); + int arcSize = getShort(ROUNDED_RECT_ARC_SIZE); + if (arcSize > 0) + throw new NotSupportedException("Type rounded rectangle not supported."); + break; + case 2: //Ellipse + if (subPixelRect) + roi = ROI.CreateEllipse(new AForge.ZCT(slice - 1, channel - 1, frame - 1), xd, yd, widthd, heightd); + else + roi = ROI.CreateEllipse(new AForge.ZCT(slice - 1, channel - 1, frame - 1), left, top, width, height); + break; + case 3: //Line + float x1 = getFloat(X1); + float y1 = getFloat(Y1); + float x2 = getFloat(X2); + float y2 = getFloat(Y2); + + if (subtype == ARROW) + { + throw new NotSupportedException("Type arrow not supported."); + /* + roi = new Arrow(x1, y1, x2, y2); + ((Arrow)roi).setDoubleHeaded((options & DOUBLE_HEADED) != 0); + ((Arrow)roi).setOutline((options & OUTLINE) != 0); + int style = getByte(ARROW_STYLE); + if (style >= Arrow.FILLED && style <= Arrow.BAR) + ((Arrow)roi).setStyle(style); + int headSize = getByte(ARROW_HEAD_SIZE); + if (headSize >= 0 && style <= 30) + ((Arrow)roi).setHeadSize(headSize); + */ + } + else + { + roi = ROI.CreateLine(new AForge.ZCT(slice, channel, frame), new AForge.PointD(x1, y1), new AForge.PointD(x2, y2)); + //roi.setDrawOffset(drawOffset); + } + + break; + case 0: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + //IJ.log("type: "+type); + //IJ.log("n: "+n); + //ij.IJ.log("rect: "+left+","+top+" "+width+" "+height); + if (n == 0 || n < 0) break; + int[] x = new int[n]; + int[] y = new int[n]; + float[] xf = null; + float[] yf = null; + int base1 = COORDINATES; + int base2 = base1 + 2 * n; + int xtmp, ytmp; + for (int i = 0; i < n; i++) + { + xtmp = getShort(base1 + i * 2); + if (xtmp < 0) xtmp = 0; + ytmp = getShort(base2 + i * 2); + if (ytmp < 0) ytmp = 0; + x[i] = left + xtmp; + y[i] = top + ytmp; + } + if (subPixelResolution) + { + xf = new float[n]; + yf = new float[n]; + base1 = COORDINATES + 4 * n; + base2 = base1 + 4 * n; + for (int i = 0; i < n; i++) + { + xf[i] = getFloat(base1 + i * 4); + yf[i] = getFloat(base2 + i * 4); + } + } + if (type == point) + { + //TODO implement non subpizel ROI + if (subPixelResolution) + { + roi.AddPoints(xf, yf); + } + else + roi.AddPoints(x, y); + if (version >= 226) + { + //((PointRoi)roi).setPointType(getByte(POINT_TYPE)); + roi.strokeWidth = getShort(STROKE_WIDTH); + } + //if ((options & SHOW_LABELS) != 0 && !ij.Prefs.noPointLabels) + // ((PointRoi)roi).setShowLabels(true); + //if ((options & PROMPT_BEFORE_DELETING) != 0) + // ((PointRoi)roi).promptBeforeDeleting(true); + roi.type = ROI.Type.Point; + break; + } + if (type == polygon) + roi.type = ROI.Type.Polygon; + else if (type == freehand) + { + roi.type = ROI.Type.Freeform; + if (subtype == ELLIPSE || subtype == ROTATED_RECT) + { + throw new NotSupportedException("ROI type not supported."); + /* + double ex1 = getFloat(X1); + double ey1 = getFloat(Y1); + double ex2 = getFloat(X2); + double ey2 = getFloat(Y2); + double param = getFloat(FLOAT_PARAM); + if (subtype == ROTATED_RECT) + roi = new RotatedRectRoi(ex1, ey1, ex2, ey2, param); + else + roi = new EllipseRoi(ex1, ey1, ex2, ey2, param); + break; + */ + } + } + else if (type == traced) + roi.type = ROI.Type.Polyline; + else if (type == polyline) + roi.type = ROI.Type.Polyline; + else if (type == freeline) + roi.type = ROI.Type.Polyline; + else if (type == angle) + roi.type = ROI.Type.Point; + else + roi.type = ROI.Type.Freeform; + if (subPixelResolution) + { + roi.AddPoints(xf, yf); + //roi = new PolygonRoi(xf, yf, n, roiType); + //roi.setDrawOffset(drawOffset); + } + else + roi.AddPoints(x, y); + break; + default: + throw new IOException("Unrecognized ROI type: " + type); + } + if (roi == null) + return null; + roi.roiName = getRoiName(); + + // read stroke width, stroke color and fill color (1.43i or later) + if (version >= 218) + { + getStrokeWidthAndColor(roi, hdr2Offset, scaleStrokeWidth); + /* + if (type == point) + roi.setStrokeWidth(0); + bool splineFit = (options & SPLINE_FIT) != 0; + if (splineFit && roi instanceof PolygonRoi) + ((PolygonRoi)roi).fitSpline(); + */ + } + + if (version >= 218 && subtype == TEXT) + { + getTextRoi(roi, version); + roi.type = ROI.Type.Label; + } + /* + if (version >= 221 && subtype == IMAGE) + roi = getImageRoi(roi, imageOpacity, imageSize, options); + + if (version >= 224) + { + string props = getRoiProps(); + if (props != null) + roi.setProperties(props); + } + + if (version >= 227) + { + int[] counters = getPointCounters(n); + if (counters != null && (roi instanceof PointRoi)) + ((PointRoi)roi).setCounters(counters); + } + */ + // set group (1.52t or later) + if (version >= 228 && group > 0) + roi.serie = group; + + roi.coord.Z = position; + if (channel > 0 || slice > 0 || frame > 0) + roi.coord = new AForge.ZCT(slice - 1, channel - 1, frame - 1); //-1 because our ROI coordinates are 0 based + //decodeOverlayOptions(roi, version, options, overlayLabelColor, overlayFontSize); + + //We convert pixel to subpixel + if (!roi.subPixel) + { + for (int i = 0; i < roi.PointsD.Count; i++) + { + AForge.PointD pd = ImageView.SelectedImage.ToStageSpace(roi.PointsD[i]); + roi.PointsD[i] = pd; + roi.UpdateBoundingBox(); + } + } + if (roi.type == ROI.Type.Polygon || roi.type == ROI.Type.Freeform) + roi.closed = true; + return roi; + } + /* + void decodeOverlayOptions(ROI roi, int version, int options, int color, int fontSize) + { + + Overlay proto = new Overlay(); + proto.drawLabels((options & OVERLAY_LABELS) != 0); + proto.drawNames((options & OVERLAY_NAMES) != 0); + proto.drawBackgrounds((options & OVERLAY_BACKGROUNDS) != 0); + if (version >= 220 && color != 0) + proto.setLabelColor(new Color(color)); + bool bold = (options & OVERLAY_BOLD) != 0; + bool scalable = (options & SCALE_LABELS) != 0; + if (fontSize > 0 || bold || scalable) + { + proto.setLabelFont(new Font("SansSerif", bold ? Font.BOLD : Font.PLAIN, fontSize), scalable); + } + roi.setPrototypeOverlay(proto); + + } + */ + void getStrokeWidthAndColor(ROI roi, int hdr2Offset, bool scaleStrokeWidth) + { + double strokeWidth = getShort(STROKE_WIDTH); + if (hdr2Offset > 0) + { + double strokeWidthD = getFloat(hdr2Offset + FLOAT_STROKE_WIDTH); + if (strokeWidthD > 0.0) + strokeWidth = strokeWidthD; + } + if (strokeWidth > 0.0) + { + roi.strokeWidth = strokeWidth; + } + int strokeColor = getInt(STROKE_COLOR); + if (strokeColor != 0) + { + byte[] bts = BitConverter.GetBytes(strokeColor); + AForge.Color c = AForge.Color.FromArgb(bts[0], bts[1], bts[2], bts[3]); + roi.strokeColor = c; + } + int fillColor = getInt(FILL_COLOR); + if (fillColor != 0) + { + byte[] bts = BitConverter.GetBytes(strokeColor); + AForge.Color c = AForge.Color.FromArgb(bts[0], bts[1], bts[2], bts[3]); + roi.fillColor = c; + } + } + /* + public ROI getShapeRoi() + { + int type = getByte(TYPE); + if (type!=rect) + throw new NotSupportedException("Invalid composite ROI type"); + int top = getShort(TOP); + int left = getShort(LEFT); + int bottom = getShort(BOTTOM); + int right = getShort(RIGHT); + int width = right - left; + int height = bottom - top; + int n = getInt(SHAPE_ROI_SIZE); + + ROI roi = new ROI(); + float[] shapeArray = new float[n]; + int bas = COORDINATES; + for (int i = 0; i < n; i++) + { + shapeArray[i] = getFloat(bas); + bas += 4; + } + roi = new ShapeRoi(shapeArray); + roi.setName(getRoiName()); + return roi; + } + */ + void getTextRoi(ROI roi, int version) + { + AForge.Rectangle r = roi.BoundingBox.ToRectangleInt(); + int hdrSize = 64; + int size = getInt(hdrSize); + int styleAndJustification = getInt(hdrSize + 4); + int style = styleAndJustification & 255; + int justification = (styleAndJustification >> 8) & 3; + bool drawStringMode = (styleAndJustification & 1024) != 0; + int nameLength = getInt(hdrSize + 8); + int textLength = getInt(hdrSize + 12); + char[] name = new char[nameLength]; + char[] text = new char[textLength]; + for (int i = 0; i < nameLength; i++) + name[i] = (char)getShort(hdrSize + 16 + i * 2); + for (int i = 0; i < textLength; i++) + text[i] = (char)getShort(hdrSize + 16 + nameLength * 2 + i * 2); + double angle = version >= 225 ? getFloat(hdrSize + 16 + nameLength * 2 + textLength * 2) : 0f; + //Font font = new Font(new string(name), style, size); + roi.family = new string(name); + roi.Text = new string(text); + roi.slant = Cairo.FontSlant.Normal; + roi.weight = Cairo.FontWeight.Normal; + roi.fontSize = size; + /* + if (roi.subPixel) + { + RectangleD fb = roi.Rect; + roi2 = new TextRoi(fb.getX(), fb.getY(), fb.getWidth(), fb.getHeight(), new string(text), font); + } + else + roi2 = new TextRoi(r.x, r.y, r.width, r.height, new string(text), font); + + roi.strokeColor + roi2.setFillColor(roi.getFillColor()); + roi2.setName(getRoiName()); + roi2.setJustification(justification); + roi2.setDrawStringMode(drawStringMode); + roi2.setAngle(angle); + return roi2; + */ + } + + string getRoiName() + { + string fileName = name; + int hdr2Offset = getInt(HEADER2_OFFSET); + if (hdr2Offset == 0) + return fileName; + int offset = getInt(hdr2Offset + NAME_OFFSET); + int Length = getInt(hdr2Offset + NAME_LENGTH); + if (offset == 0 || Length == 0) + return fileName; + if (offset + Length * 2 > size) + return fileName; + char[] namem = new char[Length]; + for (int i = 0; i < Length; i++) + namem[i] = (char)getShort(offset + i * 2); + return new string(namem); + } + + string getRoiProps() + { + int hdr2Offset = getInt(HEADER2_OFFSET); + if (hdr2Offset == 0) + return null; + int offset = getInt(hdr2Offset + ROI_PROPS_OFFSET); + int Length = getInt(hdr2Offset + ROI_PROPS_LENGTH); + if (offset == 0 || Length == 0) + return null; + if (offset + Length * 2 > size) + return null; + char[] props = new char[Length]; + for (int i = 0; i < Length; i++) + props[i] = (char)getShort(offset + i * 2); + return new string(props); + } + + int[] getPointCounters(int n) + { + int hdr2Offset = getInt(HEADER2_OFFSET); + if (hdr2Offset == 0) + return null; + int offset = getInt(hdr2Offset + COUNTERS_OFFSET); + if (offset == 0) + return null; + if (offset + n * 4 > data.Length) + return null; + int[] counters = new int[n]; + for (int i = 0; i < n; i++) + counters[i] = getInt(offset + i * 4); + return counters; + } + + + int getByte(int bas) + { + return data[bas] & 255; + } + + int getShort(int bas) + { + int b0 = data[bas] & 255; + int b1 = data[bas + 1] & 255; + int n = (short)((b0 << 8) + b1); + if (n < -5000) + n = (b0 << 8) + b1; // assume n>32767 and unsigned + return n; + } + + int getUnsignedShort(int bas) + { + int b0 = data[bas] & 255; + int b1 = data[bas + 1] & 255; + return (b0 << 8) + b1; + } + + int getInt(int bas) + { + int b0 = data[bas] & 255; + int b1 = data[bas + 1] & 255; + int b2 = data[bas + 2] & 255; + int b3 = data[bas + 3] & 255; + return ((b0 << 24) + (b1 << 16) + (b2 << 8) + b3); + } + + float getFloat(int bas) + { + return BitConverter.Int32BitsToSingle(getInt(bas)); + } + + /** Opens an ROI from a byte array. */ + public static ROI openFromByteArray(byte[] bytes) + { + ROI roi = null; + if (bytes == null || bytes.Length == 0) + return roi; + try + { + RoiDecoder decoder = new RoiDecoder(bytes, null); + roi = decoder.getRoi(); + } + catch (IOException e) + { + return null; + } + return roi; + } + + } + + /// The function `GetImageJType` takes in a `ROI` object and returns an integer representing the + /// type of the ROI in ImageJ. + /// + /// @param ROI The ROI parameter is an object of type ROI, which represents a region of interest + /// in an image. It has a property called "type" which indicates the type of the ROI. + /// + /// @return The method is returning an integer value that represents the ImageJ type of the + /// given ROI. + static int GetImageJType(ROI roi) + { + //private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, freehand = 7, + // traced = 8, angle = 9, point = 10; + switch (roi.type) + { + case ROI.Type.Rectangle: + return 1; + case ROI.Type.Point: + return 10; + case ROI.Type.Line: + return 3; + case ROI.Type.Polygon: + return 0; + case ROI.Type.Polyline: + return 5; + case ROI.Type.Freeform: + return 7; + case ROI.Type.Ellipse: + return 2; + case ROI.Type.Label: + default: + return 0; + } + } + + /// The function "GetPointsXY" takes a ROI object and returns the X and Y coordinates of its + /// points as arrays. + /// + /// @param ROI The ROI parameter is an object that represents a region of interest. It contains + /// a collection of points (PointsD) that define the boundary of the region. + /// @param xp An array of integers representing the x-coordinates of the points in the ROI. + /// @param yp The `yp` parameter is an output parameter of type `int[]`. It is used to return + /// the y-coordinates of the points in the `ROI` object. + static void GetPointsXY(ROI roi, out int[] xp, out int[] yp) + { + int[] x = new int[roi.PointsD.Count]; + int[] y = new int[roi.PointsD.Count]; + for (int i = 0; i < roi.PointsD.Count; i++) + { + PointD pd = ImageView.SelectedImage.ToImageSpace(roi.PointsD[i]); + x[i] = (int)pd.X; + y[i] = (int)pd.Y; + } + xp = x; + yp = y; + + } + + /// The function "GetXY" takes a region of interest (ROI) and returns the corresponding X and Y + /// coordinates in image space. + /// + /// @param ROI The ROI parameter is of type ROI, which is likely a custom class representing a + /// region of interest. It contains information about the position and size of the region. + /// @param x An output parameter that will store the X coordinate of the ROI in image space. + /// @param y The "y" parameter is an output parameter that will hold the y-coordinate of the ROI + /// (Region of Interest) after the method is called. + static void GetXY(ROI roi,out float x, out float y) + { + PointD pd = ImageView.SelectedImage.ToImageSpace(new PointD(roi.X,roi.Y)); + x = (float)pd.X; + y = (float)pd.Y; + } + /// The function "GetWH" takes a ROI (region of interest) and returns the width and height of + /// the ROI in terms of image size. + /// + /// @param ROI The ROI parameter is an object that represents a region of interest. It likely + /// contains information such as the position (x, y) and size (width, height) of the region. + /// @param w The width of the ROI (Region of Interest) in the selected image. + /// @param h The "h" parameter is an output parameter of type float. It is used to store the + /// height value calculated in the method. + static void GetWH(ROI roi, out float w, out float h) + { + w = (float)ImageView.SelectedImage.ToImageSizeX(roi.W); + h = (float)ImageView.SelectedImage.ToImageSizeY(roi.H); + } + /// The function rightMove takes an integer value and a position as input, and returns the value + /// after performing a right shift operation by the specified position. + /// + /// @param value The value is an integer that represents the number you want to perform a right + /// move on. + /// @param pos The "pos" parameter represents the number of positions to move the bits to the + /// right. + /// + /// @return the value after performing a right shift operation. + static int rightMove(int value, int pos) + { + if (pos != 0) + { + int mask = 0x7fffffff; + value >>= 1; + value &= mask; + value >>= pos - 1; + } + return value; + } + public class RoiEncoder + { + static int HEADER_SIZE = 64; + static int HEADER2_SIZE = 64; + static int VERSION = 228; // v1.52t (roi groups, scale stroke width) + private string path; + private FileStream f; + private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, freehand = 7, + traced = 8, angle = 9, point = 10; + private byte[] data; + private string roiName; + private int roiNameSize; + private string roiProps; + private int roiPropsSize; + private int countersSize; + private int[] counters; + private bool subres = true; + + /** Creates an RoiEncoder using the specified path. */ + public RoiEncoder(String path) + { + this.path = path; + } + + /** Creates an RoiEncoder using the specified OutputStream. */ + public RoiEncoder(FileStream f) + { + this.f = f; + } + + /** Saves the specified ROI as a file, returning 'true' if successful. */ + public static bool save(ROI roi, String path) + { + RoiEncoder re = new RoiEncoder(path); + try + { + re.write(roi); + } + catch (IOException e) + { + return false; + } + return true; + } + + /** Save the Roi to the file of stream. */ + public void write(ROI roi) + { + if (f != null) + { + write(roi, f); + } + else + { + f = new FileStream(path,FileMode.Create); + write(roi, f); + f.Close(); + } + } + + /** Saves the specified ROI as a byte array. + public static byte[] saveAsByteArray(ROI roi) + { + if (roi == null) return null; + byte[] bytes = null; + try + { + MemoryStream outs = new MemoryStream(4096); + RoiEncoder encoder = new RoiEncoder(path); + encoder.write(roi); + outs.close(); + bytes = out.toByteArray(); + } + catch (IOException e) + { + return null; + } + return bytes; + } + */ + /// The function "write" saves the properties and coordinates of a region of interest (ROI) + /// to a file stream. + /// + /// @param ROI The ROI (Region of Interest) is an object that represents a selected area or + /// shape in an image. It contains information about the type of ROI (e.g., rectangle, + /// polygon, line), its position and size, and any additional properties or data associated + /// with it. + /// @param FileStream FileStream is a class that represents a stream of bytes that can be + /// written to a file. It is used to write the data of the ROI (Region of Interest) to a + /// file. + /// + /// @return The code snippet does not have a return statement, so it does not return + /// anything. + void write(ROI roi, FileStream f) + { + RectangleD r = roi.Rect; + //if (r.width > 60000 || r.height > 60000 || r.x > 60000 || r.y > 60000) + // roi.enableSubPixelResolution(); + //int roiType = GetImageJType(roi); + int type = GetImageJType(roi); + int options = 0; + //if (roi.getScaleStrokeWidth()) + // options |= RoiDecoder.SCALE_STROKE_WIDTH; + roiName = roi.Text; + if (roiName != null) + roiNameSize = roiName.Length * 2; + else + roiNameSize = 0; + + roiProps = roi.properties; + if (roiProps != null) + roiPropsSize = roiProps.Length * 2; + else + roiPropsSize = 0; + /* + switch (roiType) { + case Roi.POLYGON: type = polygon; break; + case Roi.FREEROI: type = freehand; break; + case Roi.TRACED_ROI: type = traced; break; + case Roi.OVAL: type = oval; break; + case Roi.LINE: type = line; break; + case Roi.POLYLINE: type = polyline; break; + case Roi.FREELINE: type = freeline; break; + case Roi.ANGLE: type = angle; break; + case Roi.COMPOSITE: type = rect; break; // shape array size (36-39) will be >0 to indicate composite type + case Roi.POINT: type = point; break; + default: type = rect; break; + } + */ + /* + if (roiType == Roi.COMPOSITE) { + saveShapeRoi(roi, type, f, options); + return; + } + */ + int n = 0; + int[] + x = null, y = null; + float[] + xf = null, yf = null; + int floatSize = 0; + //if (roi instanceof PolygonRoi) { + //PolygonRoi proi = (PolygonRoi)roi; + //Polygon p = proi.getNonSplineCoordinates(); + n = roi.PointsD.Count; //p.npoints; + //x = p.xpoints; + //y = p.ypoints; + GetPointsXY(roi, out x, out y); + if (subres) + { + /* + if (proi.isSplineFit()) + fp = proi.getNonSplineFloatPolygon(); + else + fp = roi.getFloatPolygon(); + if (n == fp.npoints) + { + options |= RoiDecoder.SUB_PIXEL_RESOLUTION; + if (roi.getDrawOffset()) + options |= RoiDecoder.DRAW_OFFSET; + xf = fp.xpoints; + yf = fp.ypoints; + floatSize = n * 8; + } + */ + } + + + countersSize = 0; + /* + if (roi instanceof PointRoi) { + counters = ((PointRoi)roi).getCounters(); + if (counters != null && counters.length >= n) + countersSize = n * 4; + } + */ + data = new byte[HEADER_SIZE + HEADER2_SIZE + n * 4 + floatSize + roiNameSize + roiPropsSize + countersSize]; + data[0] = 73; data[1] = 111; data[2] = 117; data[3] = 116; // "Iout" + putShort(RoiDecoder.VERSION_OFFSET, VERSION); + data[RoiDecoder.TYPE] = (byte)type; + float px, py, pw, ph; + GetXY(roi, out px, out py); + GetWH(roi, out pw, out ph); + putShort(RoiDecoder.TOP, (int)py); + putShort(RoiDecoder.LEFT, (int)px); + putShort(RoiDecoder.BOTTOM, (int)(py + ph)); + putShort(RoiDecoder.RIGHT, (int)(px + pw)); + PointD[] pds = roi.GetPoints(); + if (subres && (type == rect || type == oval)) + { + //FloatPolygon p = null; + /* + if (roi instanceof OvalRoi) + p = ((OvalRoi)roi).getFloatPolygon4(); + else + { + int d = roi.getCornerDiameter(); + if (d > 0) + { + roi.setCornerDiameter(0); + p = roi.getFloatPolygon(); + roi.setCornerDiameter(d); + } + else + p = roi.getFloatPolygon(); + } + */ + if (roi.PointsD.Count == 4) + { + + putFloat(RoiDecoder.XD, (float)pds[0].X); + putFloat(RoiDecoder.YD, (float)pds[0].Y); + //putFloat(RoiDecoder.WIDTHD, p.xpoints[1] - roi.PointsD[0]); + //putFloat(RoiDecoder.HEIGHTD, p.ypoints[2] - p.ypoints[1]); + putFloat(RoiDecoder.WIDTHD, (float)pds[1].X - (float)pds[0].X); + putFloat(RoiDecoder.HEIGHTD, (float)pds[2].Y - (float)pds[1].Y); + options |= RoiDecoder.SUB_PIXEL_RESOLUTION; + putShort(RoiDecoder.OPTIONS, options); + } + } + if (n > 65535 && type != point) + { + if (type == polygon || type == freehand || type == traced) + { + //String name = roi.Text; + //roi = new ShapeRoi(roi); + //if (name != null) roi.setName(name); + saveShapeRoi(roi, rect, f, options); + return; + } + //ij.IJ.beep(); + //ij.IJ.log("Non-polygonal selections with more than 65k points cannot be saved."); + n = 65535; + } + if (type == point && n > 65535) + putInt(RoiDecoder.SIZE, n); + else + putShort(RoiDecoder.N_COORDINATES, n); + putInt(RoiDecoder.POSITION, roi.coord.Z); + + /* + if (type == rect) + { + int arcSize = roi.getCornerDiameter(); + if (arcSize > 0) + putShort(RoiDecoder.ROUNDED_RECT_ARC_SIZE, arcSize); + } + */ + + if(type == line) //(roi instanceof Line) + { + //Line line = (Line)roi; + putFloat(RoiDecoder.X1, (float)pds[0].X); + putFloat(RoiDecoder.Y1, (float)pds[0].Y); + putFloat(RoiDecoder.X2, (float)pds[1].X); + putFloat(RoiDecoder.Y2, (float)pds[1].Y); + /* + if (roi instanceof Arrow) { + putShort(RoiDecoder.SUBTYPE, RoiDecoder.ARROW); + if (((Arrow)roi).getDoubleHeaded()) + options |= RoiDecoder.DOUBLE_HEADED; + if (((Arrow)roi).getOutline()) + options |= RoiDecoder.OUTLINE; + putShort(RoiDecoder.OPTIONS, options); + putByte(RoiDecoder.ARROW_STYLE, ((Arrow)roi).getStyle()); + putByte(RoiDecoder.ARROW_HEAD_SIZE, (int)((Arrow)roi).getHeadSize()); + } else + { + if (roi.getDrawOffset()) + options |= RoiDecoder.SUB_PIXEL_RESOLUTION + RoiDecoder.DRAW_OFFSET; + } + */ + } + + if (type == point) { + //PointRoi point = (PointRoi)roi; + putByte(RoiDecoder.POINT_TYPE, 1);//point.getPointType()); + putShort(RoiDecoder.STROKE_WIDTH, (int)roi.strokeWidth); + /* + if (point.getShowLabels()) + options |= RoiDecoder.SHOW_LABELS; + if (point.promptBeforeDeleting()) + options |= RoiDecoder.PROMPT_BEFORE_DELETING; + */ + } + + if (type == oval) + { + /* + double[] p = null; + if (roi instanceof RotatedRectRoi) { + putShort(RoiDecoder.SUBTYPE, RoiDecoder.ROTATED_RECT); + p = ((RotatedRectRoi)roi).getParams(); + } else + { + */ + putShort(RoiDecoder.SUBTYPE, RoiDecoder.ELLIPSE); + //p = ((EllipseRoi)roi).getParams(); + //} + float fx, fy, fw, fh; + GetXY(roi, out fx, out fy); + GetWH(roi, out fw, out fh); + putFloat(RoiDecoder.X1, fx); + putFloat(RoiDecoder.Y1, fy); + putFloat(RoiDecoder.X2, fw); + putFloat(RoiDecoder.Y2, fh); + //putFloat(RoiDecoder.FLOAT_PARAM, (float)p[4]); + } + + // save stroke width, stroke color and fill color (1.43i or later) + if (VERSION >= 218) + { + saveStrokeWidthAndColor(roi); + /* + if ((roi instanceof PolygonRoi) && ((PolygonRoi)roi).isSplineFit()) { + options |= RoiDecoder.SPLINE_FIT; + putShort(RoiDecoder.OPTIONS, options); + } + */ + } + + if (roi.type == ROI.Type.Label)//(n == 0 && roi instanceof TextRoi) + saveTextRoi(roi); + /* + else if (n == 0 && roi instanceof ImageRoi) + options = saveImageRoi((ImageRoi)roi, options); + */ + //else + putHeader2(roi, HEADER_SIZE + n * 4 + floatSize); + + if (n > 0) + { + int base1 = 64; + int base2 = base1 + 2 * n; + for (int i = 0; i < n; i++) + { + putShort(base1 + i * 2, (int)(x[i] - px)); + putShort(base2 + i * 2, (int)(y[i] - py)); + } + if (xf != null) + { + base1 = 64 + 4 * n; + base2 = base1 + 4 * n; + for (int i = 0; i < n; i++) + { + putFloat(base1 + i * 4, xf[i]); + putFloat(base2 + i * 4, yf[i]); + } + } + } + + //saveOverlayOptions(roi, options); + f.Write(data); + } + + /// The function saves the stroke width and color of a region of interest (ROI) in a + /// specific format. + /// + /// @param ROI The ROI parameter is an object that represents a region of interest. It + /// contains information about the stroke width, stroke color, and fill color of the region. + void saveStrokeWidthAndColor(ROI roi) + { + //BasicStroke stroke = roi.getStroke(); + //if (stroke != null) + putShort(RoiDecoder.STROKE_WIDTH, (int)roi.strokeWidth); + AForge.Color strokeColor = roi.strokeColor; + int intColor = (strokeColor.R << 16) | (strokeColor.G << 8) | (strokeColor.B); + putInt(RoiDecoder.STROKE_COLOR, 0); + AForge.Color fillColor = roi.fillColor; + int intFillColor = (fillColor.R << 16) | (fillColor.G << 8) | (fillColor.B); + putInt(RoiDecoder.FILL_COLOR, 0); + } + + /// The function `saveShapeRoi` saves a shape region of interest (ROI) to a file stream in a + /// specific format. + /// + /// @param ROI The `ROI` parameter is an object that represents a region of interest. It + /// contains information about the shape and position of the region. + /// @param type The "type" parameter is an integer that represents the type of the ROI. It + /// is used to determine how the ROI should be saved and interpreted. + /// @param FileStream FileStream is a class in C# that represents a stream of bytes to read + /// from or write to a file. It is used to handle file input/output operations. In the given + /// code, it is used to write the data to a file. + /// @param options The "options" parameter is an integer that represents various options for + /// saving the shape ROI. It is used to specify additional information or settings related + /// to the saving process. The specific meaning and usage of the options parameter would + /// depend on the context and the implementation of the saveShapeRoi() method. + void saveShapeRoi(ROI roi, int type, FileStream f, int options) + { + //float[] shapeArray = ((ShapeRoi)roi).getShapeAsArray(); + //if (shapeArray == null) return; + //BufferedOutputStream bout = new BufferedOutputStream(f); + + data = new byte[HEADER_SIZE + HEADER2_SIZE + roiNameSize + roiPropsSize];//shapeArray.length * 4 + roiNameSize + roiPropsSize]; + data[0] = 73; data[1] = 111; data[2] = 117; data[3] = 116; // "Iout" + + putShort(RoiDecoder.VERSION_OFFSET, VERSION); + data[RoiDecoder.TYPE] = (byte)type; + + float x, y, w, h; + GetXY(roi, out x,out y); + GetWH(roi, out w, out h); + putShort(RoiDecoder.TOP, (int)y); + putShort(RoiDecoder.LEFT, (int)x); + putShort(RoiDecoder.BOTTOM, (int)(y + h)); + putShort(RoiDecoder.RIGHT, (int)(x + w)); + putInt(RoiDecoder.POSITION, roi.coord.Z); + ///putShort(16, n); + //putInt(36, shapeArray.Length); // non-zero segment count indicate composite type + if (VERSION >= 218) + saveStrokeWidthAndColor(roi); + //saveOverlayOptions(roi, options); + + // handle the actual data: data are stored segment-wise, i.e., + // the type of the segment followed by 0-6 control point coordinates. + /* + int bas = 64; + for (int i = 0; i < shapeArray.Length; i++) + { + putFloat(bas, shapeArray[i]); + bas += 4; + } + */ + int hdr2Offset = HEADER_SIZE;// + shapeArray.Length * 4; + //ij.IJ.log("saveShapeRoi: "+HEADER_SIZE+" "+shapeArray.length); + putHeader2(roi, hdr2Offset); + f.Write(data, 0, data.Length); + f.Flush(); + } + + /* + void saveOverlayOptions(ROI roi, int options) + { + Overlay proto = roi.getPrototypeOverlay(); + if (proto.getDrawLabels()) + options |= RoiDecoder.OVERLAY_LABELS; + if (proto.getDrawNames()) + options |= RoiDecoder.OVERLAY_NAMES; + if (proto.getDrawBackgrounds()) + options |= RoiDecoder.OVERLAY_BACKGROUNDS; + Font font = proto.getLabelFont(); + if (font != null && font.getStyle() == Font.BOLD) + options |= RoiDecoder.OVERLAY_BOLD; + if (proto.scalableLabels()) + options |= RoiDecoder.SCALE_LABELS; + putShort(RoiDecoder.OPTIONS, options); + } + */ + /// The function `saveTextRoi` saves the properties of a text region of interest (ROI) into + /// a byte array. + /// + /// @param ROI The `ROI` parameter is an object that represents a region of interest. It + /// contains information about the font, size, style, text, and other properties of the + /// region of interest. + void saveTextRoi(ROI roi) + { + //Font font = roi.getCurrentFont(); + string fontName = roi.family; + int size = (int)roi.fontSize; + int drawStringMode = 0; //roi.getDrawStringMode() ? 1024 : 0; + int style = 0;//font.getStyle() + roi.getJustification() * 256 + drawStringMode; + string text = roi.roiName; + float angle = 0; + int angleLength = 4; + int fontNameLength = fontName.Length; + int textLength = text.Length; + int textRoiDataLength = 16 + fontNameLength * 2 + textLength * 2 + angleLength; + byte[] data2 = new byte[HEADER_SIZE + HEADER2_SIZE + textRoiDataLength + roiNameSize + roiPropsSize]; + Array.Copy(data, 0, data2, 0, HEADER_SIZE); + data = data2; + putShort(RoiDecoder.SUBTYPE, RoiDecoder.TEXT); + putInt(HEADER_SIZE, size); + putInt(HEADER_SIZE + 4, style); + putInt(HEADER_SIZE + 8, fontNameLength); + putInt(HEADER_SIZE + 12, textLength); + for (int i = 0; i < fontNameLength; i++) + putShort(HEADER_SIZE + 16 + i * 2, fontName.ElementAt(i)); + for (int i = 0; i < textLength; i++) + putShort(HEADER_SIZE + 16 + fontNameLength * 2 + i * 2, text.ElementAt(i)); + int hdr2Offset = HEADER_SIZE + textRoiDataLength; + //ij.IJ.log("saveTextRoi: "+HEADER_SIZE+" "+textRoiDataLength+" "+fontNameLength+" "+textLength); + putFloat(hdr2Offset - angleLength, angle); + putHeader2(roi, hdr2Offset); + } + /* + private int saveImageRoi(ROI roi, int options) + { + byte[] bytes = roi.getSerializedImage(); + int imageSize = bytes.length; + byte[] data2 = new byte[HEADER_SIZE + HEADER2_SIZE + imageSize + roiNameSize + roiPropsSize]; + System.arraycopy(data, 0, data2, 0, HEADER_SIZE); + data = data2; + putShort(RoiDecoder.SUBTYPE, RoiDecoder.IMAGE); + for (int i = 0; i < imageSize; i++) + putByte(HEADER_SIZE + i, bytes[i] & 255); + int hdr2Offset = HEADER_SIZE + imageSize; + double opacity = roi.getOpacity(); + putByte(hdr2Offset + RoiDecoder.IMAGE_OPACITY, (int)(opacity * 255.0)); + putInt(hdr2Offset + RoiDecoder.IMAGE_SIZE, imageSize); + if (roi.getZeroTransparent()) + options |= RoiDecoder.ZERO_TRANSPARENT; + putHeader2(roi, hdr2Offset); + return options; + } + */ + /// The function "putHeader2" is used to set various properties of a Region of Interest + /// (ROI) object, such as its position, label color, font size, stroke width, and group. + /// + /// @param ROI The ROI parameter is an object of type ROI, which represents a region of + /// interest in an image. It contains information about the position and size of the ROI, as + /// well as other properties such as the stroke color, stroke width, and font size. + /// @param hdr2Offset The `hdr2Offset` parameter is an integer that represents the offset + /// position in the header where the information for the second header should be stored. + void putHeader2(ROI roi, int hdr2Offset) + { + //ij.IJ.log("putHeader2: "+hdr2Offset+" "+roiNameSize+" "+roiName); + putInt(RoiDecoder.HEADER2_OFFSET, hdr2Offset); + putInt(hdr2Offset + RoiDecoder.C_POSITION, roi.coord.C + 1); + putInt(hdr2Offset + RoiDecoder.Z_POSITION, roi.coord.Z + 1); + putInt(hdr2Offset + RoiDecoder.T_POSITION, roi.coord.T + 1); + //Overlay proto = roi.getPrototypeOverlay(); + AForge.Color overlayLabelColor = roi.strokeColor; //proto.getLabelColor(); + int intColor = (overlayLabelColor.R << 16) | (overlayLabelColor.G << 8) | (overlayLabelColor.B); + //if (overlayLabelColor != null) + putInt(hdr2Offset + RoiDecoder.OVERLAY_LABEL_COLOR, 0); + //Font font = proto.getLabelFont(); + //if (font != null) + putShort(hdr2Offset + RoiDecoder.OVERLAY_FONT_SIZE, (int)roi.fontSize); + if (roiNameSize > 0) + putName(roi, hdr2Offset); + double strokeWidth = roi.strokeWidth; + //if (roi.getStroke() == null) + // strokeWidth = 0.0; + putFloat(hdr2Offset + RoiDecoder.FLOAT_STROKE_WIDTH, (float)strokeWidth); + if (roiPropsSize > 0) + putProps(roi, hdr2Offset); + if (countersSize > 0) + putPointCounters(roi, hdr2Offset); + putByte(hdr2Offset + RoiDecoder.GROUP, roi.serie);//roi.getGroup()); + } + + /// The function "putName" takes a ROI object and an offset value, and sets the name and + /// length of the ROI in the header. + /// + /// @param ROI The ROI parameter is an object of type ROI. It is used to access the + /// properties and methods of the ROI object within the putName method. + /// @param hdr2Offset The `hdr2Offset` parameter is an integer value representing the offset + /// of the header2 in a data structure or file. It is used to calculate the offset for + /// storing the name of the ROI (Region of Interest) in the data structure or file. + void putName(ROI roi, int hdr2Offset) + { + int offset = hdr2Offset + HEADER2_SIZE; + int nameLength = roiNameSize / 2; + putInt(hdr2Offset + RoiDecoder.NAME_OFFSET, offset); + putInt(hdr2Offset + RoiDecoder.NAME_LENGTH, nameLength); + for (int i = 0; i < nameLength; i++) + putShort(offset + i * 2, roiName.ElementAt(i)); + } + + /// The function "putProps" takes a ROI object and an offset value, and updates the ROI + /// properties in the header based on the given offset and ROI object. + /// + /// @param ROI The ROI parameter is an object of type ROI. It is used to pass information + /// about a region of interest. + /// @param hdr2Offset The `hdr2Offset` parameter is an integer value representing the offset + /// of the header2 in memory. It is used to calculate the offset for storing the ROI + /// properties. + void putProps(ROI roi, int hdr2Offset) + { + int offset = hdr2Offset + HEADER2_SIZE + roiNameSize; + int roiPropsLength = roiPropsSize / 2; + putInt(hdr2Offset + RoiDecoder.ROI_PROPS_OFFSET, offset); + putInt(hdr2Offset + RoiDecoder.ROI_PROPS_LENGTH, roiPropsLength); + for (int i = 0; i < roiPropsLength; i++) + putShort(offset + i * 2, roiProps.ElementAt(i)); + } + + /// The function "putPointCounters" updates the counters in a region of interest (ROI) by + /// copying the values from an array to a specific offset in memory. + /// + /// @param ROI The ROI parameter is an object of type ROI, which likely represents a region + /// of interest in an image. It may contain information such as the coordinates, size, and + /// properties of the region. + /// @param hdr2Offset The `hdr2Offset` parameter is the offset value for the second header + /// in the data structure. It is used to calculate the position where the point counters + /// will be stored. + void putPointCounters(ROI roi, int hdr2Offset) + { + int offset = hdr2Offset + HEADER2_SIZE + roiNameSize + roiPropsSize; + putInt(hdr2Offset + RoiDecoder.COUNTERS_OFFSET, offset); + for (int i = 0; i < countersSize / 4; i++) + putInt(offset + i * 4, counters[i]); + countersSize = 0; + } + + /// The function "putByte" assigns a byte value to a specific index in an array. + /// + /// @param bas The parameter "bas" is an integer that represents the base address or index + /// of the array "data" where the byte value will be stored. + /// @param v The parameter "v" is an integer value that represents the value to be stored in + /// the byte array. + void putByte(int bas, int v) + { + data[bas] = (byte)v; + } + + /// The function "putShort" takes two integer parameters, "bas" and "v", and stores the + /// value of "v" in the "data" array at index "bas" and "bas + 1" after performing a right + /// shift operation on "v" by 8 bits. + /// + /// @param bas The parameter "bas" represents the base index in the "data" array where the + /// short value will be stored. + /// @param v The parameter "v" is an integer value that represents the value to be stored in + /// the data array. + void putShort(int bas, int v) + { + //data[bas] = (byte)(v >>> 8); + //data[bas] = (byte)UnsignedRightShift(v, 8); + data[bas] = (byte)rightMove(v, 8); + data[bas + 1] = (byte)v; + } + + /// The function "putFloat" takes an integer and a float as input and converts the float + /// into its binary representation, storing it in a byte array. + /// + /// @param bas The parameter "bas" represents the base index in the "data" array where the + /// float value will be stored. + /// @param v The parameter "v" is a float value that needs to be converted and stored in the + /// "data" array. + void putFloat(int bas, float v) + { + int tmp = BitConverter.SingleToInt32Bits(v);//Float.floatToIntBits(v); + data[bas] = (byte)(tmp >> 24); + data[bas + 1] = (byte)(tmp >> 16); + data[bas + 2] = (byte)(tmp >> 8); + data[bas + 3] = (byte)tmp; + } + + /// The function "putInt" takes two integer parameters and stores the bytes of the second + /// integer in a byte array starting at the specified index in big-endian order. + /// + /// @param bas The parameter "bas" represents the base index in the "data" array where the + /// integer value will be stored. + /// @param i The parameter "i" is an integer value that needs to be stored in the "data" + /// array. + void putInt(int bas, int i) + { + data[bas] = (byte)(i >> 24); + data[bas + 1] = (byte)(i >> 16); + data[bas + 2] = (byte)(i >> 8); + data[bas + 3] = (byte)i; + } + } + + } +} diff --git a/BioCore/Source/ImageJ.cs b/BioCore/Source/ImageJ.cs index 986e085..0967d08 100644 --- a/BioCore/Source/ImageJ.cs +++ b/BioCore/Source/ImageJ.cs @@ -1,1668 +1,297 @@ using System; +using System.Buffers; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Diagnostics; -using System.IO; -using CSScripting; -using AForge; +using Gtk; +using ij; +using ij.plugin; +using ij.process; using BioLib; +using AForge; +using java.awt.image; + namespace BioCore { public class ImageJ { - public static class Macro + static short[] ConvertByteArrayToShortArray(byte[] byteArray, bool littleEndian) { - public class Command + if (byteArray.Length % 2 != 0) { - public string Name { get; set; } - public string Arguments { get; set; } - public string Description { get; set; } - public Command(string name, string args, string description) + throw new ArgumentException("The length of the byte array must be even."); + } + + // Each short is made from two bytes + short[] shortArray = new short[byteArray.Length / 2]; + + for (int i = 0; i < shortArray.Length; i++) + { + if (littleEndian) { - Name = name; - Arguments = args; - Description = description; + // If the system is little-endian, reverse the byte order + shortArray[i] = (short)((byteArray[i * 2 + 1] << 8) | byteArray[i * 2]); } - public override string ToString() + else + { + // If the system is big-endian, keep the byte order as is + shortArray[i] = (short)((byteArray[i * 2] << 8) | byteArray[i * 2 + 1]); + } + } + return shortArray; + } + + public static ImagePlus GetImagePlus(BioImage bm) + { + ImageStack ims = new ImageStack(bm.SizeX, bm.SizeY); + if (bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format16bppGrayScale) + { + // Populate the stack with 16-bit images + for (int t = 0; t < bm.SizeT; t++) { - return Name.ToString(); + for (int c = 0; c < bm.SizeC; c++) + { + for (int z = 0; z < bm.SizeZ; z++) + { + short[] pixels = ConvertByteArrayToShortArray(bm.Buffers[bm.GetFrameIndex(z, c, t)].Bytes, true); + ImageProcessor ip = new ShortProcessor(bm.SizeX, bm.SizeY, pixels, java.awt.image.ColorModel.getRGBdefault()); + ims.addSlice("Z" + z + "C" + c + "T" + t, ip); + } + } } } - public class Function + else if (bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format8bppIndexed) { - public string Name { get; set; } - public string Arguments { get; set; } - public string Description { get; set; } - public Function(string name, string args, string description) + // Create a grayscale IndexColorModel + byte[] grayscale = new byte[256]; + for (int i = 0; i < 256; i++) { - Name = name; - Arguments = args; - Description = description; + grayscale[i] = (byte)i; } - public override string ToString() + IndexColorModel colorModel = new IndexColorModel(8, 256, grayscale, grayscale, grayscale); + for (int t = 0; t < bm.SizeT; t++) { - return Name.ToString(); + for (int c = 0; c < bm.SizeC; c++) + { + for (int z = 0; z < bm.SizeZ; z++) + { + ImageProcessor ip = new ByteProcessor(bm.SizeX, bm.SizeY, bm.Buffers[bm.GetFrameIndex(z, c, t)].Bytes, colorModel); + ims.addSlice("Z" + z + "C" + c + "T" + t, ip); + } + } } } - public static Dictionary Commands = new Dictionary(); - public static Dictionary> Functions = new Dictionary>(); - internal static void Initialize() + else if (bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format24bppRgb) { - string[] sts = File.ReadAllLines("macro-functions.txt"); - foreach (string s in sts) + for (int t = 0; t < bm.SizeT; t++) { - string com = ""; - string args = ""; - string doc = ""; - bool indoc = false, inargs = false; - if (!s.StartsWith('#')) + for (int c = 0; c < bm.SizeC; c++) { - for (int i = 0; i < s.Length; i++) + for (int z = 0; z < bm.SizeZ; z++) { - if (!inargs) + byte[] bts = bm.Buffers[bm.GetFrameIndex(z, c, t)].Bytes; + // Convert the byte array to an int array for the RGB processor + int[] rgbPixels = new int[bm.SizeX * bm.SizeY]; + for (int i = 0; i < rgbPixels.Length; i++) { - if (s[i] != '(') - com += s[i]; - else - inargs = true; - if (s[i] == ' ') - { - inargs = true; - indoc = true; - } + int r = bts[i * 3] & 0xFF; // Red + int g = bts[i * 3 + 1] & 0xFF; // Green + int b = bts[i * 3 + 2] & 0xFF; // Blue + rgbPixels[i] = (r << 16) | (g << 8) | b; // Combine into RGB int } - else - if (!indoc) - if (s[i] != ')') - args += s[i]; - else - indoc = true; - else - doc += s[i]; + // Create a ColorProcessor + ImageProcessor ip = new ColorProcessor(bm.SizeX, bm.SizeY, rgbPixels); + ims.addSlice("Z" + z + "C" + c + "T" + t, ip); } - if (!Functions.ContainsKey(com)) - Functions.Add(com, new List() { new Function(com, args, doc) }); - else - Functions[com].Add(new Function(com, args, doc)); } } - string[] cs = File.ReadAllLines("macro-commands.csv"); - foreach (string s in cs) - { - string[] v = s.Split(','); - Commands.Add(v[0], new Command(v[0], v[1], "")); - } } - } - public static List Macros = new List(); - public static List processes = new List(); - public static string ImageJPath = Properties.Settings.Default.ImageJPath; - private static Random rng = new Random(); - static bool init = false; - public static bool Initialized { get { return init; } private set { } } - /// It runs a macro in ImageJ - /// - /// @param file the path to the macro file - /// @param param - /// - /// @return The macro is being returned. - public static void RunMacro(string file, string param) - { - if(!Initialized) + else if(bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format32bppArgb || bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format32bppRgb) { - Initialize(); - } - file.Replace("/", "\\"); - Process pr = new Process(); - pr.StartInfo.FileName = ImageJPath; - pr.StartInfo.Arguments = "-macro " + file + " " + param; - - pr.Start(); - processes.Add(pr); - Recorder.AddLine("ImageJ.RunMacro(" + file + "," + '"' + param + '"' + ");"); - } - /// It runs a macro in ImageJ - /// - /// @param con The macro code - /// @param param The parameters to pass to the macro. - /// @param headless whether or not to run ImageJ in headless mode - /// - /// @return The macro is returning a string. - public static void RunString(string con, string param, bool headless) - { - if (!Initialized) - { - Initialize(); - } - Process pr = new Process(); - pr.StartInfo.FileName = ImageJPath; - pr.StartInfo.CreateNoWindow = true; - string te = rng.Next(0, 9999999).ToString(); - string p = Environment.CurrentDirectory + "\\" + te + ".txt"; - p.Replace("/", "\\"); - File.WriteAllText(p,con); - if(headless) - pr.StartInfo.Arguments = "--headless -macro " + p + " " + param; - else - pr.StartInfo.Arguments = "-macro " + p + " " + param; - pr.Start(); - File.Delete(Path.GetDirectoryName(ImageJPath) + "/done.txt"); - processes.Add(pr); - do - { - if (File.Exists(Path.GetDirectoryName(ImageJPath) + "/done.txt")) + for (int t = 0; t < bm.SizeT; t++) { - do + for (int c = 0; c < bm.SizeC; c++) { - try - { - File.Delete(Path.GetDirectoryName(ImageJPath) + "/done.txt"); - } - catch (Exception) + for (int z = 0; z < bm.SizeZ; z++) { - + byte[] bts = bm.Buffers[bm.GetFrameIndex(z, c, t)].Bytes; + // Convert the byte array to an int array for the ColorProcessor + int[] rgbaPixels = new int[bm.SizeX * bm.SizeY]; + for (int i = 0; i < rgbaPixels.Length; i++) + { + int r = bts[i * 4] & 0xFF; // Red + int g = bts[i * 4 + 1] & 0xFF; // Green + int b = bts[i * 4 + 2] & 0xFF; // Blue + int a = bts[i * 4 + 3] & 0xFF; // Alpha + rgbaPixels[i] = (a << 24) | (r << 16) | (g << 8) | b; // Combine into ARGB int + } + // Create a ColorProcessor with the ARGB int array + ImageProcessor ip = new ColorProcessor(bm.SizeX, bm.SizeY, rgbaPixels); + ims.addSlice("Z" + z + "C" + c + "T" + t, ip); } - } while (File.Exists(Path.GetDirectoryName(ImageJPath) + "/done.txt")); - pr.Kill(); - break; + } } - } while (!pr.HasExited); - File.Delete(p); - } - /// It runs a macro on the selected image, saves the result as a new image, and then opens the - /// new image in a new tab - /// - /// @param con The ImageJ macro to run. - /// @param headless Whether to run ImageJ in headless mode. - /// @param onTab Whether to run the macro on current tab of images. - /// @param bioformats If the image is a bioformats image, it will use the bioformats importer to - /// open the image. - /// @param newTab Whether or not the result should open in a new tab. - /// - /// @return The return value is the result of the last statement in the script. - public static void RunOnImage(string con, bool headless, bool onTab, bool bioformats, bool newTab) - { - if (!Initialized) - { - Initialize(); - } - string filename = ""; - string dir = Path.GetDirectoryName(ImageView.SelectedImage.file); - - if (ImageView.SelectedImage.ID.EndsWith(".ome.tif")) - { - filename = Path.GetFileNameWithoutExtension(ImageView.SelectedImage.ID); - filename = filename.Remove(filename.Length - 4, 4); - } - else - filename = Path.GetFileNameWithoutExtension(ImageView.SelectedImage.ID); - string file = dir + "\\" + filename + "-temp" + ".ome.tif"; - file = file.Replace("\\", "/"); - string st = - "run(\"Bio-Formats Importer\", \"open=\" + getArgument + \" autoscale color_mode=Default open_all_series display_rois rois_import=[ROI manager] view=Hyperstack stack_order=XYCZT\"); " + con + - "run(\"Bio-Formats Exporter\", \"save=" + file + " export compression=Uncompressed\"); " + - "dir = getDir(\"startup\"); " + - "File.saveString(\"done\", dir + \"/done.txt\");"; - if(bioformats) - st = - "open(getArgument); " + con + - "run(\"Bio-Formats Exporter\", \"save=" + file + " export compression=Uncompressed\"); " + - "dir = getDir(\"startup\"); " + - "File.saveString(\"done\", dir + \"/done.txt\");"; - //We save the image as a temp image as otherwise imagej won't export due to file access error. - RunString(st, ImageView.SelectedImage.file, headless); - - if (!File.Exists(file)) - return; - - string ffile = dir + "/" + filename + ".ome.tif"; - File.Delete(ffile); - File.Copy(file, ffile); - File.Delete(file); - if (ImageView.SelectedImage.Filename != filename + ".ome.tif" || ImageView.SelectedImage.Filename != filename + ".tif") - { - if (ImageView.SelectedImage.filename.EndsWith(".tif")) - BioImage.OpenFile(ffile, newTab); - else - BioImage.OpenFile(ffile, true); } - else + else if (bm.Buffers[0].PixelFormat == AForge.PixelFormat.Format48bppRgb) { - App.viewer.Images[App.viewer.SelectedIndex] = BioImage.OpenFile(ffile, 0, false, false); + bm.To16Bit(); + // Populate the stack with 16-bit images + for (int t = 0; t < bm.SizeT; t++) + { + for (int c = 0; c < bm.SizeC; c++) + { + for (int z = 0; z < bm.SizeZ; z++) + { + short[] pixels = ConvertByteArrayToShortArray(bm.Buffers[bm.GetFrameIndex(z, c, t)].Bytes, true); + ImageProcessor ip = new ShortProcessor(bm.SizeX, bm.SizeY, pixels, java.awt.image.ColorModel.getRGBdefault()); + ims.addSlice("Z" + z + "C" + c + "T" + t, ip); + } + } + } } - App.viewer.UpdateImage(); - App.viewer.UpdateView(); - Recorder.AddLine("RunOnImage(\"" + con + "\"," + headless + "," + onTab + "," + newTab + ");"); + ImagePlus imp = new ImagePlus(bm.ID, ims); + imp.setDimensions(bm.SizeC, bm.SizeZ, bm.SizeT); + return imp; } - /// This function is used to initialize the path of the ImageJ.exe file - /// - /// @param path The path to the ImageJ executable. - public static bool Initialize() + public static bool isRGB(ImagePlus image) { - if (!SetImageJPath()) - return false; - Macro.Initialize(); - string[] ds = Directory.GetFiles(Path.GetDirectoryName(ImageJPath) + "/macros"); - foreach (string s in ds) - { - if (s.EndsWith(".ijm") || s.EndsWith(".txt")) - Macros.Add(new Macro.Command(Path.GetFileName(s), "", "")); - } - return true; + return image.getType() == ImagePlus.COLOR_RGB; } - /// If the ImageJ path is not set, prompt the user to set it - /// - /// @return The return value is a boolean. - public static bool SetImageJPath() + + public static int[] getRGBPixelsFromSlice(ImageStack stack, int sliceIndex) { - if (Properties.Settings.Default.ImageJPath == "") - { - MessageBox.Show("ImageJ path not set. Set the ImageJ executable location."); - OpenFileDialog file = new OpenFileDialog(); - file.Title = "Set the ImageJ executable location."; - if (file.ShowDialog() != DialogResult.OK) - return false; - Properties.Settings.Default.ImageJPath = file.FileName; - Properties.Settings.Default.Save(); - ImageJPath = file.FileName; - init = true; - file.Dispose(); - return true; - } - else - return true; + // Get the ImageProcessor for the specified slice + ImageProcessor ip = stack.getProcessor(sliceIndex + 1); // ImageStack uses 1-based indexing + // Cast to ColorProcessor to access RGB data + ColorProcessor colorProcessor = (ColorProcessor)ip; + // Get the RGB pixel data + return (int[])colorProcessor.getPixels(); } - - /* It reads a binary file and returns a ROI object */ - public class RoiDecoder + /// + /// Converts ImagePlus to BioImage. + /// + /// + /// + /// + /// + /// + /// The BioImage represented by the ImagePlus object. + public static BioImage GetBioImage(ImagePlus pl, VolumeD vol, double PhysicalX, double PhysicalY, double PhysicalZ) { - #region Params - // offsets - public static int VERSION_OFFSET = 4; - public static int TYPE = 6; - public static int TOP = 8; - public static int LEFT = 10; - public static int BOTTOM = 12; - public static int RIGHT = 14; - public static int N_COORDINATES = 16; - public static int X1 = 18; - public static int Y1 = 22; - public static int X2 = 26; - public static int Y2 = 30; - public static int XD = 18; - public static int YD = 22; - public static int WIDTHD = 26; - public static int HEIGHTD = 30; - public static int SIZE = 18; - public static int STROKE_WIDTH = 34; - public static int SHAPE_ROI_SIZE = 36; - public static int STROKE_COLOR = 40; - public static int FILL_COLOR = 44; - public static int SUBTYPE = 48; - public static int OPTIONS = 50; - public static int ARROW_STYLE = 52; - public static int FLOAT_PARAM = 52; //ellipse ratio or rotated rect width - public static int POINT_TYPE = 52; - public static int ARROW_HEAD_SIZE = 53; - public static int ROUNDED_RECT_ARC_SIZE = 54; - public static int POSITION = 56; - public static int HEADER2_OFFSET = 60; - public static int COORDINATES = 64; - // header2 offsets - public static int C_POSITION = 4; - public static int Z_POSITION = 8; - public static int T_POSITION = 12; - public static int NAME_OFFSET = 16; - public static int NAME_LENGTH = 20; - public static int OVERLAY_LABEL_COLOR = 24; - public static int OVERLAY_FONT_SIZE = 28; //short - public static int GROUP = 30; //byte - public static int IMAGE_OPACITY = 31; //byte - public static int IMAGE_SIZE = 32; //int - public static int FLOAT_STROKE_WIDTH = 36; //float - public static int ROI_PROPS_OFFSET = 40; - public static int ROI_PROPS_LENGTH = 44; - public static int COUNTERS_OFFSET = 48; - - // subtypes - public static int TEXT = 1; - public static int ARROW = 2; - public static int ELLIPSE = 3; - public static int IMAGE = 4; - public static int ROTATED_RECT = 5; - - // options - public static int SPLINE_FIT = 1; - public static int DOUBLE_HEADED = 2; - public static int OUTLINE = 4; - public static int OVERLAY_LABELS = 8; - public static int OVERLAY_NAMES = 16; - public static int OVERLAY_BACKGROUNDS = 32; - public static int OVERLAY_BOLD = 64; - public static int SUB_PIXEL_RESOLUTION = 128; - public static int DRAW_OFFSET = 256; - public static int ZERO_TRANSPARENT = 512; - public static int SHOW_LABELS = 1024; - public static int SCALE_LABELS = 2048; - public static int PROMPT_BEFORE_DELETING = 4096; //points - public static int SCALE_STROKE_WIDTH = 8192; - - // types - private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, - freehand = 7, traced = 8, angle = 9, point = 10; - - private byte[] data; - private string path; - private MemoryStream ins; - private string name; - private int size; - #endregion - - /** Constructs an RoiDecoder using a file path. */ - public RoiDecoder(string path) - { - this.path = path; - } - - /** Constructs an RoiDecoder using a byte array. */ - public RoiDecoder(byte[] bytes, string name) - { - ins = new MemoryStream(bytes); - this.name = name; - this.size = bytes.Length; - } - - /** Opens the ROI at the specified path. Returns null if there is an error. */ - public static ROI open(string path) - { - ROI roi = null; - RoiDecoder rd = new RoiDecoder(path); - roi = rd.getRoi(); - return roi; - } - - /** Returns the ROI. */ - public ROI getRoi() - { - ROI roi = new ROI(); - data = File.ReadAllBytes(path); - size = data.Length; - if (getByte(0) != 73 || getByte(1) != 111) //"Iout" - throw new IOException("This is not an ImageJ ROI"); - int version = getShort(VERSION_OFFSET); - int type = getByte(TYPE); - int subtype = getShort(SUBTYPE); - int top = getShort(TOP); - int left = getShort(LEFT); - int bottom = getShort(BOTTOM); - int right = getShort(RIGHT); - int width = right - left; - int height = bottom - top; - int n = getUnsignedShort(N_COORDINATES); - if (n == 0) - n = getInt(SIZE); - int options = getShort(OPTIONS); - int position = getInt(POSITION); - int hdr2Offset = getInt(HEADER2_OFFSET); - int channel = 0, slice = 0, frame = 0; - int overlayLabelColor = 0; - int overlayFontSize = 0; - int group = 0; - int imageOpacity = 0; - int imageSize = 0; - bool subPixelResolution = (options & SUB_PIXEL_RESOLUTION) != 0 && version >= 222; - bool drawOffset = subPixelResolution && (options & DRAW_OFFSET) != 0; - bool scaleStrokeWidth = true; - if (version >= 228) - scaleStrokeWidth = (options & SCALE_STROKE_WIDTH) != 0; - - bool subPixelRect = version >= 223 && subPixelResolution && (type == rect || type == oval); - double xd = 0.0, yd = 0.0, widthd = 0.0, heightd = 0.0; - if (subPixelRect) - { - xd = getFloat(XD); - yd = getFloat(YD); - widthd = getFloat(WIDTHD); - heightd = getFloat(HEIGHTD); - } - - if (hdr2Offset > 0 && hdr2Offset + IMAGE_SIZE + 4 <= size) - { - channel = getInt(hdr2Offset + C_POSITION); - slice = getInt(hdr2Offset + Z_POSITION); - frame = getInt(hdr2Offset + T_POSITION); - overlayLabelColor = getInt(hdr2Offset + OVERLAY_LABEL_COLOR); - overlayFontSize = getShort(hdr2Offset + OVERLAY_FONT_SIZE); - imageOpacity = getByte(hdr2Offset + IMAGE_OPACITY); - imageSize = getInt(hdr2Offset + IMAGE_SIZE); - group = getByte(hdr2Offset + GROUP); - } - - if (name != null && name.EndsWith(".roi")) - name = name.Substring(0, name.Length - 4); - bool isComposite = getInt(SHAPE_ROI_SIZE) > 0; - - - /* - if (isComposite) - { - roi = getShapeRoi(); - if (version >= 218) - getStrokeWidthAndColor(roi, hdr2Offset, scaleStrokeWidth); - roi.coord.Z = position; - if (channel > 0 || slice > 0 || frame > 0) - { - roi.coord.C = channel; roi.coord.Z = slice; roi.coord.T = frame; - } - decodeOverlayOptions(roi, version, options, overlayLabelColor, overlayFontSize); - if (version >= 224) + BioImage bm = new BioImage(pl.getTitle()); + ImageStack st = pl.getImageStack(); + int b = pl.getBitDepth(); + int slices = pl.getNSlices(); + int chs = pl.getNChannels(); + int frs = pl.getNFrames(); + int rgb = (int)(pl.getBytesPerPixel() * 8); + bool isrgb = isRGB(pl); + bm.UpdateCoords(slices,chs,frs,BioImage.Order.TCZ); + for (int t = 0; t < frs; t++) + { + for (int c = 0; c < chs; c++) + { + for (int z = 0; z < slices; z++) { - string props = getRoiProps(); - if (props != null) - roi.properties = props; - } - if (version >= 228 && group > 0) - roi.serie = group; - return roi; - } - */ - switch (type) - { - case 1: //Rect - if (subPixelRect) - roi = ROI.CreateRectangle(new ZCT(slice - 1, channel - 1, frame - 1), xd, yd, widthd, heightd); - else - roi = ROI.CreateRectangle(new ZCT(slice - 1, channel - 1, frame - 1), left, top, width, height); - int arcSize = getShort(ROUNDED_RECT_ARC_SIZE); - if (arcSize > 0) - throw new NotSupportedException("Type rounded rectangle not supported."); - break; - case 2: //Ellipse - if (subPixelRect) - roi = ROI.CreateEllipse(new ZCT(slice - 1, channel - 1, frame - 1), xd, yd, widthd, heightd); - else - roi = ROI.CreateEllipse(new ZCT(slice - 1, channel - 1, frame - 1), left, top, width, height); - break; - case 3: //Line - float x1 = getFloat(X1); - float y1 = getFloat(Y1); - float x2 = getFloat(X2); - float y2 = getFloat(Y2); - - if (subtype == ARROW) + AForge.Bitmap bmp; + if (!isrgb) { - throw new NotSupportedException("Type arrow not supported."); - /* - roi = new Arrow(x1, y1, x2, y2); - ((Arrow)roi).setDoubleHeaded((options & DOUBLE_HEADED) != 0); - ((Arrow)roi).setOutline((options & OUTLINE) != 0); - int style = getByte(ARROW_STYLE); - if (style >= Arrow.FILLED && style <= Arrow.BAR) - ((Arrow)roi).setStyle(style); - int headSize = getByte(ARROW_HEAD_SIZE); - if (headSize >= 0 && style <= 30) - ((Arrow)roi).setHeadSize(headSize); - */ - } - else - { - roi = ROI.CreateLine(new ZCT(slice, channel, frame), new PointD(x1, y1), new PointD(x2, y2)); - //roi.setDrawOffset(drawOffset); - } - - break; - case 0: - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - //IJ.log("type: "+type); - //IJ.log("n: "+n); - //ij.IJ.log("rect: "+left+","+top+" "+width+" "+height); - if (n == 0 || n < 0) break; - int[] x = new int[n]; - int[] y = new int[n]; - float[] xf = null; - float[] yf = null; - int base1 = COORDINATES; - int base2 = base1 + 2 * n; - int xtmp, ytmp; - for (int i = 0; i < n; i++) - { - xtmp = getShort(base1 + i * 2); - if (xtmp < 0) xtmp = 0; - ytmp = getShort(base2 + i * 2); - if (ytmp < 0) ytmp = 0; - x[i] = left + xtmp; - y[i] = top + ytmp; - } - if (subPixelResolution) - { - xf = new float[n]; - yf = new float[n]; - base1 = COORDINATES + 4 * n; - base2 = base1 + 4 * n; - for (int i = 0; i < n; i++) + if (rgb > 8) { - xf[i] = getFloat(base1 + i * 4); - yf[i] = getFloat(base2 + i * 4); - } - } - if (type == point) - { - //TODO implement non subpizel ROI - if (subPixelResolution) - { - roi.AddPoints(xf, yf); + bmp = new AForge.Bitmap(st.getWidth(), st.getHeight(), AForge.PixelFormat.Format16bppGrayScale); + bm.Buffers.Add(bmp); } else - roi.AddPoints(x, y); - if (version >= 226) { - //((PointRoi)roi).setPointType(getByte(POINT_TYPE)); - roi.strokeWidth = getShort(STROKE_WIDTH); + bmp = new AForge.Bitmap(st.getWidth(), st.getHeight(), AForge.PixelFormat.Format8bppIndexed); + bm.Buffers.Add(bmp); } - //if ((options & SHOW_LABELS) != 0 && !ij.Prefs.noPointLabels) - // ((PointRoi)roi).setShowLabels(true); - //if ((options & PROMPT_BEFORE_DELETING) != 0) - // ((PointRoi)roi).promptBeforeDeleting(true); - roi.type = ROI.Type.Point; - break; } - if (type == polygon) - roi.type = ROI.Type.Polygon; - else if (type == freehand) + else { - roi.type = ROI.Type.Freeform; - if (subtype == ELLIPSE || subtype == ROTATED_RECT) + bmp = new AForge.Bitmap(st.getWidth(), st.getHeight(), AForge.PixelFormat.Format24bppRgb); + bm.Buffers.Add(bmp); + } + int ind = bm.GetFrameIndex(z, c, t); + if (!isrgb) + { + for (int y = 0; y < pl.getHeight(); y++) { - throw new NotSupportedException("ROI type not supported."); - /* - double ex1 = getFloat(X1); - double ey1 = getFloat(Y1); - double ex2 = getFloat(X2); - double ey2 = getFloat(Y2); - double param = getFloat(FLOAT_PARAM); - if (subtype == ROTATED_RECT) - roi = new RotatedRectRoi(ex1, ey1, ex2, ey2, param); - else - roi = new EllipseRoi(ex1, ey1, ex2, ey2, param); - break; - */ + for (int x = 0; x < pl.getWidth(); x++) + { + double d = st.getVoxel(x, y, ind); + if (bm.Buffers[0].PixelFormat == PixelFormat.Format16bppGrayScale) + bmp.SetValue(x, y, (ushort)d); + else if (bm.Buffers[0].PixelFormat == PixelFormat.Format8bppIndexed) + bmp.SetValue(x, y, (byte)d); + } } } - else if (type == traced) - roi.type = ROI.Type.Polyline; - else if (type == polyline) - roi.type = ROI.Type.Polyline; - else if (type == freeline) - roi.type = ROI.Type.Polyline; - else if (type == angle) - roi.type = ROI.Type.Point; else - roi.type = ROI.Type.Freeform; - if (subPixelResolution) { - roi.AddPoints(xf, yf); - //roi = new PolygonRoi(xf, yf, n, roiType); - //roi.setDrawOffset(drawOffset); + int[] rgbPixels = getRGBPixelsFromSlice(st, ind); + // Get the ImageProcessor for the specified slice + ImageProcessor ip = st.getProcessor(ind + 1); // ImageStack uses 1-based indexing + // Cast to ColorProcessor to access RGB data + ColorProcessor colorProcessor = (ColorProcessor)ip; + for (int y = 0; y < pl.getHeight(); y++) + { + for (int x = 0; x < pl.getWidth(); x++) + { + int v = colorProcessor.getPixel(x, y); + int rv = (v >> 16) & 0xFF; // Red + int gv = (v >> 8) & 0xFF; // Green + int bv = v & 0xFF; // Blue + bmp.SetValue(x, y, 0, (byte)rv); + bmp.SetValue(x, y, 1, (byte)gv); + bmp.SetValue(x, y, 2, (byte)bv); + } + } + } - else - roi.AddPoints(x, y); - break; - default: - throw new IOException("Unrecognized ROI type: " + type); - } - if (roi == null) - return null; - roi.roiName = getRoiName(); - - // read stroke width, stroke color and fill color (1.43i or later) - if (version >= 218) - { - getStrokeWidthAndColor(roi, hdr2Offset, scaleStrokeWidth); - /* - if (type == point) - roi.setStrokeWidth(0); - bool splineFit = (options & SPLINE_FIT) != 0; - if (splineFit && roi instanceof PolygonRoi) - ((PolygonRoi)roi).fitSpline(); - */ - } - - if (version >= 218 && subtype == TEXT) - { - getTextRoi(roi, version); - roi.type = ROI.Type.Label; - } - /* - if (version >= 221 && subtype == IMAGE) - roi = getImageRoi(roi, imageOpacity, imageSize, options); - - if (version >= 224) - { - string props = getRoiProps(); - if (props != null) - roi.setProperties(props); - } - - if (version >= 227) - { - int[] counters = getPointCounters(n); - if (counters != null && (roi instanceof PointRoi)) - ((PointRoi)roi).setCounters(counters); - } - */ - // set group (1.52t or later) - if (version >= 228 && group > 0) - roi.serie = group; - - roi.coord.Z = position; - if (channel > 0 || slice > 0 || frame > 0) - roi.coord = new ZCT(slice - 1, channel - 1, frame - 1); //-1 because our ROI coordinates are 0 based - //decodeOverlayOptions(roi, version, options, overlayLabelColor, overlayFontSize); - - //We convert pixel to subpixel - - for (int i = 0; i < roi.PointsD.Count; i++) - { - PointD pd = ImageView.SelectedImage.ToStageSpace(roi.PointsD[i]); - roi.PointsD[i] = pd; - roi.UpdateBoundingBox(); - } - - if (roi.type == ROI.Type.Polygon || roi.type == ROI.Type.Freeform) - roi.closed = true; - return roi; - } - /* - void decodeOverlayOptions(ROI roi, int version, int options, int color, int fontSize) - { - - Overlay proto = new Overlay(); - proto.drawLabels((options & OVERLAY_LABELS) != 0); - proto.drawNames((options & OVERLAY_NAMES) != 0); - proto.drawBackgrounds((options & OVERLAY_BACKGROUNDS) != 0); - if (version >= 220 && color != 0) - proto.setLabelColor(new Color(color)); - bool bold = (options & OVERLAY_BOLD) != 0; - bool scalable = (options & SCALE_LABELS) != 0; - if (fontSize > 0 || bold || scalable) - { - proto.setLabelFont(new Font("SansSerif", bold ? Font.BOLD : Font.PLAIN, fontSize), scalable); - } - roi.setPrototypeOverlay(proto); - - } - */ - /// It reads the stroke width and color from the ROI header - /// - /// @param ROI The ROI object that is being created - /// @param hdr2Offset The offset of the second header. - /// @param scaleStrokeWidth if true, the stroke width is scaled by the magnification factor. - void getStrokeWidthAndColor(ROI roi, int hdr2Offset, bool scaleStrokeWidth) - { - double strokeWidth = getShort(STROKE_WIDTH); - if (hdr2Offset > 0) - { - double strokeWidthD = getFloat(hdr2Offset + FLOAT_STROKE_WIDTH); - if (strokeWidthD > 0.0) - strokeWidth = strokeWidthD; - } - if (strokeWidth > 0.0) - { - roi.strokeWidth = strokeWidth; - } - int strokeColor = getInt(STROKE_COLOR); - if (strokeColor != 0) - { - byte[] bts = BitConverter.GetBytes(strokeColor); - AForge.Color c = AForge.Color.FromArgb(bts[0], bts[1], bts[2], bts[3]); - roi.strokeColor = c; - } - int fillColor = getInt(FILL_COLOR); - if (fillColor != 0) - { - byte[] bts = BitConverter.GetBytes(strokeColor); - AForge.Color c = AForge.Color.FromArgb(bts[0], bts[1], bts[2], bts[3]); - roi.fillColor = c; - } - } - /* - public ROI getShapeRoi() - { - int type = getByte(TYPE); - if (type!=rect) - throw new NotSupportedException("Invalid composite ROI type"); - int top = getShort(TOP); - int left = getShort(LEFT); - int bottom = getShort(BOTTOM); - int right = getShort(RIGHT); - int width = right - left; - int height = bottom - top; - int n = getInt(SHAPE_ROI_SIZE); - - ROI roi = new ROI(); - float[] shapeArray = new float[n]; - int bas = COORDINATES; - for (int i = 0; i < n; i++) - { - shapeArray[i] = getFloat(bas); - bas += 4; - } - roi = new ShapeRoi(shapeArray); - roi.setName(getRoiName()); - return roi; - } - */ - /// It reads the font name, font size, and text from the ROI file and sets the font and text - /// properties of the ROI object - /// - /// @param ROI The ROI object that is being created. - /// @param version 225 - void getTextRoi(ROI roi, int version) - { - Rectangle r = roi.BoundingBox.ToRectangleInt(); - int hdrSize = 64; - int size = getInt(hdrSize); - int styleAndJustification = getInt(hdrSize + 4); - int style = styleAndJustification & 255; - int justification = (styleAndJustification >> 8) & 3; - bool drawStringMode = (styleAndJustification & 1024) != 0; - int nameLength = getInt(hdrSize + 8); - int textLength = getInt(hdrSize + 12); - char[] name = new char[nameLength]; - char[] text = new char[textLength]; - for (int i = 0; i < nameLength; i++) - name[i] = (char)getShort(hdrSize + 16 + i * 2); - for (int i = 0; i < textLength; i++) - text[i] = (char)getShort(hdrSize + 16 + nameLength * 2 + i * 2); - double angle = version >= 225 ? getFloat(hdrSize + 16 + nameLength * 2 + textLength * 2) : 0f; - //Font font = new Font(new string(name), style, size); - string fam = new string(name); - roi.family = fam; - roi.fontSize = size; - roi.Text = new string(text); - /* - if (roi.subPixel) - { - RectangleD fb = roi.Rect; - roi2 = new TextRoi(fb.getX(), fb.getY(), fb.getWidth(), fb.getHeight(), new string(text), font); - } - else - roi2 = new TextRoi(r.x, r.y, r.width, r.height, new string(text), font); - - roi.strokeColor - roi2.setFillColor(roi.getFillColor()); - roi2.setName(getRoiName()); - roi2.setJustification(justification); - roi2.setDrawStringMode(drawStringMode); - roi2.setAngle(angle); - return roi2; - */ - } - - /// It reads the name of the ROI from the file - /// - /// @return The name of the ROI. - string getRoiName() - { - string fileName = name; - int hdr2Offset = getInt(HEADER2_OFFSET); - if (hdr2Offset == 0) - return fileName; - int offset = getInt(hdr2Offset + NAME_OFFSET); - int Length = getInt(hdr2Offset + NAME_LENGTH); - if (offset == 0 || Length == 0) - return fileName; - if (offset + Length * 2 > size) - return fileName; - char[] namem = new char[Length]; - for (int i = 0; i < Length; i++) - namem[i] = (char)getShort(offset + i * 2); - return new string(namem); - } - - /// It reads the ROI properties from the file and returns them as a string - /// - /// @return A string of characters. - string getRoiProps() - { - int hdr2Offset = getInt(HEADER2_OFFSET); - if (hdr2Offset == 0) - return null; - int offset = getInt(hdr2Offset + ROI_PROPS_OFFSET); - int Length = getInt(hdr2Offset + ROI_PROPS_LENGTH); - if (offset == 0 || Length == 0) - return null; - if (offset + Length * 2 > size) - return null; - char[] props = new char[Length]; - for (int i = 0; i < Length; i++) - props[i] = (char)getShort(offset + i * 2); - return new string(props); - } - - /// > Reads the point counters from the file - /// - /// @param n number of points - /// - /// @return The number of points in the polygon. - int[] getPointCounters(int n) - { - int hdr2Offset = getInt(HEADER2_OFFSET); - if (hdr2Offset == 0) - return null; - int offset = getInt(hdr2Offset + COUNTERS_OFFSET); - if (offset == 0) - return null; - if (offset + n * 4 > data.Length) - return null; - int[] counters = new int[n]; - for (int i = 0; i < n; i++) - counters[i] = getInt(offset + i * 4); - return counters; - } - - - /// It returns the value of the byte at the specified index in the data array - /// - /// @param bas The base address of the byte to be read. - /// - /// @return The byte at the given index. - int getByte(int bas) - { - return data[bas] & 255; - } - - /// > It reads two bytes from the data array, converts them to an integer, and returns the - /// integer - /// - /// @param bas the starting position of the data in the byte array - /// - /// @return The value of the short at the specified index. - int getShort(int bas) - { - int b0 = data[bas] & 255; - int b1 = data[bas + 1] & 255; - int n = (short)((b0 << 8) + b1); - if (n < -5000) - n = (b0 << 8) + b1; // assume n>32767 and unsigned - return n; - } - - /// It returns the unsigned short value of the data at the given index. - /// - /// @param bas The base address of the data. - /// - /// @return The value of the two bytes at the given index. - int getUnsignedShort(int bas) - { - int b0 = data[bas] & 255; - int b1 = data[bas + 1] & 255; - return (b0 << 8) + b1; - } - - /// It takes a byte array and an integer as input, and returns an integer - /// - /// @param bas The base address of the data. - /// - /// @return the value of the integer at the given base. - int getInt(int bas) - { - int b0 = data[bas] & 255; - int b1 = data[bas + 1] & 255; - int b2 = data[bas + 2] & 255; - int b3 = data[bas + 3] & 255; - return ((b0 << 24) + (b1 << 16) + (b2 << 8) + b3); - } - - /// It takes a base address, reads 4 bytes from that address, converts those 4 bytes into a float, and - /// returns the float - /// - /// @param bas The base address of the value you want to read. - /// - /// @return The float value of the integer at the base address. - float getFloat(int bas) - { - //return BitConverter.ToSingle(getInt(bas)); - byte[] bytes = BitConverter.GetBytes(getInt(bas)); - return BitConverter.ToSingle(bytes, 0); - } - - /// It takes a byte array and returns a ROI object - /// - /// @param bytes byte array of the ROI - /// - /// @return A ROI object. - public static ROI openFromByteArray(byte[] bytes) - { - ROI roi = null; - if (bytes == null || bytes.Length == 0) - return roi; - try - { - RoiDecoder decoder = new RoiDecoder(bytes, null); - roi = decoder.getRoi(); - } - catch (IOException e) - { - return null; - } - return roi; - } - - } - - /// > It converts the ROI type to the ImageJ type - /// - /// @param ROI The ROI object to be converted. - /// - /// @return The ROI type is being returned. - static int GetImageJType(ROI roi) - { - //private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, freehand = 7, - // traced = 8, angle = 9, point = 10; - switch (roi.type) - { - case ROI.Type.Rectangle: - return 1; - case ROI.Type.Point: - return 10; - case ROI.Type.Line: - return 3; - case ROI.Type.Polygon: - return 0; - case ROI.Type.Polyline: - return 5; - case ROI.Type.Freeform: - return 7; - case ROI.Type.Ellipse: - return 2; - case ROI.Type.Label: - default: - return 0; - } - } - - /// It takes an ROI object and returns the x and y coordinates of the points in the ROI - /// - /// @param ROI Region of Interest - /// @param xp x-coordinates of the points in the ROI - /// @param yp y-coordinates of the points in the ROI - static void GetPointsXY(ROI roi, out int[] xp, out int[] yp) - { - int[] x = new int[roi.PointsD.Count]; - int[] y = new int[roi.PointsD.Count]; - for (int i = 0; i < roi.PointsD.Count; i++) - { - PointD pd = ImageView.SelectedImage.ToImageSpace(roi.PointsD[i]); - x[i] = (int)pd.X; - y[i] = (int)pd.Y; - } - xp = x; - yp = y; - - } - - /// The function takes an ROI and returns the X and Y coordinates of the ROI in image space - /// - /// @param ROI Region of Interest - /// @param x The x coordinate of the ROI - /// @param y The y-coordinate of the ROI. - static void GetXY(ROI roi, out float x, out float y) - { - PointD pd = ImageView.SelectedImage.ToImageSpace(new PointD(roi.X, roi.Y)); - x = (float)pd.X; - y = (float)pd.Y; - } - /// > This function takes a ROI and returns the width and height of the ROI in pixels - /// - /// @param ROI The ROI object that you want to get the width and height of. - /// @param w width of the rectangle - /// @param h height of the rectangle - static void GetWH(ROI roi, out float w, out float h) - { - w = (float)ImageView.SelectedImage.ToImageSizeX(roi.W); - h = (float)ImageView.SelectedImage.ToImageSizeY(roi.H); - } - /// It shifts the value right by one bit, then shifts it right again by the number of positions - /// specified by the pos parameter - /// - /// @param value The value to be shifted. - /// @param pos the position of the bit to be moved - /// - /// @return The value of the bit at the position specified. - static int rightMove(int value, int pos) - { - if (pos != 0) - { - int mask = 0x7fffffff; - value >>= 1; - value &= mask; - value >>= pos - 1; - } - return value; - } - /* It takes a ROI object and writes it to a file */ - public class RoiEncoder - { - static int HEADER_SIZE = 64; - static int HEADER2_SIZE = 64; - static int VERSION = 228; // v1.52t (roi groups, scale stroke width) - private string path; - private FileStream f; - private int polygon = 0, rect = 1, oval = 2, line = 3, freeline = 4, polyline = 5, noRoi = 6, freehand = 7, - traced = 8, angle = 9, point = 10; - private byte[] data; - private string roiName; - private int roiNameSize; - private string roiProps; - private int roiPropsSize; - private int countersSize; - private int[] counters; - private bool subres = true; - - /** Creates an RoiEncoder using the specified path. */ - public RoiEncoder(String path) - { - this.path = path; - } - - /** Creates an RoiEncoder using the specified OutputStream. */ - public RoiEncoder(FileStream f) - { - this.f = f; - } - - /** Saves the specified ROI as a file, returning 'true' if successful. */ - public static bool save(ROI roi, String path) - { - RoiEncoder re = new RoiEncoder(path); - try - { - re.write(roi); - } - catch (IOException e) - { - return false; + } } - return true; } - - /** Save the Roi to the file of stream. */ - public void write(ROI roi) + for(int t = 0;t < chs; t++) { - if (f != null) - { - write(roi, f); - - } + if(isrgb) + bm.Channels.Add(new AForge.Channel(t, rgb, 3)); else + bm.Channels.Add(new AForge.Channel(t, rgb, 1)); + if (t == 0) { - f = new FileStream(path, FileMode.Create); - write(roi, f); - f.Close(); + bm.rgbChannels[0] = 0; } - } - - /** Saves the specified ROI as a byte array. - public static byte[] saveAsByteArray(ROI roi) - { - if (roi == null) return null; - byte[] bytes = null; - try - { - MemoryStream outs = new MemoryStream(4096); - RoiEncoder encoder = new RoiEncoder(path); - encoder.write(roi); - outs.close(); - bytes = out.toByteArray(); - } - catch (IOException e) - { - return null; - } - return bytes; - } - */ - /// It takes a ROI object and writes it to a file - /// - /// @param ROI The ROI object to be saved - /// @param FileStream f - /// - /// @return The data is being returned as a byte array. - void write(ROI roi, FileStream f) - { - RectangleD r = new RectangleD(roi.Rect.X, roi.Rect.Y, roi.Rect.W, roi.Rect.H); - //if (r.width > 60000 || r.height > 60000 || r.x > 60000 || r.y > 60000) - // roi.enableSubPixelResolution(); - //int roiType = GetImageJType(roi); - int type = GetImageJType(roi); - int options = 0; - //if (roi.getScaleStrokeWidth()) - // options |= RoiDecoder.SCALE_STROKE_WIDTH; - roiName = roi.Text; - if (roiName != null) - roiNameSize = roiName.Length * 2; - else - roiNameSize = 0; - - roiProps = roi.properties; - if (roiProps != null) - roiPropsSize = roiProps.Length * 2; else - roiPropsSize = 0; - /* - switch (roiType) { - case Roi.POLYGON: type = polygon; break; - case Roi.FREEROI: type = freehand; break; - case Roi.TRACED_ROI: type = traced; break; - case Roi.OVAL: type = oval; break; - case Roi.LINE: type = line; break; - case Roi.POLYLINE: type = polyline; break; - case Roi.FREELINE: type = freeline; break; - case Roi.ANGLE: type = angle; break; - case Roi.COMPOSITE: type = rect; break; // shape array size (36-39) will be >0 to indicate composite type - case Roi.POINT: type = point; break; - default: type = rect; break; - } - */ - /* - if (roiType == Roi.COMPOSITE) { - saveShapeRoi(roi, type, f, options); - return; - } - */ - int n = 0; - int[] - x = null, y = null; - float[] - xf = null, yf = null; - int floatSize = 0; - //if (roi instanceof PolygonRoi) { - //PolygonRoi proi = (PolygonRoi)roi; - //Polygon p = proi.getNonSplineCoordinates(); - n = roi.PointsD.Count; //p.npoints; - //x = p.xpoints; - //y = p.ypoints; - GetPointsXY(roi, out x, out y); - if (subres) - { - /* - if (proi.isSplineFit()) - fp = proi.getNonSplineFloatPolygon(); - else - fp = roi.getFloatPolygon(); - if (n == fp.npoints) - { - options |= RoiDecoder.SUB_PIXEL_RESOLUTION; - if (roi.getDrawOffset()) - options |= RoiDecoder.DRAW_OFFSET; - xf = fp.xpoints; - yf = fp.ypoints; - floatSize = n * 8; - } - */ - } - - - countersSize = 0; - /* - if (roi instanceof PointRoi) { - counters = ((PointRoi)roi).getCounters(); - if (counters != null && counters.length >= n) - countersSize = n * 4; - } - */ - data = new byte[HEADER_SIZE + HEADER2_SIZE + n * 4 + floatSize + roiNameSize + roiPropsSize + countersSize]; - data[0] = 73; data[1] = 111; data[2] = 117; data[3] = 116; // "Iout" - putShort(RoiDecoder.VERSION_OFFSET, VERSION); - data[RoiDecoder.TYPE] = (byte)type; - float px, py, pw, ph; - GetXY(roi, out px, out py); - GetWH(roi, out pw, out ph); - putShort(RoiDecoder.TOP, (int)py); - putShort(RoiDecoder.LEFT, (int)px); - putShort(RoiDecoder.BOTTOM, (int)(py + ph)); - putShort(RoiDecoder.RIGHT, (int)(px + pw)); - var PointsImage = ImageView.SelectedImage.ToImageSpace(roi.PointsD); - if (subres && (type == rect || type == oval)) - { - //FloatPolygon p = null; - /* - if (roi instanceof OvalRoi) - p = ((OvalRoi)roi).getFloatPolygon4(); - else - { - int d = roi.getCornerDiameter(); - if (d > 0) - { - roi.setCornerDiameter(0); - p = roi.getFloatPolygon(); - roi.setCornerDiameter(d); - } - else - p = roi.getFloatPolygon(); - } - */ - if (roi.PointsD.Count == 4) - { - putFloat(RoiDecoder.XD, (float)PointsImage[0].X); - putFloat(RoiDecoder.YD, (float)PointsImage[0].Y); - //putFloat(RoiDecoder.WIDTHD, p.xpoints[1] - roi.PointsD[0]); - //putFloat(RoiDecoder.HEIGHTD, p.ypoints[2] - p.ypoints[1]); - putFloat(RoiDecoder.WIDTHD, (float)PointsImage[1].X - (float)PointsImage[0].X); - putFloat(RoiDecoder.HEIGHTD, (float)PointsImage[2].Y - (float)PointsImage[1].Y); - options |= RoiDecoder.SUB_PIXEL_RESOLUTION; - putShort(RoiDecoder.OPTIONS, options); - } - } - if (n > 65535 && type != point) + if (t == 1) { - if (type == polygon || type == freehand || type == traced) - { - //String name = roi.Text; - //roi = new ShapeRoi(roi); - //if (name != null) roi.setName(name); - saveShapeRoi(roi, rect, f, options); - return; - } - //ij.IJ.beep(); - //ij.IJ.log("Non-polygonal selections with more than 65k points cannot be saved."); - n = 65535; + bm.rgbChannels[1] = 1; } - if (type == point && n > 65535) - putInt(RoiDecoder.SIZE, n); else - putShort(RoiDecoder.N_COORDINATES, n); - putInt(RoiDecoder.POSITION, roi.coord.Z); - - /* - if (type == rect) + if (t == 2) { - int arcSize = roi.getCornerDiameter(); - if (arcSize > 0) - putShort(RoiDecoder.ROUNDED_RECT_ARC_SIZE, arcSize); - } - */ - - if (type == line) //(roi instanceof Line) - { - //Line line = (Line)roi; - putFloat(RoiDecoder.X1, (float)PointsImage[0].X); - putFloat(RoiDecoder.Y1, (float)PointsImage[0].Y); - putFloat(RoiDecoder.X2, (float)PointsImage[1].X); - putFloat(RoiDecoder.Y2, (float)PointsImage[1].Y); - /* - if (roi instanceof Arrow) { - putShort(RoiDecoder.SUBTYPE, RoiDecoder.ARROW); - if (((Arrow)roi).getDoubleHeaded()) - options |= RoiDecoder.DOUBLE_HEADED; - if (((Arrow)roi).getOutline()) - options |= RoiDecoder.OUTLINE; - putShort(RoiDecoder.OPTIONS, options); - putByte(RoiDecoder.ARROW_STYLE, ((Arrow)roi).getStyle()); - putByte(RoiDecoder.ARROW_HEAD_SIZE, (int)((Arrow)roi).getHeadSize()); - } else - { - if (roi.getDrawOffset()) - options |= RoiDecoder.SUB_PIXEL_RESOLUTION + RoiDecoder.DRAW_OFFSET; - } - */ - } - - if (type == point) - { - //PointRoi point = (PointRoi)roi; - putByte(RoiDecoder.POINT_TYPE, 1);//point.getPointType()); - putShort(RoiDecoder.STROKE_WIDTH, (int)roi.strokeWidth); - /* - if (point.getShowLabels()) - options |= RoiDecoder.SHOW_LABELS; - if (point.promptBeforeDeleting()) - options |= RoiDecoder.PROMPT_BEFORE_DELETING; - */ - } - - if (type == oval) - { - /* - double[] p = null; - if (roi instanceof RotatedRectRoi) { - putShort(RoiDecoder.SUBTYPE, RoiDecoder.ROTATED_RECT); - p = ((RotatedRectRoi)roi).getParams(); - } else - { - */ - putShort(RoiDecoder.SUBTYPE, RoiDecoder.ELLIPSE); - //p = ((EllipseRoi)roi).getParams(); - //} - float fx, fy, fw, fh; - GetXY(roi, out fx, out fy); - GetWH(roi, out fw, out fh); - putFloat(RoiDecoder.X1, fx); - putFloat(RoiDecoder.Y1, fy); - putFloat(RoiDecoder.X2, fw); - putFloat(RoiDecoder.Y2, fh); - //putFloat(RoiDecoder.FLOAT_PARAM, (float)p[4]); - } - - // save stroke width, stroke color and fill color (1.43i or later) - if (VERSION >= 218) - { - saveStrokeWidthAndColor(roi); - /* - if ((roi instanceof PolygonRoi) && ((PolygonRoi)roi).isSplineFit()) { - options |= RoiDecoder.SPLINE_FIT; - putShort(RoiDecoder.OPTIONS, options); - } - */ - } - - if (roi.type == ROI.Type.Label)//(n == 0 && roi instanceof TextRoi) - saveTextRoi(roi); - /* - else if (n == 0 && roi instanceof ImageRoi) - options = saveImageRoi((ImageRoi)roi, options); - */ - //else - putHeader2(roi, HEADER_SIZE + n * 4 + floatSize); - - if (n > 0) - { - int base1 = 64; - int base2 = base1 + 2 * n; - for (int i = 0; i < n; i++) - { - putShort(base1 + i * 2, (int)(x[i] - px)); - putShort(base2 + i * 2, (int)(y[i] - py)); - } - if (xf != null) - { - base1 = 64 + 4 * n; - base2 = base1 + 4 * n; - for (int i = 0; i < n; i++) - { - putFloat(base1 + i * 4, xf[i]); - putFloat(base2 + i * 4, yf[i]); - } - } + bm.rgbChannels[2] = 2; } - - //saveOverlayOptions(roi, options); - f.Write(data,0,data.Length); - } - - /// It saves the stroke width and color of the ROI - /// - /// @param ROI The ROI object that is being saved. - void saveStrokeWidthAndColor(ROI roi) - { - //BasicStroke stroke = roi.getStroke(); - //if (stroke != null) - putShort(RoiDecoder.STROKE_WIDTH, (int)roi.strokeWidth); - AForge.Color strokeColor = roi.strokeColor; - int intColor = (strokeColor.R << 16) | (strokeColor.G << 8) | (strokeColor.B); - putInt(RoiDecoder.STROKE_COLOR, 0); - AForge.Color fillColor = roi.fillColor; - int intFillColor = (fillColor.R << 16) | (fillColor.G << 8) | (fillColor.B); - putInt(RoiDecoder.FILL_COLOR, 0); - } - - /// It writes the header of the ROI file, then writes the header2 of the ROI file, then - /// writes the name of the ROI file, then writes the properties of the ROI file - /// - /// @param ROI the ROI object - /// @param type 0 - /// @param FileStream the file stream to write to - /// @param options 0 - void saveShapeRoi(ROI roi, int type, FileStream f, int options) - { - //float[] shapeArray = ((ShapeRoi)roi).getShapeAsArray(); - //if (shapeArray == null) return; - //BufferedOutputStream bout = new BufferedOutputStream(f); - - data = new byte[HEADER_SIZE + HEADER2_SIZE + roiNameSize + roiPropsSize];//shapeArray.length * 4 + roiNameSize + roiPropsSize]; - data[0] = 73; data[1] = 111; data[2] = 117; data[3] = 116; // "Iout" - - putShort(RoiDecoder.VERSION_OFFSET, VERSION); - data[RoiDecoder.TYPE] = (byte)type; - - float x, y, w, h; - GetXY(roi, out x, out y); - GetWH(roi, out w, out h); - putShort(RoiDecoder.TOP, (int)y); - putShort(RoiDecoder.LEFT, (int)x); - putShort(RoiDecoder.BOTTOM, (int)(y + h)); - putShort(RoiDecoder.RIGHT, (int)(x + w)); - putInt(RoiDecoder.POSITION, roi.coord.Z); - ///putShort(16, n); - //putInt(36, shapeArray.Length); // non-zero segment count indicate composite type - if (VERSION >= 218) - saveStrokeWidthAndColor(roi); - //saveOverlayOptions(roi, options); - - // handle the actual data: data are stored segment-wise, i.e., - // the type of the segment followed by 0-6 control point coordinates. - /* - int bas = 64; - for (int i = 0; i < shapeArray.Length; i++) - { - putFloat(bas, shapeArray[i]); - bas += 4; - } - */ - int hdr2Offset = HEADER_SIZE;// + shapeArray.Length * 4; - //ij.IJ.log("saveShapeRoi: "+HEADER_SIZE+" "+shapeArray.length); - putHeader2(roi, hdr2Offset); - f.Write(data, 0, data.Length); - f.Flush(); - } - - /* - void saveOverlayOptions(ROI roi, int options) - { - Overlay proto = roi.getPrototypeOverlay(); - if (proto.getDrawLabels()) - options |= RoiDecoder.OVERLAY_LABELS; - if (proto.getDrawNames()) - options |= RoiDecoder.OVERLAY_NAMES; - if (proto.getDrawBackgrounds()) - options |= RoiDecoder.OVERLAY_BACKGROUNDS; - Font font = proto.getLabelFont(); - if (font != null && font.getStyle() == Font.BOLD) - options |= RoiDecoder.OVERLAY_BOLD; - if (proto.scalableLabels()) - options |= RoiDecoder.SCALE_LABELS; - putShort(RoiDecoder.OPTIONS, options); - } - */ - /// The function takes a ROI object and converts it to a byte array - /// - /// @param ROI The ROI object - void saveTextRoi(ROI roi) - { - //Font font = roi.getCurrentFont(); - string fontName = roi.family.ToString(); - int size = (int)roi.fontSize; - int drawStringMode = 0; //roi.getDrawStringMode() ? 1024 : 0; - int style = 0;//font.getStyle() + roi.getJustification() * 256 + drawStringMode; - string text = roi.roiName; - float angle = 0; - int angleLength = 4; - int fontNameLength = fontName.Length; - int textLength = text.Length; - int textRoiDataLength = 16 + fontNameLength * 2 + textLength * 2 + angleLength; - byte[] data2 = new byte[HEADER_SIZE + HEADER2_SIZE + textRoiDataLength + roiNameSize + roiPropsSize]; - Array.Copy(data, 0, data2, 0, HEADER_SIZE); - data = data2; - putShort(RoiDecoder.SUBTYPE, RoiDecoder.TEXT); - putInt(HEADER_SIZE, size); - putInt(HEADER_SIZE + 4, style); - putInt(HEADER_SIZE + 8, fontNameLength); - putInt(HEADER_SIZE + 12, textLength); - for (int i = 0; i < fontNameLength; i++) - putShort(HEADER_SIZE + 16 + i * 2, fontName.ElementAt(i)); - for (int i = 0; i < textLength; i++) - putShort(HEADER_SIZE + 16 + fontNameLength * 2 + i * 2, text.ElementAt(i)); - int hdr2Offset = HEADER_SIZE + textRoiDataLength; - //ij.IJ.log("saveTextRoi: "+HEADER_SIZE+" "+textRoiDataLength+" "+fontNameLength+" "+textLength); - putFloat(hdr2Offset - angleLength, angle); - putHeader2(roi, hdr2Offset); - } - /* - private int saveImageRoi(ROI roi, int options) - { - byte[] bytes = roi.getSerializedImage(); - int imageSize = bytes.length; - byte[] data2 = new byte[HEADER_SIZE + HEADER2_SIZE + imageSize + roiNameSize + roiPropsSize]; - System.arraycopy(data, 0, data2, 0, HEADER_SIZE); - data = data2; - putShort(RoiDecoder.SUBTYPE, RoiDecoder.IMAGE); - for (int i = 0; i < imageSize; i++) - putByte(HEADER_SIZE + i, bytes[i] & 255); - int hdr2Offset = HEADER_SIZE + imageSize; - double opacity = roi.getOpacity(); - putByte(hdr2Offset + RoiDecoder.IMAGE_OPACITY, (int)(opacity * 255.0)); - putInt(hdr2Offset + RoiDecoder.IMAGE_SIZE, imageSize); - if (roi.getZeroTransparent()) - options |= RoiDecoder.ZERO_TRANSPARENT; - putHeader2(roi, hdr2Offset); - return options; - } - */ - /// It writes the header information of the ROI to the file - /// - /// @param ROI the ROI object - /// @param hdr2Offset the offset of the header2 - void putHeader2(ROI roi, int hdr2Offset) - { - //ij.IJ.log("putHeader2: "+hdr2Offset+" "+roiNameSize+" "+roiName); - putInt(RoiDecoder.HEADER2_OFFSET, hdr2Offset); - putInt(hdr2Offset + RoiDecoder.C_POSITION, roi.coord.C + 1); - putInt(hdr2Offset + RoiDecoder.Z_POSITION, roi.coord.Z + 1); - putInt(hdr2Offset + RoiDecoder.T_POSITION, roi.coord.T + 1); - //Overlay proto = roi.getPrototypeOverlay(); - AForge.Color overlayLabelColor = roi.strokeColor; //proto.getLabelColor(); - int intColor = (overlayLabelColor.R << 16) | (overlayLabelColor.G << 8) | (overlayLabelColor.B); - //if (overlayLabelColor != null) - putInt(hdr2Offset + RoiDecoder.OVERLAY_LABEL_COLOR, 0); - //Font font = proto.getLabelFont(); - //if (font != null) - putShort(hdr2Offset + RoiDecoder.OVERLAY_FONT_SIZE, (int)roi.fontSize); - if (roiNameSize > 0) - putName(roi, hdr2Offset); - double strokeWidth = roi.strokeWidth; - //if (roi.getStroke() == null) - // strokeWidth = 0.0; - putFloat(hdr2Offset + RoiDecoder.FLOAT_STROKE_WIDTH, (float)strokeWidth); - if (roiPropsSize > 0) - putProps(roi, hdr2Offset); - if (countersSize > 0) - putPointCounters(roi, hdr2Offset); - putByte(hdr2Offset + RoiDecoder.GROUP, roi.serie);//roi.getGroup()); - } - - /// The function takes a ROI object, and an offset value, and writes the name of the ROI to - /// the file at the offset - /// - /// @param ROI the ROI object - /// @param hdr2Offset the offset of the header2 - void putName(ROI roi, int hdr2Offset) - { - int offset = hdr2Offset + HEADER2_SIZE; - int nameLength = roiNameSize / 2; - putInt(hdr2Offset + RoiDecoder.NAME_OFFSET, offset); - putInt(hdr2Offset + RoiDecoder.NAME_LENGTH, nameLength); - for (int i = 0; i < nameLength; i++) - putShort(offset + i * 2, roiName.ElementAt(i)); - } - - /// The function `putProps` takes a ROI object and an integer offset as parameters. It then - /// calculates the offset of the ROI properties, and the length of the ROI properties. It - /// then writes the offset and length to the header, and then writes the ROI properties to - /// the byte array - /// - /// @param ROI the ROI object - /// @param hdr2Offset the offset of the header2 - void putProps(ROI roi, int hdr2Offset) - { - int offset = hdr2Offset + HEADER2_SIZE + roiNameSize; - int roiPropsLength = roiPropsSize / 2; - putInt(hdr2Offset + RoiDecoder.ROI_PROPS_OFFSET, offset); - putInt(hdr2Offset + RoiDecoder.ROI_PROPS_LENGTH, roiPropsLength); - for (int i = 0; i < roiPropsLength; i++) - putShort(offset + i * 2, roiProps.ElementAt(i)); - } - - /// > The function `putPointCounters` writes the counters of the ROI to the byte array - /// - /// @param ROI the region of interest - /// @param hdr2Offset the offset of the header2 - void putPointCounters(ROI roi, int hdr2Offset) - { - int offset = hdr2Offset + HEADER2_SIZE + roiNameSize + roiPropsSize; - putInt(hdr2Offset + RoiDecoder.COUNTERS_OFFSET, offset); - for (int i = 0; i < countersSize / 4; i++) - putInt(offset + i * 4, counters[i]); - countersSize = 0; - } - - /// It takes a base address and a value, and writes the value to the base address - /// - /// @param bas The base address of the memory block. - /// @param v The value to be written to the memory address. - void putByte(int bas, int v) - { - data[bas] = (byte)v; - } - - /// The function takes an integer value and shifts it right by 8 bits - /// - /// @param bas the base address of the array - /// @param v the value to be converted to a byte - void putShort(int bas, int v) - { - //data[bas] = (byte)(v >>> 8); - //data[bas] = (byte)UnsignedRightShift(v, 8); - data[bas] = (byte)rightMove(v, 8); - data[bas + 1] = (byte)v; - } - - /// > It takes a float and converts it to a byte array, then it takes the first 4 bytes of - /// the array and puts them into the data array - /// - /// @param bas the base address of the data array - /// @param v the float value to be converted - void putFloat(int bas, float v) - { - byte[] bytes = BitConverter.GetBytes(v); - int tmp = BitConverter.ToInt32(bytes, 0); - //int tmp = BitConverter.SingleToInt32Bits(v);//Float.floatToIntBits(v); - data[bas] = (byte)(tmp >> 24); - data[bas + 1] = (byte)(tmp >> 16); - data[bas + 2] = (byte)(tmp >> 8); - data[bas + 3] = (byte)tmp; } + bm.Resolutions.Add(new Resolution(pl.getWidth(), pl.getHeight(), bm.Buffers[0].PixelFormat, PhysicalX, PhysicalY, PhysicalZ, vol.Location.X, vol.Location.Y, vol.Location.X)); + bm.Volume = vol; + bm.littleEndian = BitConverter.IsLittleEndian; + bm.seriesCount = 1; + bm.bitsPerPixel = b; + BioImage.AutoThreshold(bm, true); + if (rgb > 8) + bm.StackThreshold(true); + else + bm.StackThreshold(false); - /// It takes an integer and writes it to the byte array - /// - /// @param bas The base address of the data array. - /// @param i The integer to be converted to bytes. - void putInt(int bas, int i) + if (bm.RGBChannelCount == 4) { - data[bas] = (byte)(i >> 24); - data[bas + 1] = (byte)(i >> 16); - data[bas + 2] = (byte)(i >> 8); - data[bas + 3] = (byte)i; + bm.Channels.Last().SamplesPerPixel = 4; } + return bm; } - } } diff --git a/BioCore/Source/ImageView.cs b/BioCore/Source/ImageView.cs index fa4db33..6578f13 100644 --- a/BioCore/Source/ImageView.cs +++ b/BioCore/Source/ImageView.cs @@ -175,6 +175,10 @@ public static BioImage SelectedImage else return null; } + set + { + App.viewer.Images[selectedIndex] = value; + } } public static Bitmap SelectedBuffer { diff --git a/BioCore/Source/TabsView.cs b/BioCore/Source/TabsView.cs index 563e0fe..7f13385 100644 --- a/BioCore/Source/TabsView.cs +++ b/BioCore/Source/TabsView.cs @@ -73,7 +73,7 @@ public TabsView(string[] arg) } if (arg[i].EndsWith(".ijm")) { - ImageJ.RunMacro(arg[i], ""); + Fiji.RunMacro(arg[i], ""); return; } else @@ -120,7 +120,7 @@ private void Init() foreach (char c in a) { ToolStripMenuItem me = new ToolStripMenuItem(c.ToString()); - foreach (ImageJ.Macro.Command command in ImageJ.Macro.Commands.Values) + foreach (Fiji.Macro.Command command in Fiji.Macro.Commands.Values) { if (command.Name.StartsWith(c)) { @@ -137,7 +137,7 @@ private void Init() mi.Click += Mi_Click; runToolStripMenuItem1.DropDownItems.Add(mi); } - foreach (ImageJ.Macro.Command c in ImageJ.Macros) + foreach (Fiji.Macro.Command c in Fiji.Macros) { ToolStripMenuItem mi = new ToolStripMenuItem(c.Name); mi.Click += Mi_Click; @@ -151,7 +151,7 @@ private void Mi_Click(object? sender, EventArgs e) if (m.Text.EndsWith(".ijm") || m.Text.EndsWith(".txt") && !m.Text.EndsWith(".cs")) { string ma = File.ReadAllText(m.Text); - ImageJ.RunOnImage(ma, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); + Fiji.RunOnImage(ma, 0, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); } else Scripting.RunByName(m.Text); @@ -161,7 +161,7 @@ private void MenuItem_Click(object? sender, EventArgs e) { if (ImageView.SelectedImage == null) return; ToolStripMenuItem m = (ToolStripMenuItem)sender; - ImageJ.RunOnImage("run(\"" + m.Text + "\");", BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); + Fiji.RunOnImage("run(\"" + m.Text + "\");",0, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.newTab); ToolStripMenuItem mi = new ToolStripMenuItem(m.Text); mi.Click += MenuItem_Click; bool con = false; @@ -1322,7 +1322,7 @@ private void importImageJROIToolStripMenuItem_Click(object sender, EventArgs e) return; foreach (string item in openImageJDialog.FileNames) { - ImageView.SelectedImage.Annotations.Add(ImageJ.RoiDecoder.open(item)); + ImageView.SelectedImage.Annotations.Add(Fiji.RoiDecoder.open(item)); } App.viewer.UpdateView(); } @@ -1343,7 +1343,7 @@ private void exportImageJROIToolStripMenuItem_Click(object sender, EventArgs e) foreach (ROI roi in ImageView.SelectedImage.Annotations) { string s = Path.GetDirectoryName(saveImageJDialog.FileName) + "//" + Path.GetFileNameWithoutExtension(saveImageJDialog.FileName) + "-" + i + ".roi"; - ImageJ.RoiEncoder.save(roi, s); + Fiji.RoiEncoder.save(roi, s); i++; } App.viewer.UpdateView();