BioImager  5.0.0
A .NET microscopy imaging application based on Bio library. Supports various microscopes by using imported libraries & GUI automation. Supports XInput game controllers to move stage, take images, run ImageJ macros on images or Bio C# scripts.
Loading...
Searching...
No Matches
ML.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using AForge;
6using Microsoft.ML.OnnxRuntime;
7using Microsoft.ML.OnnxRuntime.Tensors;
8using TorchSharp;
9using MathNet.Numerics.Statistics;
10using YamlDotNet;
11using YamlDotNet.Serialization.NamingConventions;
12using YamlDotNet.Serialization;
13using System.Collections;
14using Bitmap = AForge.Bitmap;
15using BioLib;
16namespace BioImager.ML
17{
18 public static class ML
19 {
20 public class Model
21 {
22 public Model(string file)
23 {
24 File = file;
25 Name = Path.GetFileNameWithoutExtension(file);
26 if (file.EndsWith(".onnx"))
27 {
29 foreach (var m in InferenceSession.InputMetadata)
30 {
31 InputValueTypes.Add(m.Value);
32 }
33 foreach (var m in InferenceSession.InputNames)
34 {
35 InputValueNames.Add(m);
36 }
37 foreach (var m in InferenceSession.OutputMetadata)
38 {
39 OutputValueTypes.Add(m.Value);
40 }
41 }
42 else if (file.EndsWith(".pt"))
43 {
44 try
45 {
46 Module = torch.jit.load(file);
47 Module.eval();
48 var chs = Module.named_modules();
49 var input = chs.First();
50 InputModule = input.module;
51 var output = chs.Last();
52 OutputModule = output.module;
53
54
55 }
56 catch (Exception e)
57 {
58 Console.WriteLine(e.Message.ToString());
59 }
60 string f = Path.GetDirectoryName(file) + "/" + Path.GetFileNameWithoutExtension(file) + ".yaml";
61 if (!System.IO.File.Exists(f))
62 {
63 MessageBox.Show("No corresponding model Yaml metadata file for:" + Name);
64 return;
65 }
66 using (var reader = new StreamReader(f))
67 {
68 var dynamicDeserializer = new DeserializerBuilder().Build();
69 Metadata = dynamicDeserializer.Deserialize<Dictionary<string, object>>(reader);
70 IDictionary conf = (IDictionary)Metadata["config"];
71 Object inpp = Metadata["inputs"];
72 List<Object> inpps = (List<Object>)inpp;
73 IDictionary inpinfo = (IDictionary)inpps[0];
74 IDictionary imagej = (IDictionary)conf["deepimagej"];
75 IDictionary test = (IDictionary)imagej["test_information"];
76 Object inputs = test["inputs"];
77 List<Object> inps = (List<Object>)inputs;
78 IDictionary info = (IDictionary)inps[0];
79 IDictionary inputinfo = (IDictionary)inpps[0];
80 IDictionary shapeinfo = (IDictionary)inputinfo["shape"];
81 List<Object> shapeinfomin = (List<Object>)shapeinfo["min"];
82 int len = shapeinfomin.Count;
83 string size = (string)info["size"];
84 string[] sts = size.Split("x");
85 if (len != sts.Length)
86 {
87 shape = new long[len];
88 for (int i = 0; i < len; i++)
89 {
90 if (len - i > sts.Length)
91 {
92 shape[i] = 1;
93 }
94 else
95 {
96 shape[i] = long.Parse(sts[sts.Length - i]);
97 }
98 }
99 }
100 else
101 {
102 shape = new long[sts.Length];
103 for (int i = 0; i < sts.Length; i++)
104 {
105 String s = sts[i];
106 shape[i] = long.Parse(s);
107 }
108 }
109 List<Object> val = (List<Object>)inpinfo["data_range"];
110 MaxValue = int.Parse(val[1].ToString());
111 }
112 }
113 }
114 public Dictionary<string, object> Metadata { get; set; } = null;
116 public torch.jit.ScriptModule Module { get; set; }
117 public torch.nn.Module InputModule { get; set; }
118 public torch.nn.Module OutputModule { get; set; }
119 private List<NodeMetadata> InputValueTypes { get; set; } = new List<NodeMetadata>();
120 private List<NodeMetadata> OutputValueTypes { get; set; } = new List<NodeMetadata>();
121 private List<string> InputValueNames { get; set; } = new List<string>();
122 public string File { get; set; }
123 public string Name { get; set; }
124 public int Width
125 {
126 get
127 {
128 if (File.EndsWith(".onnx"))
129 {
130 if (InputValueTypes.Count > 0)
131 {
132 int w = InputValueTypes[0].Dimensions[2];
133 if (w == 0)
134 {
135 if (OutputValueTypes.Count > 0)
136 {
137 for (int i = 0; i < OutputValueTypes.Count; i++)
138 {
139 int max = int.MinValue;
140 for (int j = 0; j < OutputValueTypes[i].Dimensions.Length; j++)
141 {
142 if (OutputValueTypes[i].Dimensions[j] > max)
143 max = OutputValueTypes[i].Dimensions[j];
144 }
145 return max;
146 }
147 }
148 }
149 return w;
150 }
151 return 0;
152 }
153 else
154 {
155 if (shape.Length == 4)
156 return (int)Shape[0];
157 else
158 return (int)Shape[4];
159 }
160 }
161 }
162 public int Height
163 {
164 get
165 {
166 if (File.EndsWith(".onnx"))
167 {
168 if (InputValueTypes.Count > 0)
169 {
170 int h = InputValueTypes[0].Dimensions[3];
171 if (h == 0)
172 {
173 if (OutputValueTypes.Count > 0)
174 {
175 for (int i = 0; i < OutputValueTypes.Count; i++)
176 {
177 int max = int.MinValue;
178 for (int j = 0; j < OutputValueTypes[i].Dimensions.Length; j++)
179 {
180 if (OutputValueTypes[i].Dimensions[j] > max)
181 max = OutputValueTypes[i].Dimensions[j];
182 }
183 return max;
184 }
185 }
186 }
187 return h;
188 }
189 return 0;
190 }
191 else
192 {
193 if (shape.Length == 4)
194 return (int)Shape[1];
195 else
196 return (int)Shape[3];
197 }
198 }
199 }
200 public int Depth
201 {
202 get
203 {
204 if (shape.Length == 4)
205 return (int)Shape[2];
206 else
207 return (int)shape[1];
208 }
209 }
210 long[] shape;
211 public long[] Shape
212 {
213 get
214 {
215 if (Metadata == null)
216 return new long[] { 1, 3, 256, 256 };
217 else
218 {
219 return shape;
220 }
221 }
222 }
223 public int MaxValue { get; set; }
224 public ModelOutputType ModelType { get; set; }
225 public enum ModelOutputType
226 {
228 image
229 }
230 public void Run(BioImage b)
231 {
232 if (File.EndsWith(".onnx"))
233 RunONNX(b);
234 else if (File.EndsWith(".pt"))
235 RunTorch(b);
236 }
237 public void RunONNX(BioImage b)
238 {
239 BioImage bb = new BioImage("output.ome.tif");
240 if (b.Type != BioImage.ImageType.pyramidal)
241 {
242 for (int i = 0; i < b.Buffers.Count; i++)
243 {
244 Bitmap bm = ResizeBilinear(b.Buffers[i], Width, Height);
245 int w = Width;
246 int h = Height;
247 int d = InputValueTypes[0].Dimensions[1];
248 float[] img = new float[w * h * d];
249 for (int y = 0; y < h; y++)
250 {
251 for (int x = 0; x < w; x++)
252 {
253 for (int c = 0; c < d; c++)
254 {
255 int index = (y * w * d) + x;
256 if (c > bm.RGBChannelsCount)
257 {
258 img[index] = 0;
259 }
260 else
261 {
262 if (bm.BitsPerPixel > 8)
263 img[index] = bm.GetValue(x, y, c);
264 else
265 img[index] = bm.GetValue(x, y, c);
266 }
267 }
268 }
269 }
270 var tensor = new DenseTensor<float>(img, new[] { 1, InputValueTypes[0].Dimensions[1], w, h });
271 var inputs = new List<NamedOnnxValue>
272 {
273 NamedOnnxValue.CreateFromTensor(InputValueNames[0], tensor)
274 };
275 var results = InferenceSession.Run(inputs);
276 int r = 0;
277 foreach (var result in results)
278 {
279 if (OutputValueTypes[r].Dimensions[3] == Width)
280 {
281 float[] outputmask = result.AsTensor<float>().ToArray();
282 double mean = outputmask.Mean();
283 for (int c = 0; c < OutputValueTypes[r].Dimensions[1]; c++)
284 {
285 Bitmap bmp = new Bitmap(Width, Height, b.Buffers[0].PixelFormat);
286 for (int y = 0; y < h; y++)
287 {
288 for (int x = 0; x < w; x++)
289 {
290 int index = (y * w * d) + x;
291 bmp.SetPixel(x, y, new ColorS((ushort)(outputmask[index] / mean)));
292 }
293 }
294 bb.Buffers.Add(ResizeBilinear(bmp, b.SizeX, b.SizeY));
295 }
296 }
297 r++;
298 }
299 }
300 bb.Channels.AddRange(b.Channels);
301 if (OutputValueTypes[0].Dimensions[1] > 1)
302 {
303 bb.Channels.AddRange(b.Channels);
304 for (int i = 0; i < bb.Channels.Count; i++)
305 {
306 bb.Channels[i].Index = i;
307 }
308 }
309 bb.UpdateCoords(b.SizeZ, b.SizeC * OutputValueTypes[0].Dimensions[1], b.SizeT);
310 bb.Resolutions.Add(new Resolution(bb.SizeX, bb.SizeY, bb.Buffers[0].PixelFormat, b.PhysicalSizeX, b.PhysicalSizeY, b.PhysicalSizeZ, b.StageSizeX, b.StageSizeY, b.StageSizeZ));
311 bb.Volume = b.Volume;
312 BioImage.AutoThreshold(bb, true);
313 Images.AddImage(bb);
314 App.tabsView.AddTab(bb);
315 }
316 else
317 throw new NotImplementedException();
318 }
319 public void RunTorch(BioImage b)
320 {
321 BioImage bb = new BioImage("output.ome.tif");
322 if (b.Type != BioImage.ImageType.pyramidal)
323 {
324 int r = 0;
325 int resDepth = 1;
326 if (shape.Length == 4)
327 {
328 for (int i = 0; i < b.Buffers.Count; i++)
329 {
330 Bitmap bm = ResizeBilinear(b.Buffers[i], Width, Height);
331 int w = Width;
332 int h = Height;
333 int d = Depth;
334 float[,,,] img = new float[1, Depth, Width, Height];
335 for (int y = 0; y < h; y++)
336 {
337 for (int x = 0; x < w; x++)
338 {
339 for (int c = 0; c < d; c++)
340 {
341 int index = (y * w * c) + x;
342 if (c >= bm.RGBChannelsCount)
343 {
344 img[0, c, x, y] = 0;
345 }
346 else
347 {
348 if (bm.BitsPerPixel > 8)
349 img[0, c, x, y] = ((float)bm.GetValue(x, y, c) / (float)ushort.MaxValue) * MaxValue;
350 else
351 img[0, c, x, y] = ((float)bm.GetValue(x, y, c) / (float)byte.MaxValue) * MaxValue;
352 }
353 }
354 }
355 }
356 var tensor = torch.tensor(img);
357 object results = null;
358 try
359 {
360 results = Module.forward(tensor);
361 }
362 catch (Exception e)
363 {
364 Console.WriteLine(e.ToString());
365 }
366 TorchSharp.Utils.TensorAccessor<float> outputmask = ((torch.Tensor)results).data<float>();
367 double mean = outputmask.Mean();
368 resDepth = (int)((torch.Tensor)results).shape[1];
369 for (int c = 0; c < resDepth; c++)
370 {
371 Bitmap bmp = new Bitmap(Width, Height, b.Buffers[0].PixelFormat);
372 for (int y = 0; y < h; y++)
373 {
374 for (int x = 0; x < w; x++)
375 {
376 int index = (y * w * c) + x;
377 bmp.SetPixel(x, y, new ColorS((ushort)(outputmask[index] / mean)));
378 }
379 }
380 bb.Buffers.Add(ResizeBilinear(bmp, b.SizeX, b.SizeY));
381 bb.Buffers[bb.Buffers.Count - 1].RotateFlip(AForge.RotateFlipType.Rotate90FlipX);
382 }
383 r++;
384
385 }
386 }
387 else if (shape.Length == 5)
388 {
389 torch.Tensor tensor = torch.empty(shape);
390 for (int i = 0; i < b.Buffers.Count; i++)
391 {
392 Bitmap bm = ResizeBilinear(b.Buffers[i], Width, Height);
393 for (int y = 0; y < Height; y++)
394 {
395 for (int x = 0; x < Width; x++)
396 {
397 for (int c = 0; c < Depth; c++)
398 {
399 if (c >= bm.RGBChannelsCount)
400 {
401 tensor[0, c, i, x, y] = 0;
402 }
403 else
404 {
405 if (bm.BitsPerPixel > 8)
406 tensor[0, c, i, x, y] = ((float)bm.GetValue(x, y, c) / (float)ushort.MaxValue) * MaxValue;
407 else
408 tensor[0, c, i, x, y] = ((float)bm.GetValue(x, y, c) / (float)byte.MaxValue) * MaxValue;
409 }
410 }
411 }
412 }
413 }
414 torch.Tensor outputmask = (torch.Tensor)Module.forward(tensor);
415 resDepth = (int)(outputmask.shape[1]);
416 for (int c = 0; c < resDepth; c++)
417 {
418 for (int buf = 0; buf < (int)outputmask.shape[2]; buf++)
419 {
420 Bitmap bmp = new Bitmap((int)outputmask.shape[3], (int)outputmask.shape[4], b.Buffers[0].PixelFormat);
421 for (int y = 0; y < Height; y++)
422 {
423 for (int x = 0; x < Width; x++)
424 {
425 float by = outputmask[0, c, buf, x, y].ToSingle();
426 bmp.SetPixel(x, y, new ColorS((ushort)(by * byte.MaxValue)));
427 }
428 }
429 bb.Buffers.Add(ResizeBilinear(bmp, b.SizeX, b.SizeY));
430 bb.Buffers[bb.Buffers.Count - 1].RotateFlip(AForge.RotateFlipType.Rotate90FlipX);
431 }
432 }
433 }
434 bb.Channels.AddRange(b.Channels);
435 if (resDepth > 1)
436 {
437 bb.Channels.AddRange(b.Channels);
438 for (int i = 0; i < bb.Channels.Count; i++)
439 {
440 bb.Channels[i].Index = i;
441 }
442 }
443 bb.UpdateCoords(b.SizeZ, b.SizeC * resDepth, b.SizeT);
444 bb.Resolutions.Add(new Resolution(bb.SizeX, bb.SizeY, bb.Buffers[0].PixelFormat, b.PhysicalSizeX, b.PhysicalSizeY, b.PhysicalSizeZ, b.StageSizeX, b.StageSizeY, b.StageSizeZ));
445 bb.Volume = b.Volume;
446 BioImage.AutoThreshold(bb, true);
447 Images.AddImage(bb);
448 App.tabsView.AddTab(bb);
449 }
450 else
451 throw new NotImplementedException();
452 }
453 }
454 public static List<Model> Models = new List<Model>();
455 public static void Load(string mod)
456 {
457 Models.Add(new Model(mod));
458 }
459
460 public static void Run(string modelsName, BioImage image)
461 {
462 string st = System.IO.Path.GetDirectoryName(Environment.ProcessPath) + "/Models/" + modelsName;
463 bool loaded = false;
464 foreach (var m in Models)
465 {
466 if (m.Name == Path.GetFileNameWithoutExtension(modelsName))
467 {
468 loaded = true;
469 break;
470 }
471 }
472 if (!loaded)
473 {
474 Models.Add(new Model(st));
475 }
476 foreach (var m in Models)
477 {
478 if (m.Name == Path.GetFileNameWithoutExtension(modelsName))
479 {
480 m.Run(image);
481 }
482 }
483 }
484 public static Bitmap ResizeBilinear(Bitmap original, int targetWidth, int targetHeight)
485 {
486 // Create a new Pixbuf with target dimensions
487 Bitmap resized = new Bitmap(targetWidth, targetHeight, original.PixelFormat);
488
489 int originalWidth = original.Width;
490 int originalHeight = original.Height;
491 float xRatio = originalWidth / (float)targetWidth;
492 float yRatio = originalHeight / (float)targetHeight;
493
494 // Lock the bits of the original and resized images (if necessary)
495
496 for (int y = 0; y < targetHeight; y++)
497 {
498 for (int x = 0; x < targetWidth; x++)
499 {
500 float gx = ((float)x) * xRatio;
501 float gy = ((float)y) * yRatio;
502 int gxi = (int)gx;
503 int gyi = (int)gy;
504
505 ColorS topLeft = original.GetPixel(gxi, gyi);
506 ColorS topRight = original.GetPixel(gxi + 1, gyi);
507 ColorS bottomLeft = original.GetPixel(gxi, gyi + 1);
508 ColorS bottomRight = original.GetPixel(gxi + 1, gyi + 1);
509
510 ushort red = (ushort)BilinearInterpolate(gx - gxi, gy - gyi, topLeft.R, topRight.R, bottomLeft.R, bottomRight.R);
511 ushort green = (ushort)BilinearInterpolate(gx - gxi, gy - gyi, topLeft.G, topRight.G, bottomLeft.G, bottomRight.G);
512 ushort blue = (ushort)BilinearInterpolate(gx - gxi, gy - gyi, topLeft.B, topRight.B, bottomLeft.B, bottomRight.B);
513
514 resized.SetPixel(x, y, new ColorS(red, green, blue));
515 }
516 }
517
518 // Unlock the bits (if necessary)
519
520 return resized;
521 }
522 private static float BilinearInterpolate(float x, float y, float topLeft, float topRight, float bottomLeft, float bottomRight)
523 {
524 float top = Interpolate(x, topLeft, topRight);
525 float bottom = Interpolate(x, bottomLeft, bottomRight);
526 return Interpolate(y, top, bottom);
527 }
528
529 private static float Interpolate(float t, float a, float b)
530 {
531 return a + (b - a) * t;
532 }
533
534 // Implement GetPixel and SetPixel methods based on how Gdk.Pixbuf manages pixel data
535
536 public static void Initialize()
537 {
538 string st = System.IO.Path.GetDirectoryName(Environment.ProcessPath);
539 Directory.CreateDirectory(st + "/Models");
540 foreach (string f in Directory.GetFiles(st + "/Models"))
541 {
542 if (f.EndsWith(".onnx") || f.EndsWith(".pt"))
543 {
544 string path = "Run/" + Path.GetFileName(f);
545 App.AddMenu(path);
546 }
547 }
548 }
549 }
550}
AForge.Bitmap Bitmap
BioLib.BioImage BioImage
Definition TabsView.cs:15
static void AddMenu(string menu, Function f)
Definition App.cs:390
static TabsView tabsView
Definition App.cs:16
void RunONNX(BioImage b)
Definition ML.cs:237
torch.nn.Module InputModule
Definition ML.cs:117
InferenceSession InferenceSession
Definition ML.cs:115
Model(string file)
Definition ML.cs:22
ModelOutputType ModelType
Definition ML.cs:224
Dictionary< string, object > Metadata
Definition ML.cs:114
void RunTorch(BioImage b)
Definition ML.cs:319
torch.jit.ScriptModule Module
Definition ML.cs:116
void Run(BioImage b)
Definition ML.cs:230
torch.nn.Module OutputModule
Definition ML.cs:118
static List< Model > Models
Definition ML.cs:454
static void Initialize()
Definition ML.cs:536
static void Load(string mod)
Definition ML.cs:455
static Bitmap ResizeBilinear(Bitmap original, int targetWidth, int targetHeight)
Definition ML.cs:484
static void Run(string modelsName, BioImage image)
Definition ML.cs:460
void AddTab(BioImage b)
Definition TabsView.cs:134