diff --git a/Tests/ConnectionTest/Client/Client.csproj b/Tests/ConnectionTest/Client/Client.csproj index f18c94b..fd7fff8 100644 --- a/Tests/ConnectionTest/Client/Client.csproj +++ b/Tests/ConnectionTest/Client/Client.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable diff --git a/Tests/ConnectionTest/Server/Server.csproj b/Tests/ConnectionTest/Server/Server.csproj index 9c71c66..5470b3f 100644 --- a/Tests/ConnectionTest/Server/Server.csproj +++ b/Tests/ConnectionTest/Server/Server.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 AnyCPU;x64;x86 diff --git a/Tests/FileTransferTest/FileTransferClient/FileTransferClient.csproj b/Tests/FileTransferTest/FileTransferClient/FileTransferClient.csproj index 51f959c..e1b7800 100644 --- a/Tests/FileTransferTest/FileTransferClient/FileTransferClient.csproj +++ b/Tests/FileTransferTest/FileTransferClient/FileTransferClient.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net8.0-windows7.0 enable true AnyCPU;x64 diff --git a/Tests/FileTransferTest/FileTransferServer/FileTransferServer.csproj b/Tests/FileTransferTest/FileTransferServer/FileTransferServer.csproj index 51f959c..e1b7800 100644 --- a/Tests/FileTransferTest/FileTransferServer/FileTransferServer.csproj +++ b/Tests/FileTransferTest/FileTransferServer/FileTransferServer.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net8.0-windows7.0 enable true AnyCPU;x64 diff --git a/Tests/HNSWDemo/HNSWDemo.csproj b/Tests/HNSWDemo/HNSWDemo.csproj deleted file mode 100644 index 46fe152..0000000 --- a/Tests/HNSWDemo/HNSWDemo.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exe - net6.0 - AnyCPU;x64 - - - - - - - - - - - - - Always - - - - diff --git a/Tests/HNSWDemo/Model/Gender.cs b/Tests/HNSWDemo/Model/Gender.cs deleted file mode 100644 index 518e5a3..0000000 --- a/Tests/HNSWDemo/Model/Gender.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace HNSWDemo.Model -{ - public enum Gender - { - Unknown, Male, Feemale - } -} diff --git a/Tests/HNSWDemo/Model/Person.cs b/Tests/HNSWDemo/Model/Person.cs deleted file mode 100644 index bcd031c..0000000 --- a/Tests/HNSWDemo/Model/Person.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Model -{ - public class Person - { - public Gender Gender { get; set; } - public int Age { get; set; } - public long Number { get; set; } - - private static (float[], Person) Generate(int vector_size) - { - var rnd = new Random((int)Environment.TickCount); - var vector = new float[vector_size]; - DefaultRandomGenerator.Instance.NextFloats(vector); - VectorUtils.NormalizeSIMD(vector); - var p = new Person(); - p.Age = rnd.Next(15, 80); - var gr = rnd.Next(0, 3); - p.Gender = (gr == 0) ? Gender.Male : (gr == 1) ? Gender.Feemale : Gender.Unknown; - p.Number = CreateNumber(rnd); - return (vector, p); - } - - public static List<(float[], Person)> GenerateRandom(int vectorSize, int vectorsCount) - { - var vectors = new List<(float[], Person)>(); - for (int i = 0; i < vectorsCount; i++) - { - vectors.Add(Generate(vectorSize)); - } - return vectors; - } - - static HashSet _exists = new HashSet(); - private static long CreateNumber(Random rnd) - { - long start_number; - do - { - start_number = 79600000000L; - start_number = start_number + rnd.Next(4, 8) * 10000000; - start_number += rnd.Next(0, 1000000); - } - while (_exists.Add(start_number) == false); - return start_number; - } - } -} diff --git a/Tests/HNSWDemo/Program.cs b/Tests/HNSWDemo/Program.cs deleted file mode 100644 index 6ede5ff..0000000 --- a/Tests/HNSWDemo/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using HNSWDemo.Tests; -using System; -using System.IO; -using ZeroLevel.HNSW; - -namespace HNSWDemo -{ - class Program - { - static void Main(string[] args) - { - //new QuantizatorTest().Run(); - //new AutoClusteringMNISTTest().Run(); - new AccuracityTest().Run(); - Console.WriteLine("Completed"); - Console.ReadKey(); - } - - static int GetC(string file) - { - var name = Path.GetFileNameWithoutExtension(file); - var index = name.IndexOf("_M"); - if (index > 0) - { - index = name.IndexOf("_", index + 2); - if (index > 0) - { - var num = name.Substring(index + 1, name.Length - index - 1); - return int.Parse(num); - } - } - return -1; - } - } -} diff --git a/Tests/HNSWDemo/Properties/launchSettings.json b/Tests/HNSWDemo/Properties/launchSettings.json deleted file mode 100644 index 0246ed6..0000000 --- a/Tests/HNSWDemo/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "HNSWDemo": { - "commandName": "Project", - "hotReloadEnabled": false, - "nativeDebugging": false - } - } -} \ No newline at end of file diff --git a/Tests/HNSWDemo/Tests/AccuracityTest.cs b/Tests/HNSWDemo/Tests/AccuracityTest.cs deleted file mode 100644 index e04ec64..0000000 --- a/Tests/HNSWDemo/Tests/AccuracityTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using HNSWDemo.Utils; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class AccuracityTest - : ITest - { - private static int K = 200; - private static int count = 10000; - private static int testCount = 500; - private static int dimensionality = 128; - - public void Run() - { - var totalHits = new List(); - var timewatchesNP = new List(); - var timewatchesHNSW = new List(); - - var samples = VectorUtils.RandomVectors(dimensionality, count); - - var sw = new Stopwatch(); - - var test = new VectorsDirectCompare(samples, Metrics.CosineDistance); - var world = new SmallWorld(NSWOptions.Create(8, 16, 100, 100, (a, b) => (float)Metrics.DotProductDistance(a, b))); - - sw.Start(); - var ids = world.AddItems(samples.ToArray()); - sw.Stop(); - - Console.WriteLine($"Insert {ids.Length} items: {sw.ElapsedMilliseconds} ms"); - Console.WriteLine("Start test"); - - var test_vectors = VectorUtils.RandomVectors(dimensionality, testCount); - foreach (var v in test_vectors) - { - sw.Restart(); - var gt = test.KNearest(v, K).ToDictionary(p => p.Item1, p => p.Item2); - sw.Stop(); - timewatchesNP.Add(sw.ElapsedMilliseconds); - - sw.Restart(); - var result = world.Search(v, K); - sw.Stop(); - - timewatchesHNSW.Add(sw.ElapsedMilliseconds); - var hits = 0; - foreach (var r in result) - { - if (gt.ContainsKey(r.Item1)) - { - hits++; - } - } - totalHits.Add(hits); - } - - Console.WriteLine($"MIN Accuracity: {totalHits.Min() * 100 / K}%"); - Console.WriteLine($"AVG Accuracity: {totalHits.Average() * 100 / K}%"); - Console.WriteLine($"MAX Accuracity: {totalHits.Max() * 100 / K}%"); - - Console.WriteLine($"MIN HNSW TIME: {timewatchesHNSW.Min()} ms"); - Console.WriteLine($"AVG HNSW TIME: {timewatchesHNSW.Average()} ms"); - Console.WriteLine($"MAX HNSW TIME: {timewatchesHNSW.Max()} ms"); - - Console.WriteLine($"MIN NP TIME: {timewatchesNP.Min()} ms"); - Console.WriteLine($"AVG NP TIME: {timewatchesNP.Average()} ms"); - Console.WriteLine($"MAX NP TIME: {timewatchesNP.Max()} ms"); - } - } -} diff --git a/Tests/HNSWDemo/Tests/AutoClusteringMNISTTest.cs b/Tests/HNSWDemo/Tests/AutoClusteringMNISTTest.cs deleted file mode 100644 index f8b470a..0000000 --- a/Tests/HNSWDemo/Tests/AutoClusteringMNISTTest.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.FileSystem; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class AutoClusteringMNISTTest - : ITest - { - private static int Width = 3000; - private static int Height = 3000; - - private static byte[] PadLines(byte[] bytes, int rows, int columns) - { - int currentStride = columns; // 3 - int newStride = columns; // 4 - byte[] newBytes = new byte[newStride * rows]; - for (int i = 0; i < rows; i++) - Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride); - return newBytes; - } - - public void Run() - { - var folder = @"D:\Mnist"; - int columns = 28; - int rows = 28; - int imageCount, rowCount, colCount; - var buf = new byte[4]; - var image = new byte[rows * columns]; - var vectors = new List(); - using (var fs = new FileStream("t10k-images.idx3-ubyte", FileMode.Open, FileAccess.Read, FileShare.None)) - { - // first 4 bytes is a magic number - fs.Read(buf, 0, 4); - // second 4 bytes is the number of images - fs.Read(buf, 0, 4); - imageCount = BitConverter.ToInt32(buf.Reverse().ToArray(), 0); - // third 4 bytes is the row count - fs.Read(buf, 0, 4); - rowCount = BitConverter.ToInt32(buf.Reverse().ToArray(), 0); - // fourth 4 bytes is the column count - fs.Read(buf, 0, 4); - colCount = BitConverter.ToInt32(buf.Reverse().ToArray(), 0); - - for (int i = 0; i < imageCount; i++) - { - fs.Read(image, 0, image.Length); - var v = new byte[image.Length]; - Array.Copy(image, v, image.Length); - vectors.Add(v); - } - } - var options = NSWOptions.Create(8, 16, 200, 200, Metrics.L2EuclideanDistance); - SmallWorld world; - if (File.Exists("graph_mnist.bin")) - { - using (var fs = new FileStream("graph_mnist.bin", FileMode.Open, FileAccess.Read, FileShare.None)) - { - world = SmallWorld.CreateWorldFrom(options, fs); - } - } - else - { - world = SmallWorld.CreateWorld(options); - world.AddItems(vectors); - using (var fs = new FileStream("graph_mnist.bin", FileMode.Create, FileAccess.Write, FileShare.None)) - { - world.Serialize(fs); - } - } - - var distance = new Func((id1, id2) => Metrics.L2EuclideanDistance(world.GetVector(id1), world.GetVector(id2))); - var links = world.GetLinks().SelectMany(pair => pair.Value.Select(p=> distance(pair.Key, p))).ToList(); - var exists = links.Where(n => n > 0).ToArray(); - - var histogram = new Histogram(HistogramMode.LOG, links); - DrawHistogram(histogram, @"D:\histogram.jpg"); - - var clusters = AutomaticGraphClusterer.DetectClusters(world); - Console.WriteLine($"Found {clusters.Count} clusters"); - - while (clusters.Count > 10) - { - var last = clusters[clusters.Count - 1]; - var testDistance = clusters[0].MinDistance(distance, last); - var index = 0; - for (int i = 1; i < clusters.Count - 1; i++) - { - var d = clusters[i].MinDistance(distance, last); - if (d < testDistance) - { - testDistance = d; - index = i; - } - } - clusters[index].Merge(last); - clusters.RemoveAt(clusters.Count - 1); - } - - for (int i = 0; i < clusters.Count; i++) - { - var ouput = Path.Combine(folder, i.ToString("D3")); - FSUtils.CleanAndTestFolder(ouput); - foreach (var v in clusters[i]) - { - int stride = columns; - byte[] newbytes = PadLines(world.GetVector(v), rows, columns); - using (var im = new Bitmap(columns, rows, stride, PixelFormat.Format8bppIndexed, Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0))) - { - im.Save(Path.Combine(ouput, $"{v}.bmp")); - } - } - Console.WriteLine($"Cluster {i + 1} countains {clusters[i].Count} items"); - } - } - - static void DrawHistogram(Histogram histogram, string filename) - { - var wb = Width / histogram.Values.Length; - var k = ((float)Height) / (float)histogram.Values.Max(); - - var maxes = histogram.GetMaximums().ToDictionary(m => m.Index, m => m); - int threshold = histogram.CuttOff(); - - using (var bmp = new Bitmap(Width, Height)) - { - using (var g = Graphics.FromImage(bmp)) - { - for (int i = 0; i < histogram.Values.Length; i++) - { - var height = (int)(histogram.Values[i] * k); - if (maxes.ContainsKey(i)) - { - g.DrawRectangle(Pens.Red, i * wb, bmp.Height - height, wb, height); - g.DrawRectangle(Pens.Red, i * wb + 1, bmp.Height - height, wb - 1, height); - } - else - { - g.DrawRectangle(Pens.Blue, i * wb, bmp.Height - height, wb, height); - } - if (i == threshold) - { - g.DrawLine(Pens.Green, i * wb + wb / 2, 0, i * wb + wb / 2, bmp.Height); - } - } - } - bmp.Save(filename); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/AutoClusteringTest.cs b/Tests/HNSWDemo/Tests/AutoClusteringTest.cs deleted file mode 100644 index 2e77c21..0000000 --- a/Tests/HNSWDemo/Tests/AutoClusteringTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class AutoClusteringTest - : ITest - { - private static int Count = 3000; - private static int Dimensionality = 128; - - public void Run() - { - var vectors = VectorUtils.RandomVectors(Dimensionality, Count); - var world = SmallWorld.CreateWorld(NSWOptions.Create(8, 16, 200, 200, Metrics.L2EuclideanDistance)); - world.AddItems(vectors); - var clusters = AutomaticGraphClusterer.DetectClusters(world); - Console.WriteLine($"Found {clusters.Count} clusters"); - for (int i = 0; i < clusters.Count; i++) - { - Console.WriteLine($"Cluster {i + 1} countains {clusters[i].Count} items"); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/FilterTest.cs b/Tests/HNSWDemo/Tests/FilterTest.cs deleted file mode 100644 index d69cd8f..0000000 --- a/Tests/HNSWDemo/Tests/FilterTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -using HNSWDemo.Model; -using System; -using System.Linq; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Tests -{ - public class FilterTest - : ITest - { - private const int count = 3000; - private const int testCount = 100; - private const int dimensionality = 128; - - public void Run() - { - var map = new HNSWMap(); - var samples = Person.GenerateRandom(dimensionality, count); - var testDict = samples.ToDictionary(s => s.Item2.Number, s => s.Item2); - var world = new SmallWorld(NSWOptions.Create(6, 15, 200, 200, CosineDistance.ForUnits)); - var ids = world.AddItems(samples.Select(i => i.Item1).ToArray()); - for (int bi = 0; bi < samples.Count; bi++) - { - map.Append(samples[bi].Item2.Number, ids[bi]); - } - Console.WriteLine("Start test"); - int K = 200; - var vectors = VectorUtils.RandomVectors(dimensionality, testCount); - - var context = new SearchContext() - .SetActiveNodes(map - .ConvertFeaturesToIds(samples - .Where(p => p.Item2.Age > 20 && p.Item2.Age < 50 && p.Item2.Gender == Gender.Feemale) - .Select(p => p.Item2.Number))); - var hits = 0; - var miss = 0; - foreach (var v in vectors) - { - var numbers = map.ConvertIdsToFeatures(world.Search(v, K, context).Select(r => r.Item1)); - foreach (var r in numbers) - { - var record = testDict[r]; - if (context.NodeCheckMode == Mode.None || (record.Gender == Gender.Feemale && record.Age > 20 && record.Age < 50)) - { - hits++; - } - else - { - miss++; - } - } - } - Console.WriteLine($"SUCCESS: {hits}"); - Console.WriteLine($"ERROR: {miss}"); - } - } -} diff --git a/Tests/HNSWDemo/Tests/HistogramTest.cs b/Tests/HNSWDemo/Tests/HistogramTest.cs deleted file mode 100644 index e13375d..0000000 --- a/Tests/HNSWDemo/Tests/HistogramTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Drawing; -using System.IO; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class HistogramTest - : ITest - { - private static int Count = 3000; - private static int Dimensionality = 128; - private static int Width = 2440; - private static int Height = 1920; - - public void Run() - { - Create(Dimensionality, @"D:\hist"); - // Process.Start("explorer", $"D:\\hist{Dimensionality.ToString("D3")}.jpg"); - - /* for (int i = 12; i < 512; i++) - { - Create(i, @"D:\hist"); - }*/ - } - - private void Create(int dim, string output) - { - var vectors = VectorUtils.RandomVectors(dim, Count); - var world = SmallWorld.CreateWorld(NSWOptions.Create(8, 16, 200, 200, Metrics.L2EuclideanDistance)); - world.AddItems(vectors); - - var distance = new Func((id1, id2) => Metrics.L2EuclideanDistance(world.GetVector(id1), world.GetVector(id2))); - var weights = world.GetLinks().SelectMany(pair => pair.Value.Select(id => distance(pair.Key, id))); - var histogram = new Histogram(HistogramMode.SQRT, weights); - histogram.Smooth(); - - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - DrawHistogram(histogram, Path.Combine(output, $"hist{dim.ToString("D3")}.jpg")); - } - - static void DrawHistogram(Histogram histogram, string filename) - { - var wb = Width / histogram.Values.Length; - var k = ((float)Height) / (float)histogram.Values.Max(); - - var maxes = histogram.GetMaximums().ToDictionary(m => m.Index, m => m); - int threshold = histogram.CuttOff(); - - using (var bmp = new Bitmap(Width, Height)) - { - using (var g = Graphics.FromImage(bmp)) - { - for (int i = 0; i < histogram.Values.Length; i++) - { - var height = (int)(histogram.Values[i] * k); - if (maxes.ContainsKey(i)) - { - g.DrawRectangle(Pens.Red, i * wb, bmp.Height - height, wb, height); - g.DrawRectangle(Pens.Red, i * wb + 1, bmp.Height - height, wb - 1, height); - } - else - { - g.DrawRectangle(Pens.Blue, i * wb, bmp.Height - height, wb, height); - } - if (i == threshold) - { - g.DrawLine(Pens.Green, i * wb + wb / 2, 0, i * wb + wb / 2, bmp.Height); - } - } - } - bmp.Save(filename); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/ITest.cs b/Tests/HNSWDemo/Tests/ITest.cs deleted file mode 100644 index 6ee3d4c..0000000 --- a/Tests/HNSWDemo/Tests/ITest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace HNSWDemo.Tests -{ - public interface ITest - { - void Run(); - } -} diff --git a/Tests/HNSWDemo/Tests/InsertTimeExplosionTest.cs b/Tests/HNSWDemo/Tests/InsertTimeExplosionTest.cs deleted file mode 100644 index d4c7513..0000000 --- a/Tests/HNSWDemo/Tests/InsertTimeExplosionTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Diagnostics; -using ZeroLevel.HNSW; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class InsertTimeExplosionTest - : ITest - { - private static int Count = 10000; - private static int IterationCount = 100; - private static int Dimensionality = 128; - - public void Run() - { - var sw = new Stopwatch(); - var world = new SmallWorld(NSWOptions.Create(6, 12, 100, 100, Metrics.CosineDistance)); - for (int i = 0; i < IterationCount; i++) - { - var samples = VectorUtils.RandomVectors(Dimensionality, Count); - sw.Restart(); - var ids = world.AddItems(samples.ToArray()); - sw.Stop(); - Console.WriteLine($"ITERATION: [{i.ToString("D4")}] COUNT: [{ids.Length}] ELAPSED [{sw.ElapsedMilliseconds} ms]"); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/LALTest.cs b/Tests/HNSWDemo/Tests/LALTest.cs deleted file mode 100644 index dbeedcd..0000000 --- a/Tests/HNSWDemo/Tests/LALTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -using HNSWDemo.Model; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - internal class LALTest - : ITest - { - private const int count = 20000; - private const int dimensionality = 128; - private const string _graphFileCachee = @"lal_test_graph.bin"; - private const string _mapFileCachee = @"lal_test_map.bin"; - - public void Run() - { - var moda = 3; - var persons = Person.GenerateRandom(dimensionality, count); - var samples = new Dictionary>(); - var options = NSWOptions.Create(6, 8, 100, 100, Metrics.CosineDistance); - - foreach (var p in persons) - { - var c = (int)Math.Abs(p.Item2.Number.GetHashCode() % moda); - if (samples.ContainsKey(c) == false) samples.Add(c, new List<(float[], Person)>()); - samples[c].Add(p); - } - - SplittedLALGraph worlds; - HNSWMappers mappers; - - if (File.Exists(_graphFileCachee) && File.Exists(_mapFileCachee)) - { - worlds = new SplittedLALGraph(_graphFileCachee); - mappers = new HNSWMappers(_mapFileCachee, l => (int)Math.Abs(l.GetHashCode() % moda)); - } - else - { - - worlds = new SplittedLALGraph(); - mappers = new HNSWMappers(l => (int)Math.Abs(l.GetHashCode() % moda)); - - var worlds_dict = new Dictionary>(); - var maps_dict = new Dictionary>(); - - foreach (var p in samples) - { - var c = p.Key; - if (worlds_dict.ContainsKey(c) == false) - { - worlds_dict.Add(c, new SmallWorld(options)); - } - if (maps_dict.ContainsKey(c) == false) - { - maps_dict.Add(c, new HNSWMap()); - } - var w = worlds_dict[c]; - var m = maps_dict[c]; - var ids = w.AddItems(p.Value.Select(i => i.Item1)); - - for (int i = 0; i < ids.Length; i++) - { - m.Append(p.Value[i].Item2.Number, ids[i]); - } - } - - var name = Guid.NewGuid().ToString(); - foreach (var p in samples) - { - var c = p.Key; - var w = worlds_dict[c]; - var m = maps_dict[c]; - - using (var s = File.Create(name)) - { - w.Serialize(s); - } - using (var s = File.OpenRead(name)) - { - var l = LALGraph.FromHNSWGraph(s); - worlds.Append(l, c); - } - File.Delete(name); - mappers.Append(m, c); - } - - worlds.Save(_graphFileCachee); - mappers.Save(_mapFileCachee); - } - - var entries = new long[10]; - for (int i = 0; i < entries.Length; i++) - { - entries[i] = persons[DefaultRandomGenerator.Instance.Next(0, persons.Count - 1)].Item2.Number; - } - var contexts = mappers.CreateContext(null, entries); - var result = worlds.KNearest(5000, contexts); - - Console.WriteLine($"Found: {result.Sum(r=>r.Value.Count)}"); - - /*Console.WriteLine("Entries:"); - foreach (var n in entries) - { - Console.WriteLine($"\t{n}"); - } - - Console.WriteLine("Extensions:"); - foreach (var r in result) - { - foreach (var n in mappers.ConvertIdsToFeatures(r.Key, r.Value)) - { - Console.WriteLine($"\t[{n}]"); - } - }*/ - } - } -} diff --git a/Tests/HNSWDemo/Tests/QuantizatorTest.cs b/Tests/HNSWDemo/Tests/QuantizatorTest.cs deleted file mode 100644 index ffa01e8..0000000 --- a/Tests/HNSWDemo/Tests/QuantizatorTest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class QuantizatorTest - : ITest - { - private static int Count = 500000; - private static int Dimensionality = 221; - - public void Run() - { - var samples = VectorUtils.RandomVectors(Dimensionality, Count); - var min = samples.SelectMany(s => s).Min(); - var max = samples.SelectMany(s => s).Max(); - var q = new Quantizator(min, max); - var q_samples = samples.Select(s => q.QuantizeToInt(s)).ToArray(); - - // comparing - var list = new List(); - for (int i = 0; i < samples.Count - 1; i++) - { - var v1 = samples[i]; - var v2 = samples[i + 1]; - var dist = Metrics.CosineDistance(v1, v2); - - var qv1 = q_samples[i]; - var qv2 = q_samples[i + 1]; - var qdist = Metrics.CosineDistance(qv1, qv2); - - list.Add(Math.Abs(dist - qdist)); - } - - Console.WriteLine($"Min diff: {list.Min()}"); - Console.WriteLine($"Avg diff: {list.Average()}"); - Console.WriteLine($"Max diff: {list.Max()}"); - } - } -} diff --git a/Tests/HNSWDemo/Tests/QuantizeAccuracityTest.cs b/Tests/HNSWDemo/Tests/QuantizeAccuracityTest.cs deleted file mode 100644 index 7518257..0000000 --- a/Tests/HNSWDemo/Tests/QuantizeAccuracityTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -using HNSWDemo.Utils; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class QuantizeAccuracityTest - : ITest - { - private static int Count = 5000; - private static int Dimensionality = 128; - private static int K = 200; - private static int TestCount =500; - - public void Run() - { - var totalHits = new List(); - var timewatchesNP = new List(); - var timewatchesHNSW = new List(); - var q = new Quantizator(-1f, 1f); - - var s = VectorUtils.RandomVectors(Dimensionality, Count); - var samples = s.Select(v => q.QuantizeToLong(v)).ToList(); - - var sw = new Stopwatch(); - - var test = new VectorsDirectCompare(s, Metrics.CosineDistance); - var world = new SmallWorld(NSWOptions.Create(6, 8, 100, 100, Metrics.CosineDistance)); - - sw.Start(); - var ids = world.AddItems(samples.ToArray()); - sw.Stop(); - - Console.WriteLine($"Insert {ids.Length} items: {sw.ElapsedMilliseconds} ms"); - Console.WriteLine("Start test"); - - var tv = VectorUtils.RandomVectors(Dimensionality, TestCount); - var test_vectors = tv.Select(v => q.QuantizeToLong(v)).ToList(); - for (int i = 0; i < tv.Count; i++) - { - sw.Restart(); - var gt = test.KNearest(tv[i], K).ToDictionary(p => p.Item1, p => p.Item2); - sw.Stop(); - timewatchesNP.Add(sw.ElapsedMilliseconds); - - sw.Restart(); - var result = world.Search(test_vectors[i], K); - sw.Stop(); - - timewatchesHNSW.Add(sw.ElapsedMilliseconds); - var hits = 0; - foreach (var r in result) - { - if (gt.ContainsKey(r.Item1)) - { - hits++; - } - } - totalHits.Add(hits); - } - - Console.WriteLine($"MIN Accuracity: {totalHits.Min() * 100 / K}%"); - Console.WriteLine($"AVG Accuracity: {totalHits.Average() * 100 / K}%"); - Console.WriteLine($"MAX Accuracity: {totalHits.Max() * 100 / K}%"); - - Console.WriteLine($"MIN HNSW TIME: {timewatchesHNSW.Min()} ms"); - Console.WriteLine($"AVG HNSW TIME: {timewatchesHNSW.Average()} ms"); - Console.WriteLine($"MAX HNSW TIME: {timewatchesHNSW.Max()} ms"); - - Console.WriteLine($"MIN NP TIME: {timewatchesNP.Min()} ms"); - Console.WriteLine($"AVG NP TIME: {timewatchesNP.Average()} ms"); - Console.WriteLine($"MAX NP TIME: {timewatchesNP.Max()} ms"); - } - } -} diff --git a/Tests/HNSWDemo/Tests/QuantizeHistogramTest.cs b/Tests/HNSWDemo/Tests/QuantizeHistogramTest.cs deleted file mode 100644 index 53fa40e..0000000 --- a/Tests/HNSWDemo/Tests/QuantizeHistogramTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Drawing; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class QuantizeHistogramTest - : ITest - { - private static int Count = 3000; - private static int Dimensionality = 128; - private static int Width = 3000; - private static int Height = 3000; - - public void Run() - { - var vectors = VectorUtils.RandomVectors(Dimensionality, Count); - var q = new Quantizator(-1f, 1f); - var world = SmallWorld.CreateWorld(NSWOptions.Create(8, 16, 200, 200, Metrics.CosineDistance)); - world.AddItems(vectors.Select(v => q.QuantizeToLong(v)).ToList()); - - var distance = new Func((id1, id2) => Metrics.CosineDistance(world.GetVector(id1), world.GetVector(id2))); - var weights = world.GetLinks().SelectMany(pair => pair.Value.Select(id => distance(pair.Key, id))); - var histogram = new Histogram(HistogramMode.SQRT, weights); - histogram.Smooth(); - - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - DrawHistogram(histogram, @"D:\hist.jpg"); - } - - static void DrawHistogram(Histogram histogram, string filename) - { - var wb = Width / histogram.Values.Length; - var k = ((float)Height) / (float)histogram.Values.Max(); - - var maxes = histogram.GetMaximums().ToDictionary(m => m.Index, m => m); - int threshold = histogram.CuttOff(); - - using (var bmp = new Bitmap(Width, Height)) - { - using (var g = Graphics.FromImage(bmp)) - { - for (int i = 0; i < histogram.Values.Length; i++) - { - var height = (int)(histogram.Values[i] * k); - if (maxes.ContainsKey(i)) - { - g.DrawRectangle(Pens.Red, i * wb, bmp.Height - height, wb, height); - g.DrawRectangle(Pens.Red, i * wb + 1, bmp.Height - height, wb - 1, height); - } - else - { - g.DrawRectangle(Pens.Blue, i * wb, bmp.Height - height, wb, height); - } - if (i == threshold) - { - g.DrawLine(Pens.Green, i * wb + wb / 2, 0, i * wb + wb / 2, bmp.Height); - } - } - } - bmp.Save(filename); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/QuantizeInsertTimeExplosionTest.cs b/Tests/HNSWDemo/Tests/QuantizeInsertTimeExplosionTest.cs deleted file mode 100644 index a0c6679..0000000 --- a/Tests/HNSWDemo/Tests/QuantizeInsertTimeExplosionTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using ZeroLevel.HNSW; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Mathemathics; - -namespace HNSWDemo.Tests -{ - public class QuantizeInsertTimeExplosionTest - : ITest - { - private static int Count = 10000; - private static int IterationCount = 100; - private static int Dimensionality = 128; - - public void Run() - { - var sw = new Stopwatch(); - var world = new SmallWorld(NSWOptions.Create(6, 12, 100, 100, Metrics.CosineDistance)); - var q = new Quantizator(-1f, 1f); - for (int i = 0; i < IterationCount; i++) - { - var samples = VectorUtils.RandomVectors(Dimensionality, Count); - sw.Restart(); - var ids = world.AddItems(samples.Select(v => q.QuantizeToLong(v)).ToArray()); - sw.Stop(); - Console.WriteLine($"ITERATION: [{i.ToString("D4")}] COUNT: [{ids.Length}] ELAPSED [{sw.ElapsedMilliseconds} ms]"); - } - } - } -} diff --git a/Tests/HNSWDemo/Tests/SaveRestoreTest.cs b/Tests/HNSWDemo/Tests/SaveRestoreTest.cs deleted file mode 100644 index f135ea6..0000000 --- a/Tests/HNSWDemo/Tests/SaveRestoreTest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Tests -{ - public class SaveRestoreTest - : ITest - { - private static int Count = 1000; - private static int Dimensionality = 128; - - public void Run() - { - var samples = VectorUtils.RandomVectors(Dimensionality, Count); - var world = new SmallWorld(NSWOptions.Create(6, 15, 200, 200, CosineDistance.ForUnits)); - var sw = new Stopwatch(); - sw.Start(); - var ids = world.AddItems(samples.ToArray()); - sw.Stop(); - Console.WriteLine($"Insert {ids.Length} items on {sw.ElapsedMilliseconds} ms"); - Console.WriteLine("Start test"); - - byte[] dump; - using (var ms = new MemoryStream()) - { - world.Serialize(ms); - dump = ms.ToArray(); - } - Console.WriteLine($"Full dump size: {dump.Length} bytes"); - - byte[] testDump; - var restoredWorld = new SmallWorld(NSWOptions.Create(6, 15, 200, 200, CosineDistance.ForUnits)); - using (var ms = new MemoryStream(dump)) - { - restoredWorld.Deserialize(ms); - } - - using (var ms = new MemoryStream()) - { - restoredWorld.Serialize(ms); - testDump = ms.ToArray(); - } - if (testDump.Length != dump.Length) - { - Console.WriteLine($"Incorrect restored size. Got {testDump.Length}. Expected: {dump.Length}"); - return; - } - } - } -} diff --git a/Tests/HNSWDemo/Utils/QLVectorsDirectCompare.cs b/Tests/HNSWDemo/Utils/QLVectorsDirectCompare.cs deleted file mode 100644 index 10182b5..0000000 --- a/Tests/HNSWDemo/Utils/QLVectorsDirectCompare.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Utils -{ - public class QLVectorsDirectCompare - { - private const int HALF_LONG_BITS = 32; - private readonly IList _vectors; - private readonly Func _distance; - - public QLVectorsDirectCompare(List vectors, Func distance) - { - _vectors = vectors; - _distance = distance; - } - - public IEnumerable<(int, float)> KNearest(long[] v, int k) - { - var weights = new Dictionary(); - for (int i = 0; i < _vectors.Count; i++) - { - var d = _distance(v, _vectors[i]); - weights[i] = d; - } - return weights.OrderBy(p => p.Value).Take(k).Select(p => (p.Key, p.Value)); - } - - public List> DetectClusters() - { - var links = new SortedList(); - for (int i = 0; i < _vectors.Count; i++) - { - for (int j = i + 1; j < _vectors.Count; j++) - { - long k = (((long)(i)) << HALF_LONG_BITS) + j; - links.Add(k, _distance(_vectors[i], _vectors[j])); - } - } - - // 1. Find R - bound between intra-cluster distances and out-of-cluster distances - var histogram = new Histogram(HistogramMode.SQRT, links.Values); - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - // 2. Get links with distances less than R - var resultLinks = new SortedList(); - foreach (var pair in links) - { - if (pair.Value < R) - { - resultLinks.Add(pair.Key, pair.Value); - } - } - - // 3. Extract clusters - List> clusters = new List>(); - foreach (var pair in resultLinks) - { - var k = pair.Key; - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - - bool found = false; - foreach (var c in clusters) - { - if (c.Contains(id1)) - { - c.Add(id2); - found = true; - break; - } - else if (c.Contains(id2)) - { - c.Add(id1); - found = true; - break; - } - } - if (found == false) - { - var c = new HashSet(); - c.Add(id1); - c.Add(id2); - clusters.Add(c); - } - } - return clusters; - } - } -} diff --git a/Tests/HNSWDemo/Utils/QVectorsDirectCompare.cs b/Tests/HNSWDemo/Utils/QVectorsDirectCompare.cs deleted file mode 100644 index 1649ea9..0000000 --- a/Tests/HNSWDemo/Utils/QVectorsDirectCompare.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Utils -{ - public class QVectorsDirectCompare - { - private const int HALF_LONG_BITS = 32; - private readonly IList _vectors; - private readonly Func _distance; - - public QVectorsDirectCompare(List vectors, Func distance) - { - _vectors = vectors; - _distance = distance; - } - - public IEnumerable<(int, float)> KNearest(byte[] v, int k) - { - var weights = new Dictionary(); - for (int i = 0; i < _vectors.Count; i++) - { - var d = _distance(v, _vectors[i]); - weights[i] = d; - } - return weights.OrderBy(p => p.Value).Take(k).Select(p => (p.Key, p.Value)); - } - - public List> DetectClusters() - { - var links = new SortedList(); - for (int i = 0; i < _vectors.Count; i++) - { - for (int j = i + 1; j < _vectors.Count; j++) - { - long k = (((long)i) << HALF_LONG_BITS) + j; - links.Add(k, _distance(_vectors[i], _vectors[j])); - } - } - - // 1. Find R - bound between intra-cluster distances and out-of-cluster distances - var histogram = new Histogram(HistogramMode.SQRT, links.Values); - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - // 2. Get links with distances less than R - var resultLinks = new SortedList(); - foreach (var pair in links) - { - if (pair.Value < R) - { - resultLinks.Add(pair.Key, pair.Value); - } - } - - // 3. Extract clusters - List> clusters = new List>(); - foreach (var pair in resultLinks) - { - var k = pair.Key; - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - - bool found = false; - foreach (var c in clusters) - { - if (c.Contains(id1)) - { - c.Add(id2); - found = true; - break; - } - else if (c.Contains(id2)) - { - c.Add(id1); - found = true; - break; - } - } - if (found == false) - { - var c = new HashSet(); - c.Add(id1); - c.Add(id2); - clusters.Add(c); - } - } - return clusters; - } - } -} diff --git a/Tests/HNSWDemo/Utils/VectorsDirectCompare.cs b/Tests/HNSWDemo/Utils/VectorsDirectCompare.cs deleted file mode 100644 index 4d50c3f..0000000 --- a/Tests/HNSWDemo/Utils/VectorsDirectCompare.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.HNSW; - -namespace HNSWDemo.Utils -{ - public class VectorsDirectCompare - { - private const int HALF_LONG_BITS = 32; - private readonly IList _vectors; - private readonly Func _distance; - - public VectorsDirectCompare(List vectors, Func distance) - { - _vectors = vectors; - _distance = distance; - } - - public IEnumerable<(int, float)> KNearest(float[] v, int k) - { - var weights = new Dictionary(); - for (int i = 0; i < _vectors.Count; i++) - { - var d = _distance(v, _vectors[i]); - weights[i] = d; - } - return weights.OrderBy(p => p.Value).Take(k).Select(p => (p.Key, p.Value)); - } - - public List> DetectClusters() - { - var links = new SortedList(); - for (int i = 0; i < _vectors.Count; i++) - { - for (int j = i + 1; j < _vectors.Count; j++) - { - long k = (((long)(i)) << HALF_LONG_BITS) + j; - links.Add(k, _distance(_vectors[i], _vectors[j])); - } - } - - // 1. Find R - bound between intra-cluster distances and out-of-cluster distances - var histogram = new Histogram(HistogramMode.SQRT, links.Values); - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - // 2. Get links with distances less than R - var resultLinks = new SortedList(); - foreach (var pair in links) - { - if (pair.Value < R) - { - resultLinks.Add(pair.Key, pair.Value); - } - } - - // 3. Extract clusters - List> clusters = new List>(); - foreach (var pair in resultLinks) - { - var k = pair.Key; - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - - bool found = false; - foreach (var c in clusters) - { - if (c.Contains(id1)) - { - c.Add(id2); - found = true; - break; - } - else if (c.Contains(id2)) - { - c.Add(id1); - found = true; - break; - } - } - if (found == false) - { - var c = new HashSet(); - c.Add(id1); - c.Add(id2); - clusters.Add(c); - } - } - return clusters; - } - } -} diff --git a/Tests/HNSWDemo/t10k-images.idx3-ubyte b/Tests/HNSWDemo/t10k-images.idx3-ubyte deleted file mode 100644 index 1170b2c..0000000 Binary files a/Tests/HNSWDemo/t10k-images.idx3-ubyte and /dev/null differ diff --git a/Tests/PartitionFileStorageTest/PartitionFileStorageTest.csproj b/Tests/PartitionFileStorageTest/PartitionFileStorageTest.csproj index 3d7cb0b..2a0493b 100644 --- a/Tests/PartitionFileStorageTest/PartitionFileStorageTest.csproj +++ b/Tests/PartitionFileStorageTest/PartitionFileStorageTest.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable diff --git a/Tests/PartitionFileStorageTest/Program.cs b/Tests/PartitionFileStorageTest/Program.cs index 152483a..4d07f42 100644 --- a/Tests/PartitionFileStorageTest/Program.cs +++ b/Tests/PartitionFileStorageTest/Program.cs @@ -61,7 +61,7 @@ namespace PartitionFileStorageTest await storePart.Store(c3, Generate(r)); await storePart.Store(c3, Generate(r)); storePart.CompleteAdding(); - await storePart.Compress(); + storePart.Compress(); } using (var readPart = store.CreateAccessor(new Metadata { Date = new DateTime(2022, 11, 08) })) @@ -124,7 +124,7 @@ namespace PartitionFileStorageTest Log.Info($"Fill journal: {sw.ElapsedMilliseconds}ms. Records writed: {storePart.TotalRecords}"); sw.Restart(); storePart.CompleteAdding(); - await storePart.Compress(); + storePart.Compress(); sw.Stop(); Log.Info($"Compress: {sw.ElapsedMilliseconds}ms"); sw.Restart(); @@ -269,11 +269,11 @@ namespace PartitionFileStorageTest using (var storePart = store.CreateBuilder(meta)) { - Parallel.ForEach(MassGenerator((long)(0.7 * PAIRS_COUNT)), parallelOptions, pair => + await Parallel.ForEachAsync(MassGenerator((long)(0.7 * PAIRS_COUNT)), CancellationToken.None, async (pair, _) => { var key = pair.Item1; var val = pair.Item2; - storePart.Store(key, val); + await storePart.Store(key, val); if (key % 717 == 0) { testKeys1.Add(key); @@ -292,7 +292,7 @@ namespace PartitionFileStorageTest Log.Info($"Fill journal: {sw.ElapsedMilliseconds}ms"); sw.Restart(); storePart.CompleteAdding(); - await storePart.Compress(); + storePart.Compress(); sw.Stop(); Log.Info($"Compress: {sw.ElapsedMilliseconds}ms"); sw.Restart(); @@ -305,11 +305,11 @@ namespace PartitionFileStorageTest sw.Restart(); using (var merger = store.CreateMergeAccessor(meta, data => Compressor.DecodeBytesContent(data))) { - Parallel.ForEach(MassGenerator((long)(0.3 * PAIRS_COUNT)), parallelOptions, pair => + await Parallel.ForEachAsync(MassGenerator((long)(0.3 * PAIRS_COUNT)), CancellationToken.None, async (pair, _) => { var key = pair.Item1; var val = pair.Item2; - merger.Store(key, val); + await merger.Store(key, val); Keys.Add(key); }); @@ -610,9 +610,10 @@ namespace PartitionFileStorageTest FSUtils.CleanAndTestFolder(root); await FastTest(options); + FSUtils.CleanAndTestFolder(root); await FullStoreMultithreadTest(optionsMultiThread); - + /*FSUtils.CleanAndTestFolder(root); diff --git a/Tests/Qdrant.Test/Program.cs b/Tests/Qdrant.Test/Program.cs deleted file mode 100644 index 9b8878a..0000000 --- a/Tests/Qdrant.Test/Program.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Grpc.Net.Client; -using static Qdrant.Collections; - -namespace Qdrant.Test -{ - // QDRANT VERSION 1.15.1 - internal class Program - { - const string COLLECTION_NAME = "my_test_collection"; - static void Main(string[] args) - { - var address = @"http://localhost:6334"; - var channel = GrpcChannel.ForAddress(address); - var collections = new CollectionsClient(channel); - var response = collections.Create(new CreateCollection - { - CollectionName = COLLECTION_NAME, - VectorsConfig = new VectorsConfig - { - Params = new VectorParams - { - Distance = Distance.Dot, - Size = 32, - HnswConfig = new HnswConfigDiff - { - OnDisk = false - } - } - } - }); - - Console.WriteLine($"CREATED: {response.Result}"); - - var d_response = collections.Delete(new DeleteCollection - { - CollectionName = COLLECTION_NAME - }); - Console.WriteLine($"DELETED: {d_response.Result}"); - } - } -} \ No newline at end of file diff --git a/Tests/Qdrant.Test/Qdrant.Test.csproj b/Tests/Qdrant.Test/Qdrant.Test.csproj deleted file mode 100644 index d459a5e..0000000 --- a/Tests/Qdrant.Test/Qdrant.Test.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - - - - ..\..\ZeroLevel.Qdrant.GrpcClient\bin\Release\net6.0\ZeroLevel.Qdrant.GrpcClient.dll - - - - diff --git a/Tests/TestApp/TestApp.csproj b/Tests/TestApp/TestApp.csproj index 375d4ee..6e86b70 100644 --- a/Tests/TestApp/TestApp.csproj +++ b/Tests/TestApp/TestApp.csproj @@ -2,13 +2,16 @@ Exe - net6.0 + net8.0 AnyCPU;x64;x86 - + + + + diff --git a/Tests/TestPipeLine/Consumer/Consumer.csproj b/Tests/TestPipeLine/Consumer/Consumer.csproj index bfff132..d187edf 100644 --- a/Tests/TestPipeLine/Consumer/Consumer.csproj +++ b/Tests/TestPipeLine/Consumer/Consumer.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable AnyCPU;x64 diff --git a/Tests/TestPipeLine/Processor/Processor.csproj b/Tests/TestPipeLine/Processor/Processor.csproj index bfff132..d187edf 100644 --- a/Tests/TestPipeLine/Processor/Processor.csproj +++ b/Tests/TestPipeLine/Processor/Processor.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable AnyCPU;x64 diff --git a/Tests/TestPipeLine/Source/Source.csproj b/Tests/TestPipeLine/Source/Source.csproj index bfff132..d187edf 100644 --- a/Tests/TestPipeLine/Source/Source.csproj +++ b/Tests/TestPipeLine/Source/Source.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable AnyCPU;x64 diff --git a/Tests/TestPipeLine/Watcher/Watcher.csproj b/Tests/TestPipeLine/Watcher/Watcher.csproj index bfff132..d187edf 100644 --- a/Tests/TestPipeLine/Watcher/Watcher.csproj +++ b/Tests/TestPipeLine/Watcher/Watcher.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 enable enable AnyCPU;x64 diff --git a/Tests/ZeroLevel.UnitTests/PartitionStorageTests.cs b/Tests/ZeroLevel.UnitTests/PartitionStorageTests.cs index 6e8bdc5..e5a9e75 100644 --- a/Tests/ZeroLevel.UnitTests/PartitionStorageTests.cs +++ b/Tests/ZeroLevel.UnitTests/PartitionStorageTests.cs @@ -152,7 +152,7 @@ namespace ZeroLevel.UnitTests await storePart.Store(numbers[2], texts[7]); // 2 - 21 storePart.CompleteAdding(); - await storePart.Compress(); + storePart.Compress(); } // Assert diff --git a/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj b/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj index d0cfea3..e6651e9 100644 --- a/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj +++ b/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 AnyCPU;x64;x86 diff --git a/ZeroLevel.EventsServer/EventRepository.cs b/ZeroLevel.EventsServer/EventRepository.cs deleted file mode 100644 index afd30f8..0000000 --- a/ZeroLevel.EventsServer/EventRepository.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Threading; - -namespace ZeroLevel.EventServer -{ - public class EventRepository - :BaseSqLiteDB - { - private readonly SQLiteConnection _db; - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - private readonly string _tableName; - - public EventRepository() - { - _tableName = "events"; - - var path = PrepareDb($"{_tableName}.db"); - _db = new SQLiteConnection($"Data Source={path};Version=3;"); - _db.Open(); - - Execute($"CREATE TABLE IF NOT EXISTS {_tableName} (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT, body BLOB)", _db); - Execute($"CREATE INDEX IF NOT EXISTS key_index ON {_tableName} (key)", _db); - } - } -} diff --git a/ZeroLevel.EventsServer/EventService.cs b/ZeroLevel.EventsServer/EventService.cs deleted file mode 100644 index 8741acf..0000000 --- a/ZeroLevel.EventsServer/EventService.cs +++ /dev/null @@ -1,62 +0,0 @@ -using ZeroLevel.EventServer.Model; -using ZeroLevel.Network; -using ZeroLevel.Services.Applications; - -namespace ZeroLevel.EventServer -{ - public class EventService - : BaseZeroService - { - public EventService() - { - } - - protected override void StartAction() - { - var host = UseHost(); - this.AutoregisterInboxes(host); - host.OnConnect += Host_OnConnect; - host.OnDisconnect += Host_OnDisconnect; - } - - private void Host_OnDisconnect(ISocketClient obj) - { - Log.Info($"Client '{obj.Endpoint.Address}:{obj.Endpoint.Port}' disconnected"); - } - - private void Host_OnConnect(IClient obj) - { - Log.Info($"Client '{obj.Socket.Endpoint.Address}:{obj.Socket.Endpoint.Port}' connected"); - } - - protected override void StopAction() - { - } - - #region Inboxes - [ExchangeReplier("onetime")] - public long OneTimeHandler(ISocketClient client, OneTimeEvent e) - { - return 0; - } - - [ExchangeReplier("periodic")] - public long PeriodicHandler(ISocketClient client, PeriodicTimeEvent e) - { - return 0; - } - - [ExchangeReplier("eventtrigger")] - public long AfterEventHandler(ISocketClient client, EventAfterEvent e) - { - return 0; - } - - [ExchangeReplier("eventstrigger")] - public long AfterEventsHandler(ISocketClient client, EventAfterEvents e) - { - return 0; - } - #endregion - } -} diff --git a/ZeroLevel.EventsServer/Model/BaseEvent.cs b/ZeroLevel.EventsServer/Model/BaseEvent.cs deleted file mode 100644 index 1ba11bd..0000000 --- a/ZeroLevel.EventsServer/Model/BaseEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public abstract class BaseEvent - { - public string ServiceKey { get; set; } - public string Inbox { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Model/Condition.cs b/ZeroLevel.EventsServer/Model/Condition.cs deleted file mode 100644 index 0a7f365..0000000 --- a/ZeroLevel.EventsServer/Model/Condition.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public enum Condition - : int - { - /// - /// В любом случае - /// - None = 0, - /// - /// Если хотя бы одно событие успешно обработано - /// - OneSuccessfull = 1, - /// - /// Если обработаны все события - /// - AllSuccessfull = 2, - /// - /// Если хотя бы одно событие не обработано - /// - AnyFault = 3 - } -} diff --git a/ZeroLevel.EventsServer/Model/EventAfterEvent.cs b/ZeroLevel.EventsServer/Model/EventAfterEvent.cs deleted file mode 100644 index 3df207f..0000000 --- a/ZeroLevel.EventsServer/Model/EventAfterEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public class EventAfterEvent - : BaseEvent - { - public long EventId { get; set; } - - public Condition Confition { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Model/EventAfterEvents.cs b/ZeroLevel.EventsServer/Model/EventAfterEvents.cs deleted file mode 100644 index 9229d1a..0000000 --- a/ZeroLevel.EventsServer/Model/EventAfterEvents.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace ZeroLevel.EventServer.Model -{ - public class EventAfterEvents - : BaseEvent - { - public IEnumerable EventIds { get; set; } - - public Condition Confition { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Model/EventInfoRecord.cs b/ZeroLevel.EventsServer/Model/EventInfoRecord.cs deleted file mode 100644 index 207e339..0000000 --- a/ZeroLevel.EventsServer/Model/EventInfoRecord.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ZeroLevel.EventServer -{ - public class EventInfoRecord - { - public long EventId { get; set; } - - public string ServiceKey { get; set; } - // OR - public string ServiceEndpoint { get; set; } - - public string Inbox { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Model/EventResult.cs b/ZeroLevel.EventsServer/Model/EventResult.cs deleted file mode 100644 index 406a0c0..0000000 --- a/ZeroLevel.EventsServer/Model/EventResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public class EventResult - { - public long EventId; - public EventResultState State; - public long StartTimestamp; - public long EndTimestamp; - } -} diff --git a/ZeroLevel.EventsServer/Model/EventResultState.cs b/ZeroLevel.EventsServer/Model/EventResultState.cs deleted file mode 100644 index 7c418b5..0000000 --- a/ZeroLevel.EventsServer/Model/EventResultState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public enum EventResultState - { - InProgress, - Success, - Unsuccess - } -} diff --git a/ZeroLevel.EventsServer/Model/EventType.cs b/ZeroLevel.EventsServer/Model/EventType.cs deleted file mode 100644 index 85f2688..0000000 --- a/ZeroLevel.EventsServer/Model/EventType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ZeroLevel.EventServer.Model -{ - public enum EventType - : int - { - OneType = 0, - Periodic = 1, - EventTrigger = 2, - EventsTrigger = 3 - } -} diff --git a/ZeroLevel.EventsServer/Model/OneTimeEvent.cs b/ZeroLevel.EventsServer/Model/OneTimeEvent.cs deleted file mode 100644 index 590c6b5..0000000 --- a/ZeroLevel.EventsServer/Model/OneTimeEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace ZeroLevel.EventServer.Model -{ - public class OneTimeEvent - : BaseEvent - { - public TimeSpan Period { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Model/PeriodicTimeEvent.cs b/ZeroLevel.EventsServer/Model/PeriodicTimeEvent.cs deleted file mode 100644 index c165591..0000000 --- a/ZeroLevel.EventsServer/Model/PeriodicTimeEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace ZeroLevel.EventServer.Model -{ - public class PeriodicTimeEvent - : BaseEvent - { - public TimeSpan Period { get; set; } - } -} diff --git a/ZeroLevel.EventsServer/Program.cs b/ZeroLevel.EventsServer/Program.cs deleted file mode 100644 index 62d2c7c..0000000 --- a/ZeroLevel.EventsServer/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ZeroLevel.EventServer -{ - class Program - { - static void Main(string[] args) - { - Bootstrap.Startup(args, configuration: () => Configuration.ReadOrEmptySetFromIniFile("config.ini")) - .EnableConsoleLog() - .UseDiscovery() - .Run() - .WaitWhileStatus(ZeroServiceStatus.Running); - Bootstrap.Shutdown(); - } - } -} diff --git a/ZeroLevel.EventsServer/ZeroLevel.EventsServer.csproj b/ZeroLevel.EventsServer/ZeroLevel.EventsServer.csproj deleted file mode 100644 index c1cb915..0000000 --- a/ZeroLevel.EventsServer/ZeroLevel.EventsServer.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - net6.0 - AnyCPU;x64;x86 - - - - - - - diff --git a/ZeroLevel.HNSW/Model/Histogram.cs b/ZeroLevel.HNSW/Model/Histogram.cs deleted file mode 100644 index f89ec1a..0000000 --- a/ZeroLevel.HNSW/Model/Histogram.cs +++ /dev/null @@ -1,287 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ZeroLevel.HNSW -{ - public class HistogramValue - { - public int Index { get; internal set; } - public int Value { get; internal set; } - public float MinBound { get; internal set; } - public float MaxBound { get; internal set; } - } - - public class Histogram - { - public HistogramMode Mode { get; } - public float Min { get; } - public float Max { get; } - public float BoundsPeriod { get; } - public float[] Bounds { get; } - public int[] Values { get; } - - public Histogram(HistogramMode mode, IEnumerable data) - { - Mode = mode; - Min = data.Min(); - Max = data.Max(); - int count = data.Count(); - int M = mode == HistogramMode.LOG ? (int)(1f + 3.2f * Math.Log(count)) : (int)(Math.Sqrt(count)); - BoundsPeriod = (Max - Min) / M; - Bounds = new float[M - 1]; - - float bound = Min + BoundsPeriod; - for (int i = 0; i < Bounds.Length; i++) - { - Bounds[i] = bound; - bound += BoundsPeriod; - } - Values = new int[M]; - for (int i = 0; i < Values.Length; i++) - { - Values[i] = 0; - } - foreach (var v in data) - { - if (v < float.Epsilon) continue; - for (int i = 0; i < Bounds.Length; i++) - { - if (v < Bounds[i]) - { - Values[i]++; - break; - } - } - } - } - - public int Count => Values?.Length ?? 0; - - public int CountSignChanges() - { - if ((Values?.Length ?? 0) <= 2) return 0; - int i = 0; - while (Values[i] <= float.Epsilon) { i++; continue; } - if ((Values.Length - i) <= 2) return 0; - - var delta = Values[i + 1] - Values[i]; - int changes = 0; - i++; - for (; i < Values.Length - 1; i++) - { - var d = Values[i + 1] - Values[i]; - if (Math.Abs(d) <= float.Epsilon) - { - continue; - } - if (NumbersHasSameSign(d, delta) == false) - { - delta = d; - changes++; - } - } - return changes; - } - - public void Smooth() - { - var buffer = new int[Values.Length]; - Array.Copy(Values, buffer, buffer.Length); - for (int i = 2; i < Values.Length - 3; i++) - { - Values[i] = (buffer[i - 2] + buffer[i - 1] + buffer[i] + buffer[i + 1] + buffer[i + 2]) / 5; - } - } - - public IEnumerable GetMaximums() - { - var list = new List(); - - if ((Values?.Length ?? 0) <= 2) return list; - int i = 0; - while (Values[i] <= float.Epsilon) { i++; continue; } - if ((Values.Length - i) <= 2) return list; - - var delta = Values[i + 1] - Values[i]; - i++; - for (; i < Values.Length - 1; i++) - { - var d = Values[i + 1] - Values[i]; - if (Math.Abs(d) <= float.Epsilon) - { - continue; - } - if (NumbersHasSameSign(d, delta) == false) - { - if (delta > 0) - { - list.Add(new HistogramValue - { - Index = i, - Value = Values[i], - MinBound = Bounds[i - 1], - MaxBound = Bounds[i] - }); - } - delta = d; - } - } - return list; - } - - #region OTSU "https://en.wikipedia.org/wiki/Otsu's_method" - // function is used to compute the q values in the equation - private float Px(int init, int end) - { - int sum = 0; - int i; - for (i = init; i < end; i++) - sum += Values[i]; - return (float)sum; - } - // function is used to compute the mean values in the equation (mu) - private float Mx(int init, int end) - { - int sum = 0; - int i; - for (i = init; i < end; i++) - sum += i * Values[i]; - - return (float)sum; - } - /* - public int OTSU() - { - float p1, p2, p12; - int k; - int threshold = 0; - float bcv = 0; - for (k = 0; k < Values.Length; k++) - { - p1 = Px(0, k); - p2 = Px(k + 1, Values.Length); - p12 = p1 * p2; - if (p12 == 0) - p12 = 1; - float diff = (Mx(0, k) * p2) - (Mx(k + 1, Values.Length) * p1); - var test = (float)diff * diff / p12; - if (test > bcv) - { - bcv = test; - threshold = k; - } - } - return threshold; - } - */ - /* -1. Градиент V[I] - V[i-1] -2. Походы окнами от 1 и выше, пока не сойдется к бимодальности -3. Найти cutoff как минимум между пиками - -Modes = 0 -W = 1 -D = [V.count1] -Maxes = [] -For I in [1..V.count] - D= V[I] - V[i-1] -do - -Modes = 0 -S = +1 -do - for wnd in D - if wnd.sum > 0 & S < 0 - S = +1 - Elif wnd.sum < 0 & S > 0 - Maxes.push(wnd.maxindex) - Modes ++ - S = -1 -W++ -while Modes > 2 -If Modes == 2 -Cutoff = Maxes[0] -Min = V[I] -For I=Maxes[0] to Maxes[1] - if V[I] < Min - Min = V[I] - Cutoff = i - */ - - public int CuttOff() - { - if (Values.Length > 1) - { - var grad = new int[Values.Length]; - grad[0] = 0; - grad[1] = 0; - for (int k = 2; k < Values.Length; k++) - { - grad[k - 1] = Values[k] - Values[k - 1]; - } - var modes = 0; - var window = 0; - var sign = 1; - var sum = 0; - var max = 0; - var maxInd = 0; - var maxes = new List(); - do - { - maxes.Clear(); - window++; - modes = 0; - sum = 0; - for (int i = 0; i < grad.Length; i += window) - { - sum = grad[i]; - max = Values[i]; - maxInd = i; - for (var w = 1; w < window && (i + w) < grad.Length; w++) - { - sum += grad[i + w]; - if (Values[i + w] > max) - { - max = Values[i + w]; - maxInd = i + w; - } - } - if (sum > 0 && sign < 0) - { - sign = 1; - } - else if (sum < 0 && sign > 0) - { - modes++; - maxes.Add(maxInd); - sign = -1; - } - } - } while (modes > 2); - if (modes == 2) - { - var cutoff = maxes[0]; - var min = Values[cutoff]; - for (int i = maxes[0] + 1; i < maxes[1]; i++) - { - if (Values[i] < min) - { - cutoff = i; - min = Values[i]; - } - } - return cutoff; - } - } - return -1; - } - - #endregion - - static bool NumbersHasSameSign(int left, int right) - { - return left >= 0 && right >= 0 || left < 0 && right < 0; - } - } -} diff --git a/ZeroLevel.HNSW/Model/HistogramMode.cs b/ZeroLevel.HNSW/Model/HistogramMode.cs deleted file mode 100644 index b897d0d..0000000 --- a/ZeroLevel.HNSW/Model/HistogramMode.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace ZeroLevel.HNSW -{ - public enum HistogramMode - { - /// - /// 1 + 3.2 * Ln(LinksCount) - /// - SQRT, - /// - /// Sqrt(LinksCount) - /// - LOG - } -} diff --git a/ZeroLevel.HNSW/Model/NSWOptions.cs b/ZeroLevel.HNSW/Model/NSWOptions.cs deleted file mode 100644 index caf47a0..0000000 --- a/ZeroLevel.HNSW/Model/NSWOptions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW -{ - public sealed class NSWOptions - { - /// - /// Max node connections on Layer - /// - public readonly int M; - /// - /// Max search buffer - /// - public readonly int EF; - /// - /// Max search buffer for inserting - /// - public readonly int EFConstruction; - - public static NSWOptions Create(int v1, int v2, int v3, int v4, Func l2Euclidean, object selectionHeuristic) - { - throw new NotImplementedException(); - } - - /// - /// Distance function beetween vectors - /// - public readonly Func Distance; - - public readonly int LayersCount; - - - private NSWOptions(int layersCount, - int m, - int ef, - int ef_construction, - Func distance) - { - LayersCount = layersCount; - M = m; - EF = ef; - EFConstruction = ef_construction; - Distance = distance; - } - - public static NSWOptions Create(int layersCount, - int M, - int EF, - int EF_construction, - Func distance) => - new NSWOptions(layersCount, M, EF, EF_construction, distance); - } -} diff --git a/ZeroLevel.HNSW/Model/SearchContext.cs b/ZeroLevel.HNSW/Model/SearchContext.cs deleted file mode 100644 index a80597c..0000000 --- a/ZeroLevel.HNSW/Model/SearchContext.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace ZeroLevel.HNSW -{ - public enum Mode - { - None, - ActiveCheck, - InactiveCheck, - ActiveInactiveCheck - } - - public sealed class SearchContext - { - /// - /// Список номеров которые разрешены к добавлению итогового результата, если поиск ведется в ограниченном наборе точек (например, после предварительной фильтрации) - /// - private HashSet _activeNodes; - /// - /// Список точек с которых начинается поиск в графе для расширения - /// - private HashSet _entryNodes; - /// - /// Режим работы алгоритма расширения, зависящий от того заданы ли ограничения в точках, и заданы ли точки начала поиска - /// - private Mode _mode; - - public Mode NodeCheckMode => _mode; - public double PercentInTotal { get; private set; } = 0; - public long AvaliableNodesCount => _activeNodes?.Count ?? 0; - - public SearchContext() - { - _mode = Mode.None; - } - - /// - /// Расчет процентного содержания точек доступных для использования в данном контексте, по отношению к общему количеству точек - /// - public SearchContext CaclulatePercentage(long total) - { - if ((_mode == Mode.ActiveCheck || _mode == Mode.ActiveInactiveCheck) && total > 0) - { - PercentInTotal = ((_activeNodes?.Count ?? 0 * 100d) / (double)total) / 100.0d; - } - return this; - } - - public SearchContext SetPercentage(double percent) - { - PercentInTotal = percent; - return this; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool _isActiveNode(int nodeId) => _activeNodes?.Contains(nodeId) ?? false; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool _isEntryNode(int nodeId) => _entryNodes?.Contains(nodeId) ?? false; - - - /// - /// Проверка, подходит ли указанная точка для включения в набор расширения - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool IsActiveNode(int nodeId) - { - switch (_mode) - { - // Если задан набор разрешенных к использованию точек, проверяется вхождение в него - case Mode.ActiveCheck: return _isActiveNode(nodeId); - // Если задан набор точек начала поиска, проверка невхождения точки в него - case Mode.InactiveCheck: return _isEntryNode(nodeId) == false; - // Если задан и ограничивающий и начальный наборы точек, проверка и на ограничение и на невхождение в начальный набор - case Mode.ActiveInactiveCheck: return false == _isEntryNode(nodeId) && _isActiveNode(nodeId); - } - return nodeId >= 0; - } - - public IEnumerable EntryPoints => _entryNodes; - - public SearchContext SetActiveNodes(IEnumerable activeNodes) - { - if (activeNodes != null && activeNodes.Any()) - { - if (_mode == Mode.ActiveCheck || _mode == Mode.ActiveInactiveCheck) - { - throw new InvalidOperationException("Active nodes are already defined"); - } - _activeNodes = new HashSet(activeNodes); - if (_mode == Mode.None) - { - _mode = Mode.ActiveCheck; - } - else if (_mode == Mode.InactiveCheck) - { - _mode = Mode.ActiveInactiveCheck; - } - } - return this; - } - - public SearchContext SetEntryPointsNodes(IEnumerable entryNodes) - { - if (entryNodes != null && entryNodes.Any()) - { - if (_mode == Mode.InactiveCheck || _mode == Mode.ActiveInactiveCheck) - { - throw new InvalidOperationException("Inctive nodes are already defined"); - } - _entryNodes = new HashSet(entryNodes); - if (_mode == Mode.None) - { - _mode = Mode.InactiveCheck; - } - else if (_mode == Mode.ActiveCheck) - { - _mode = Mode.ActiveInactiveCheck; - } - } - return this; - } - } -} diff --git a/ZeroLevel.HNSW/PHNSW/HLevel.cs b/ZeroLevel.HNSW/PHNSW/HLevel.cs deleted file mode 100644 index ad2fc7a..0000000 --- a/ZeroLevel.HNSW/PHNSW/HLevel.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW.PHNSW -{ - internal class HLevel - : IPHNSWLevel - { - private readonly float _distance; - public HLevel(float distance) - { - _distance = distance; - } - - public Node Node { get; set; } = null; - public IPHNSWLevel NextLevelA { get; set; } - public IPHNSWLevel NextLevelB { get; set; } - - private float _abDistance = float.MinValue; - - public void Add(Node node) - { - if (NextLevelA.Node == null) { NextLevelA.Node = node; } - else if (NextLevelB.Node == null) - { - NextLevelB.Node = node; - _abDistance = PHNSWMetric.CosineDistance(NextLevelA.Node.Vector, NextLevelB.Node.Vector); - } - else - { - var an = PHNSWMetric.CosineDistance(NextLevelA.Node.Vector, node.Vector); - var bn = PHNSWMetric.CosineDistance(NextLevelB.Node.Vector, node.Vector); - - var abDiff = Math.Abs(_distance - _abDistance); - var anDiff = Math.Abs(_distance - an); - var bnDiff = Math.Abs(_distance - bn); - - if (abDiff < anDiff && abDiff < bnDiff) - { - if (an < bn) - { - NextLevelA.Add(node); - } - else - { - NextLevelB.Add(node); - } - } - else if (anDiff < bnDiff && anDiff < abDiff) - { - NextLevelA.Node = node; - NextLevelA.Add(node); - } - else - { - NextLevelB.Node = node; - NextLevelB.Add(node); - } - } - } - } -} diff --git a/ZeroLevel.HNSW/PHNSW/IPHNSWLevel.cs b/ZeroLevel.HNSW/PHNSW/IPHNSWLevel.cs deleted file mode 100644 index 64dfba9..0000000 --- a/ZeroLevel.HNSW/PHNSW/IPHNSWLevel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ZeroLevel.HNSW.PHNSW -{ - public interface IPHNSWLevel - { - void Add(IPHNSWLevel prevLayer, Node node); - Node Node { get; internal set; } - } -} diff --git a/ZeroLevel.HNSW/PHNSW/Node.cs b/ZeroLevel.HNSW/PHNSW/Node.cs deleted file mode 100644 index 1ef51bd..0000000 --- a/ZeroLevel.HNSW/PHNSW/Node.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ZeroLevel.DocumentObjectModel.Flow; - -namespace ZeroLevel.HNSW.PHNSW -{ - public class Node - { - public float[] Vector { get; set; } - public TPayload Payload { get; set; } - - public List> Neighbors { get; } - } -} diff --git a/ZeroLevel.HNSW/PHNSW/PHNSWBuilder.cs b/ZeroLevel.HNSW/PHNSW/PHNSWBuilder.cs deleted file mode 100644 index 6351ba5..0000000 --- a/ZeroLevel.HNSW/PHNSW/PHNSWBuilder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ZeroLevel.HNSW.PHNSW -{ - public static class PHNSWBuilder - { - public static IPHNSWLevel Build(int levels) - { - var distance = 0.33f; - var root = new HLevel(distance); - var horizontalLayers = new List>(new[] { root }); - for (var i = 0; i < levels; i++) - { - distance /= 2.0f; - var nextList = new List>(); - foreach (var layer in horizontalLayers) - { - var a = new HLevel(distance); - var b = new HLevel(distance); - layer.NextLevelA = a; - layer.NextLevelB = b; - nextList.Add(a); - nextList.Add(b); - } - horizontalLayers = nextList; - } - var uwLevel = new UWLevel(); - - } - } -} diff --git a/ZeroLevel.HNSW/PHNSW/PHNSWMetric.cs b/ZeroLevel.HNSW/PHNSW/PHNSWMetric.cs deleted file mode 100644 index f6b7dd6..0000000 --- a/ZeroLevel.HNSW/PHNSW/PHNSWMetric.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW.PHNSW -{ - internal static class PHNSWMetric - { - internal static float CosineDistance(float[] u, float[] v) - { - if (u.Length != v.Length) - { - throw new ArgumentException("Vectors have non-matching dimensions"); - } - - float dot = 0.0f; - float nru = 0.0f; - float nrv = 0.0f; - for (int i = 0; i < u.Length; ++i) - { - dot += u[i] * v[i]; - nru += u[i] * u[i]; - nrv += v[i] * v[i]; - } - - var similarity = dot / (float)(Math.Sqrt(nru) * Math.Sqrt(nrv)); - return 1 - similarity; - } - } -} diff --git a/ZeroLevel.HNSW/Services/AutomaticGraphClusterer.cs b/ZeroLevel.HNSW/Services/AutomaticGraphClusterer.cs deleted file mode 100644 index e8e0d9c..0000000 --- a/ZeroLevel.HNSW/Services/AutomaticGraphClusterer.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace ZeroLevel.HNSW.Services -{ - public class Cluster - : IEnumerable - { - private HashSet _elements = new HashSet(); - - public int Count => _elements.Count; - - public bool Contains(int id) => _elements.Contains(id); - - public bool Add(int id) => _elements.Add(id); - - public IEnumerator GetEnumerator() - { - return _elements.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _elements.GetEnumerator(); - } - - public void Merge(Cluster cluster) - { - foreach (var e in cluster) - { - this._elements.Add(e); - } - } - - public float MaxDistance(Func distance, Cluster other) - { - var max = float.MinValue; - foreach (var e in this._elements) - { - foreach (var o in other) - { - var d = distance(e, o); - if (d > max) - { - max = d; - } - } - } - return max; - } - - public float MinDistance(Func distance, Cluster other) - { - var min = float.MaxValue; - foreach (var e in this._elements) - { - foreach (var o in other) - { - var d = distance(e, o); - if (d < min) - { - min = d; - } - } - } - return min; - } - - public float AvgDistance(Func distance, Cluster other) - { - var dist = new List(); - foreach (var e in this._elements) - { - foreach (var o in other) - { - dist.Add(distance(e, o)); - } - } - return dist.Average(); - } - } - - public static class AutomaticGraphClusterer - { - private class Link - { - public int Id1; - public int Id2; - public float Distance; - } - - public static List DetectClusters(SmallWorld world) - { - var distance = world.DistanceFunction; - var links = world.GetLinks().SelectMany(pair => pair.Value.Select(id => new Link { Id1 = pair.Key, Id2 = id, Distance = distance(pair.Key, id) })).ToList(); - - // 1. Find R - bound between intra-cluster distances and out-of-cluster distances - var histogram = new Histogram(HistogramMode.LOG, links.Select(l => l.Distance)); - int threshold = histogram.CuttOff(); - var min = histogram.Bounds[threshold - 1]; - var max = histogram.Bounds[threshold]; - var R = (max + min) / 2; - - - // 2. Get links with distances less than R - var resultLinks = new List(); - foreach (var l in links) - { - if (l.Distance < R) - { - resultLinks.Add(l); - } - } - - // 3. Extract clusters - List clusters = new List(); - foreach (var l in resultLinks) - { - var id1 = l.Id1; - var id2 = l.Id2; - bool found = false; - foreach (var c in clusters) - { - if (c.Contains(id1)) - { - c.Add(id2); - found = true; - break; - } - else if (c.Contains(id2)) - { - c.Add(id1); - found = true; - break; - } - } - if (found == false) - { - var c = new Cluster(); - c.Add(id1); - c.Add(id2); - clusters.Add(c); - } - } - return clusters; - } - } -} diff --git a/ZeroLevel.HNSW/Services/CompactBiDirectionalLinksSet.cs b/ZeroLevel.HNSW/Services/CompactBiDirectionalLinksSet.cs deleted file mode 100644 index 5e4a99e..0000000 --- a/ZeroLevel.HNSW/Services/CompactBiDirectionalLinksSet.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - internal sealed class CompactBiDirectionalLinksSet - : IBinarySerializable, IDisposable - { - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - private const int HALF_LONG_BITS = 32; - - private SortedList _set = new SortedList(); - - internal SortedList Links => _set; - - internal (int, int) this[int index] - { - get - { - var k = _set.Keys[index]; - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - return (id1, id2); - } - } - - internal int Count => _set.Count; - - internal IEnumerable<(int, int, float)> FindLinksForId(int id) - { - _rwLock.EnterReadLock(); - try - { - if (_set.Count == 1) - { - var k = _set.Keys[0]; - var v = _set[k]; - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - if (id1 == id) yield return (id, id2, v); - else if (id2 == id) yield return (id1, id, v); - } - else if (_set.Count > 1) - { - foreach (var (k, v) in Search(_set, id)) - { - var id1 = (int)(k >> HALF_LONG_BITS); - var id2 = (int)(k - (((long)id1) << HALF_LONG_BITS)); - yield return (id1, id2, v); - } - } - } - finally - { - _rwLock.ExitReadLock(); - } - } - - internal IEnumerable<(int, int, float)> Items() - { - _rwLock.EnterReadLock(); - try - { - foreach (var pair in _set) - { - var id1 = (int)(pair.Key >> HALF_LONG_BITS); - var id2 = (int)(pair.Key - (((long)id1) << HALF_LONG_BITS)); - yield return (id1, id2, pair.Value); - } - } - finally - { - _rwLock.ExitReadLock(); - } - } - - internal void RemoveIndex(int id1, int id2) - { - long k1 = (((long)(id1)) << HALF_LONG_BITS) + id2; - long k2 = (((long)(id2)) << HALF_LONG_BITS) + id1; - _rwLock.EnterWriteLock(); - try - { - if (_set.ContainsKey(k1)) - { - _set.Remove(k1); - } - if (_set.ContainsKey(k2)) - { - _set.Remove(k2); - } - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - internal bool Add(int id1, int id2, float distance) - { - _rwLock.EnterWriteLock(); - try - { - long k1 = (((long)(id1)) << HALF_LONG_BITS) + id2; - long k2 = (((long)(id2)) << HALF_LONG_BITS) + id1; - if (_set.ContainsKey(k1) == false) - { - _set.Add(k1, distance); - if (k1 != k2) - { - _set.Add(k2, distance); - } - return true; - } - } - finally - { - _rwLock.ExitWriteLock(); - } - return false; - } - - /* - -function binary_search(A, n, T) is - L := 0 - R := n − 1 - while L ≤ R do - m := floor((L + R) / 2) - if A[m] < T then - L := m + 1 - else if A[m] > T then - R := m − 1 - else: - return m - return unsuccessful - - */ - - private static IEnumerable<(long, float)> Search(SortedList set, int index) - { - long k = ((long)index) << HALF_LONG_BITS; // T - int left = 0; - int right = set.Count - 1; - int mid; - long test; - while (left <= right) - { - mid = (int)Math.Floor((right + left) / 2d); - test = (set.Keys[mid] >> HALF_LONG_BITS) << HALF_LONG_BITS; // A[m] - - if (test < k) - { - left = mid + 1; - } - else if (test > k) - { - right = mid - 1; - } - else - { - return SearchByPosition(set, k, mid); - } - } - return Enumerable.Empty<(long, float)>(); - } - - private static IEnumerable<(long, float)> SearchByPosition(SortedList set, long k, int position) - { - var start = position; - var end = position; - do - { - position--; - } while (position >= 0 && ((set.Keys[position] >> HALF_LONG_BITS) << HALF_LONG_BITS) == k); - start = position + 1; - position = end + 1; - while (position < set.Count && ((set.Keys[position] >> HALF_LONG_BITS) << HALF_LONG_BITS) == k) - { - position++; - } - end = position - 1; - for (int i = start; i <= end; i++) - { - yield return (set.Keys[i], set.Values[i]); - } - } - - public Histogram CalculateHistogram(HistogramMode mode) - { - return new Histogram(mode, _set.Values); - } - - internal float Distance(int id1, int id2) - { - long k = (((long)(id1)) << HALF_LONG_BITS) + id2; - if (_set.ContainsKey(k)) - { - return _set[k]; - } - return float.MaxValue; - } - - public void Dispose() - { - _rwLock.Dispose(); - _set.Clear(); - _set = null; - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteBoolean(true); // true - set with weights - writer.WriteInt32(_set.Count); - foreach (var record in _set) - { - writer.WriteLong(record.Key); - writer.WriteFloat(record.Value); - } - } - - public void Deserialize(IBinaryReader reader) - { - if (reader.ReadBoolean() == false) - { - throw new InvalidOperationException("Incompatible data format. The set does not contain weights."); - } - _set.Clear(); - _set = null; - var count = reader.ReadInt32(); - _set = new SortedList(count + 1); - for (int i = 0; i < count; i++) - { - var key = reader.ReadLong(); - var value = reader.ReadFloat(); - _set.Add(key, value); - } - } - } -} diff --git a/ZeroLevel.HNSW/Services/HNSWMap.cs b/ZeroLevel.HNSW/Services/HNSWMap.cs deleted file mode 100644 index 0f21705..0000000 --- a/ZeroLevel.HNSW/Services/HNSWMap.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - // object -> vector -> vectorId - // HNSW vectorId + vector - // Map object feature - vectorId - public class HNSWMap - : IBinarySerializable - { - private Dictionary _map; - private Dictionary _reverse_map; - - public int this[TFeature feature] => _map.GetValueOrDefault(feature); - - public HNSWMap() { } - public HNSWMap(int capacity = -1) - { - if (capacity > 0) - { - _map = new Dictionary(capacity); - _reverse_map = new Dictionary(capacity); - } - else - { - _map = new Dictionary(); - _reverse_map = new Dictionary(); - - } - } - - public HNSWMap(Stream stream) - { - using (var reader = new MemoryStreamReader(stream)) - { - Deserialize(reader); - } - } - - public void Append(TFeature feature, int vectorId) - { - _map[feature] = vectorId; - _reverse_map[vectorId] = feature; - } - - public IEnumerable ConvertFeaturesToIds(IEnumerable features) - { - int id; - foreach (var feature in features) - { - if (_map.TryGetValue(feature, out id)) - { - yield return id; - } - } - } - - public IEnumerable ConvertIdsToFeatures(IEnumerable ids) - { - TFeature feature; - foreach (var id in ids) - { - if (_reverse_map.TryGetValue(id, out feature)) - { - yield return feature; - } - } - } - - public void Deserialize(IBinaryReader reader) - { - this._map = reader.ReadDictionary(); - this._reverse_map = reader.ReadDictionary(); - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteDictionary(this._map); - writer.WriteDictionary(this._reverse_map); - } - } -} diff --git a/ZeroLevel.HNSW/Services/HNSWMappers.cs b/ZeroLevel.HNSW/Services/HNSWMappers.cs deleted file mode 100644 index 8ac018e..0000000 --- a/ZeroLevel.HNSW/Services/HNSWMappers.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - public class HNSWMappers - : IBinarySerializable - { - private IDictionary> _mappers; - private readonly Func _bucketFunction; - - public HNSWMappers(string filePath, Func bucketFunction) - { - _bucketFunction = bucketFunction; - using (var fs = File.OpenRead(filePath)) - { - using (var bs = new BufferedStream(fs, 1024 * 1024 * 32)) - { - using (var reader = new MemoryStreamReader(bs)) - { - Deserialize(reader); - } - } - } - } - - public void Save(string filePath) - { - using (var fs = File.OpenWrite(filePath)) - { - using (var bs = new BufferedStream(fs, 1024 * 1024 * 32)) - { - using (var writer = new MemoryStreamWriter(bs)) - { - Serialize(writer); - } - } - } - } - - public HNSWMappers(Func bucketFunction) - { - _mappers = new Dictionary>(); - _bucketFunction = bucketFunction; - } - - public void Append(HNSWMap map, int c) - { - _mappers.Add(c, map); - } - - public IEnumerable ConvertIdsToFeatures(int c, IEnumerable ids) - { - foreach (var feature in _mappers[c].ConvertIdsToFeatures(ids)) - { - yield return feature; - } - } - - public IDictionary CreateContext(IEnumerable activeNodes, IEnumerable entryPoints) - { - var actives = new Dictionary>(); - var entries = new Dictionary>(); - if (activeNodes != null) - { - foreach (var node in activeNodes) - { - var c = _bucketFunction(node); - if (_mappers.ContainsKey(c)) - { - if (actives.ContainsKey(c) == false) - { - actives.Add(c, new List()); - } - actives[c].Add(_mappers[c][node]); - } - else - { - Log.Warning($"Active node {node} is not included in graphs!"); - } - } - } - if (entryPoints != null) - { - foreach (var entryPoint in entryPoints) - { - var c = _bucketFunction(entryPoint); - if (_mappers.ContainsKey(c)) - { - if (entries.ContainsKey(c) == false) - { - entries.Add(c, new List()); - } - entries[c].Add(_mappers[c][entryPoint]); - } - else - { - Log.Warning($"Entry point {entryPoint} is not included in graphs!"); - } - } - } - var result = new Dictionary(); - foreach (var pair in _mappers) - { - var active = actives.GetValueOrDefault(pair.Key); - var entry = entries.GetValueOrDefault(pair.Key); - result.Add(pair.Key, new SearchContext().SetActiveNodes(active).SetEntryPointsNodes(entry)); - } - var total = result.Values.Sum(v => v.AvaliableNodesCount); - if (total > 0) - { - foreach (var pair in result) - { - pair.Value.CaclulatePercentage(total); - } - } - else - { - //total = result.Values.Sum(v => v.EntryPoints.Count()); - foreach (var pair in result) - { - //var p = (double)pair.Value.EntryPoints.Count() / (double)total; - pair.Value.SetPercentage(0.2d); - } - } - return result; - } - - public void Deserialize(IBinaryReader reader) - { - this._mappers = reader.ReadDictionary>(); - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteDictionary>(this._mappers); - } - } -} diff --git a/ZeroLevel.HNSW/Services/LAL/LALGraph.cs b/ZeroLevel.HNSW/Services/LAL/LALGraph.cs deleted file mode 100644 index 622f185..0000000 --- a/ZeroLevel.HNSW/Services/LAL/LALGraph.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - public class LALGraph - : IBinarySerializable - { - private readonly LALLinks _links = new LALLinks(); - - public LALGraph() { } - public static LALGraph FromLALGraph(Stream stream) - { - var l = new LALGraph(); - l.Deserialize(stream); - return l; - } - - public static LALGraph FromHNSWGraph(Stream stream) - { - var l = new LALGraph(); - l.DeserializeFromHNSW(stream); - return l; - } - - public IEnumerable KNearest(int k, SearchContext context) - { - var v = new VisitedBitSet(_links.Count, 1); - var C = new Queue(); - var W = new HashSet(); - var entryPoints = context.EntryPoints; - var nextEntry = new HashSet(); - do - { - foreach (var ep in entryPoints) - { - var neighboursIds = _links.FindNeighbors(ep); - for (int i = 0; i < neighboursIds.Length; ++i) - { - if (v.Contains(neighboursIds[i]) == false) - { - C.Enqueue(neighboursIds[i]); - nextEntry.Add(neighboursIds[i]); - } - } - v.Add(ep); - } - // run bfs - while (C.Count > 0) - { - // get next candidate to check and expand - var toExpand = C.Dequeue(); - if (context.IsActiveNode(toExpand)) - { - if (W.Count < k) - { - W.Add(toExpand); - if (W.Count > k) - { - W.Remove(W.First()); - } - } - } - } - entryPoints = nextEntry.Select(id => id).ToList(); - nextEntry.Clear(); - } - while (W.Count < k && entryPoints.Any()); - C.Clear(); - v.Clear(); - return W; - } - - public void Deserialize(Stream stream) - { - using (var reader = new MemoryStreamReader(stream)) - { - _links.Deserialize(reader); - } - } - - public void DeserializeFromHNSW(Stream stream) - { - using (var reader = new MemoryStreamReader(stream)) - { - reader.ReadInt32(); // EntryPoint - reader.ReadInt32(); // MaxLayer - - int count = reader.ReadInt32(); // Vectors count - for (int i = 0; i < count; i++) - { - var v = reader.ReadCompatible(); // Vector - } - - var lc = reader.ReadInt32(); // countLayers - _links.Deserialize(reader); // deserialize only base layer and skip another - } - } - - public void Serialize(Stream stream) - { - using (var writer = new MemoryStreamWriter(stream)) - { - _links.Serialize(writer); - } - } - - public void Serialize(IBinaryWriter writer) - { - _links.Serialize(writer); - } - - public void Deserialize(IBinaryReader reader) - { - _links.Deserialize(reader); - } - } -} diff --git a/ZeroLevel.HNSW/Services/LAL/LALLinks.cs b/ZeroLevel.HNSW/Services/LAL/LALLinks.cs deleted file mode 100644 index 3a3ef5d..0000000 --- a/ZeroLevel.HNSW/Services/LAL/LALLinks.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - internal class LALLinks - : IBinarySerializable - { - private ConcurrentDictionary _set = new ConcurrentDictionary(); - internal IDictionary Links => _set; - - private readonly int[] _empty = new int[0]; - internal int Count => _set.Count; - - public LALLinks() - { - } - - internal IEnumerable<(int, int)> FindLinksForId(int id) - { - if (_set.ContainsKey(id)) - { - return _set[id].Select(v => (id, v)); - } - return Enumerable.Empty<(int, int)>(); - } - - internal int[] FindNeighbors(int id) - { - if (_set.ContainsKey(id)) - { - return _set[id]; - } - return _empty; - } - - internal IEnumerable<(int, int)> Items() - { - return _set - .SelectMany(pair => _set[pair.Key] - .Select(v => (pair.Key, v))); - } - - public void Dispose() - { - _set.Clear(); - _set = null; - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteInt32(_set.Count); - foreach (var record in _set) - { - writer.WriteInt32(record.Key); - writer.WriteCollection(record.Value); - } - } - - public void Deserialize(IBinaryReader reader) - { - _set.Clear(); - _set = null; - var count = reader.ReadInt32(); - _set = new ConcurrentDictionary(1, count); - - for (int i = 0; i < count; i++) - { - var id = reader.ReadInt32(); - var links_count = reader.ReadInt32(); - _set[id] = new int[links_count]; - for (int l = 0; l < links_count; l++) - { - _set[id][l] = reader.ReadInt32(); - } - } - } - } -} diff --git a/ZeroLevel.HNSW/Services/LAL/SplittedLALGraph.cs b/ZeroLevel.HNSW/Services/LAL/SplittedLALGraph.cs deleted file mode 100644 index 3c2ce70..0000000 --- a/ZeroLevel.HNSW/Services/LAL/SplittedLALGraph.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - public class SplittedLALGraph - : IBinarySerializable - { - private IDictionary _graphs; - - public SplittedLALGraph() - { - _graphs = new Dictionary(); - } - - public SplittedLALGraph(string filePath) - { - using (var fs = File.OpenRead(filePath)) - { - using (var bs = new BufferedStream(fs, 1024 * 1024 * 32)) - { - using (var reader = new MemoryStreamReader(bs)) - { - Deserialize(reader); - } - } - } - } - - public void Save(string filePath) - { - using (var fs = File.OpenWrite(filePath)) - { - using (var bs = new BufferedStream(fs, 1024 * 1024 * 32)) - { - using (var writer = new MemoryStreamWriter(bs)) - { - Serialize(writer); - } - } - } - } - - public void Append(LALGraph graph, int c) - { - _graphs.Add(c, graph); - } - - public IDictionary> KNearest(int k, IDictionary contexts) - { - var result = new Dictionary>(); - int step = 1; - foreach (var graph in _graphs) - { - result.Add(graph.Key, new List()); - var context = contexts[graph.Key]; - if (context.EntryPoints != null) - { - var partial_k = 1 + (int)(context.PercentInTotal * k); - var r = graph.Value.KNearest(partial_k, context) as HashSet; - result[graph.Key].AddRange(r); - } - step++; - } - return result; - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteDictionary(this._graphs); - } - public void Deserialize(IBinaryReader reader) - { - this._graphs = reader.ReadDictionary(); - } - } -} diff --git a/ZeroLevel.HNSW/Services/Layer.cs b/ZeroLevel.HNSW/Services/Layer.cs deleted file mode 100644 index f4489a1..0000000 --- a/ZeroLevel.HNSW/Services/Layer.cs +++ /dev/null @@ -1,470 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - /// - /// NSW graph - /// - internal sealed class Layer - : IBinarySerializable - { - private readonly NSWOptions _options; - private readonly VectorSet _vectors; - private readonly LinksSet _links; - public readonly int M; - private readonly Dictionary connections; - internal IDictionary> Links => _links.Links; - - /// - /// There are links е the layer - /// - internal bool HasLinks => (_links.Count > 0); - - internal IEnumerable this[int vector_index] => _links.FindNeighbors(vector_index); - - /// - /// HNSW layer - /// - /// Article: Section 4.1: - /// "Selection of the Mmax0 (the maximum number of connections that an element can have in the zero layer) also - /// has a strong influence on the search performance, especially in case of high quality(high recall) search. - /// Simulations show that setting Mmax0 to M(this corresponds to kNN graphs on each layer if the neighbors - /// selection heuristic is not used) leads to a very strong performance penalty at high recall. - /// Simulations also suggest that 2∙M is a good choice for Mmax0; - /// setting the parameter higher leads to performance degradation and excessive memory usage." - /// - /// - /// HNSW graph options - /// General vector set - internal Layer(NSWOptions options, VectorSet vectors, bool nswLayer) - { - _options = options; - _vectors = vectors; - M = nswLayer ? 2 * _options.M : _options.M; - _links = new LinksSet(M); - connections = new Dictionary(M + 1); - } - - internal int FindEntryPointAtLayer(Func targetCosts) - { - if (_links.Count == 0) return EntryPoint; - var set = new HashSet(_links.Items().Select(p => p.Item1)); - int minId = -1; - float minDist = float.MaxValue; - foreach (var id in set) - { - var d = targetCosts(id); - if (d < minDist && Math.Abs(d) > float.Epsilon) - { - minDist = d; - minId = id; - } - } - return minId; - } - - internal void Push(int q, int ep, MinHeap W, Func distance) - { - if (HasLinks == false) - { - AddBidirectionallConnections(q, q); - } - else - { - // W ← SEARCH - LAYER(q, ep, efConstruction, lc) - foreach (var i in KNearestAtLayer(ep, distance, _options.EFConstruction)) - { - W.Push(i); - } - - int count = 0; - connections.Clear(); - while (count < M && W.Count > 0) - { - var nearest = W.Pop(); - var nearest_nearest = GetNeighbors(nearest.Item1).ToArray(); - if (nearest_nearest.Length < M) - { - if (AddBidirectionallConnections(q, nearest.Item1)) - { - connections.Add(nearest.Item1, nearest.Item2); - count++; - } - } - else - { - if ((M - count) < 2) - { - // remove link q - max_q - var max = connections.OrderBy(pair => pair.Value).First(); - RemoveBidirectionallConnections(q, max.Key); - connections.Remove(max.Key); - } - // get nearest_nearest candidate - var mn_id = -1; - var mn_d = float.MinValue; - for (int i = 0; i < nearest_nearest.Length; i++) - { - var d = _options.Distance(_vectors[nearest.Item1], _vectors[nearest_nearest[i]]); - if (q != nearest_nearest[i] && connections.ContainsKey(nearest_nearest[i]) == false) - { - if (mn_id == -1 || d > mn_d) - { - mn_d = d; - mn_id = nearest_nearest[i]; - } - } - } - // remove link neareset - nearest_nearest - RemoveBidirectionallConnections(nearest.Item1, mn_id); - // add link q - neareset - if (AddBidirectionallConnections(q, nearest.Item1)) - { - connections.Add(nearest.Item1, nearest.Item2); - count++; - } - // add link q - max_nearest_nearest - if (AddBidirectionallConnections(q, mn_id)) - { - connections.Add(mn_id, mn_d); - count++; - } - } - } - } - } - - internal void RemoveBidirectionallConnections(int q, int p) - { - _links.RemoveIndex(q, p); - } - - internal bool AddBidirectionallConnections(int q, int p) - { - if (q == p) - { - if (EntryPoint >= 0) - { - return _links.Add(q, EntryPoint); - } - else - { - EntryPoint = q; - } - } - else - { - return _links.Add(q, p); - } - return false; - } - - private int EntryPoint = -1; - - #region Implementation of https://arxiv.org/ftp/arxiv/papers/1603/1603.09320.pdf - /// - /// Algorithm 2 - /// - /// query element - /// enter points ep - /// Output: ef closest neighbors to q - internal IEnumerable<(int, float)> KNearestAtLayer(int entryPointId, Func targetCosts, int ef) - { - /* - * v ← ep // set of visited elements - * C ← ep // set of candidates - * W ← ep // dynamic list of found nearest neighbors - * while │C│ > 0 - * c ← extract nearest element from C to q - * f ← get furthest element from W to q - * if distance(c, q) > distance(f, q) - * break // all elements in W are evaluated - * for each e ∈ neighbourhood(c) at layer lc // update C and W - * if e ∉ v - * v ← v ⋃ e - * f ← get furthest element from W to q - * if distance(e, q) < distance(f, q) or │W│ < ef - * C ← C ⋃ e - * W ← W ⋃ e - * if │W│ > ef - * remove furthest element from W to q - * return W - */ - - int farthestId; - float farthestDistance; - var d = targetCosts(entryPointId); - - var v = new VisitedBitSet(_vectors.Count, _options.M); - // * v ← ep // set of visited elements - v.Add(entryPointId); - // * C ← ep // set of candidates - var C = new MinHeap(ef); - C.Push((entryPointId, d)); - // * W ← ep // dynamic list of found nearest neighbors - var W = new MaxHeap(ef + 1); - W.Push((entryPointId, d)); - - // * while │C│ > 0 - while (C.Count > 0) - { - // * c ← extract nearest element from C to q - var c = C.Pop(); - // * f ← get furthest element from W to q - // * if distance(c, q) > distance(f, q) - if (W.TryPeek(out _, out farthestDistance) && c.Item2 > farthestDistance) - { - // * break // all elements in W are evaluated - break; - } - - // * for each e ∈ neighbourhood(c) at layer lc // update C and W - foreach (var e in GetNeighbors(c.Item1)) - { - // * if e ∉ v - if (!v.Contains(e)) - { - // * v ← v ⋃ e - v.Add(e); - // * f ← get furthest element from W to q - W.TryPeek(out farthestId, out farthestDistance); - - var eDistance = targetCosts(e); - // * if distance(e, q) < distance(f, q) or │W│ < ef - if (W.Count < ef || (farthestId >= 0 && eDistance < farthestDistance)) - { - // * C ← C ⋃ e - C.Push((e, eDistance)); - // * W ← W ⋃ e - W.Push((e, eDistance)); - // * if │W│ > ef - if (W.Count > ef) - { - // * remove furthest element from W to q - W.Pop(); - } - } - } - } - } - C.Clear(); - v.Clear(); - return W; - } - - internal IEnumerable<(int, float)> KNearestAtLayer(int entryPointId, Func targetCosts, int ef, SearchContext context) - { - int farthestId; - float farthestDistance; - var d = targetCosts(entryPointId); - - var v = new VisitedBitSet(_vectors.Count, _options.M); - // * v ← ep // set of visited elements - v.Add(entryPointId); - // * C ← ep // set of candidates - var C = new MinHeap(ef); - C.Push((entryPointId, d)); - // * W ← ep // dynamic list of found nearest neighbors - var W = new MaxHeap(ef + 1); - if (context.IsActiveNode(entryPointId)) - { - W.Push((entryPointId, d)); - } - - // * while │C│ > 0 - while (C.Count > 0) - { - // * c ← extract nearest element from C to q - var c = C.Pop(); - // * f ← get furthest element from W to q - // * if distance(c, q) > distance(f, q) - if (W.TryPeek(out _, out farthestDistance) && c.Item2 > farthestDistance) - { - // * break // all elements in W are evaluated - break; - } - - // * for each e ∈ neighbourhood(c) at layer lc // update C and W - foreach (var e in GetNeighbors(c.Item1)) - { - // * if e ∉ v - if (!v.Contains(e)) - { - // * v ← v ⋃ e - v.Add(e); - // * f ← get furthest element from W to q - W.TryPeek(out farthestId, out farthestDistance); - - var eDistance = targetCosts(e); - // * if distance(e, q) < distance(f, q) or │W│ < ef - if (W.Count < ef || (farthestId >= 0 && eDistance < farthestDistance)) - { - // * C ← C ⋃ e - C.Push((e, eDistance)); - // * W ← W ⋃ e - if (context.IsActiveNode(e)) - { - W.Push((e, eDistance)); - if (W.Count > ef) - { - W.Pop(); - } - } - } - } - } - } - C.Clear(); - v.Clear(); - return W; - } - - /// - /// Algorithm 2 - /// - /// query element - /// enter points ep - /// Output: ef closest neighbors to q - internal IEnumerable<(int, float)> KNearestAвtLayer(int entryPointId, Func targetCosts, int ef, SearchContext context) - { - int farthestId; - float farthestDistance; - var d = targetCosts(entryPointId); - - var v = new VisitedBitSet(_vectors.Count, _options.M); - // v ← ep // set of visited elements - v.Add(entryPointId); - // C ← ep // set of candidates - var C = new MinHeap(ef); - C.Push((entryPointId, d)); - // W ← ep // dynamic list of found nearest neighbors - var W = new MaxHeap(ef + 1); - // W ← ep // dynamic list of found nearest neighbors - if (context.IsActiveNode(entryPointId)) - { - W.Push((entryPointId, d)); - } - // run bfs - while (C.Count > 0) - { - // get next candidate to check and expand - var toExpand = C.Pop(); - if (W.TryPeek(out _, out farthestDistance) && toExpand.Item2 > farthestDistance) - { - // the closest candidate is farther than farthest result - break; - } - - // expand candidate - var neighboursIds = GetNeighbors(toExpand.Item1).ToArray(); - for (int i = 0; i < neighboursIds.Length; ++i) - { - int neighbourId = neighboursIds[i]; - if (!v.Contains(neighbourId)) - { - W.TryPeek(out farthestId, out farthestDistance); - // enqueue perspective neighbours to expansion list - var neighbourDistance = targetCosts(neighbourId); - if (context.IsActiveNode(neighbourId)) - { - if (W.Count < ef || (farthestId >= 0 && neighbourDistance < farthestDistance)) - { - W.Push((neighbourId, neighbourDistance)); - if (W.Count > ef) - { - W.Pop(); - } - } - } - if (W.TryPeek(out _, out farthestDistance) && neighbourDistance < farthestDistance) - { - C.Push((neighbourId, neighbourDistance)); - } - v.Add(neighbourId); - } - } - } - C.Clear(); - v.Clear(); - return W; - } - - /// - /// Algorithm 2, modified for LookAlike - /// - /// query element - /// enter points ep - /// Output: ef closest neighbors to q - internal IEnumerable<(int, float)> KNearestAtLayer(int ef, SearchContext context) - { - var distance = new Func((id1, id2) => _options.Distance(_vectors[id1], _vectors[id2])); - // v ← ep // set of visited elements - var v = new VisitedBitSet(_vectors.Count, _options.M); - // C ← ep // set of candidates - var C = new MinHeap(ef); - float dist; - var W = new MaxHeap(ef + 1); - var entryPoints = context.EntryPoints; - - do - { - foreach (var ep in entryPoints) - { - var neighboursIds = GetNeighbors(ep).ToArray(); - for (int i = 0; i < neighboursIds.Length; ++i) - { - C.Push((ep, distance(ep, neighboursIds[i]))); - } - v.Add(ep); - } - // run bfs - while (C.Count > 0) - { - // get next candidate to check and expand - var toExpand = C.Pop(); - if (W.TryPeek(out _, out dist) && toExpand.Item2 > dist) - { - // the closest candidate is farther than farthest result - break; - } - if (context.IsActiveNode(toExpand.Item1)) - { - if (W.Count < ef || W.Count == 0 || (W.TryPeek(out _, out dist) && toExpand.Item2 < dist)) - { - W.Push((toExpand.Item1, toExpand.Item2)); - if (W.Count > ef) - { - W.Pop(); - } - } - } - } - - entryPoints = W.Select(p => p.Item1); - } - while (W.Count < ef); - C.Clear(); - v.Clear(); - return W; - } - #endregion - - internal IEnumerable GetNeighbors(int id) => _links.FindNeighbors(id); - - public void Serialize(IBinaryWriter writer) - { - _links.Serialize(writer); - } - - public void Deserialize(IBinaryReader reader) - { - _links.Deserialize(reader); - } - - // internal Histogram GetHistogram(HistogramMode mode) => _links.CalculateHistogram(mode); - } -} diff --git a/ZeroLevel.HNSW/Services/LinksSet.cs b/ZeroLevel.HNSW/Services/LinksSet.cs deleted file mode 100644 index 2ded5f4..0000000 --- a/ZeroLevel.HNSW/Services/LinksSet.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - public class LinksSet - { - private ConcurrentDictionary> _set = new ConcurrentDictionary>(); - internal IDictionary> Links => _set; - internal int Count => _set.Count; - private readonly int _M; - - public LinksSet(int M) - { - _M = M; - } - - internal IEnumerable<(int, int)> FindLinksForId(int id) - { - if (_set.ContainsKey(id)) - { - return _set[id].Select(v => (id, v)); - } - return Enumerable.Empty<(int, int)>(); - } - - internal IEnumerable FindNeighbors(int id) - { - if (_set.ContainsKey(id)) - { - return _set[id]; - } - return Enumerable.Empty(); - } - - internal IEnumerable<(int, int)> Items() - { - return _set - .SelectMany(pair => _set[pair.Key] - .Select(v => (pair.Key, v))); - } - - internal void RemoveIndex(int id1, int id2) - { - _set[id1].Remove(id2); - _set[id2].Remove(id1); - } - - internal bool Add(int id1, int id2) - { - if (!_set.ContainsKey(id1)) - { - _set[id1] = new HashSet(_M + 1); - } - if (!_set.ContainsKey(id2)) - { - _set[id2] = new HashSet(_M + 1); - } - var r1 = _set[id1].Add(id2); - var r2 = _set[id2].Add(id1); - return r1 || r2; - } - - - public void Dispose() - { - _set.Clear(); - _set = null; - } - public void Serialize(IBinaryWriter writer) - { - writer.WriteInt32(_set.Count); - foreach (var record in _set) - { - writer.WriteInt32(record.Key); - writer.WriteCollection(record.Value); - } - } - public void Deserialize(IBinaryReader reader) - { - /*if (reader.ReadBoolean() != false) - { - throw new InvalidOperationException("Incompatible format"); - }*/ - _set.Clear(); - _set = null; - var count = reader.ReadInt32(); - _set = new ConcurrentDictionary>(); - for (int i = 0; i < count; i++) - { - var id = reader.ReadInt32(); - var links_count = reader.ReadInt32(); - _set[id] = new HashSet(links_count); - for (var l = 0; l < links_count; l++) - { - _set[id].Add(reader.ReadInt32()); - } - } - } - } -} diff --git a/ZeroLevel.HNSW/Services/MaxHeap.cs b/ZeroLevel.HNSW/Services/MaxHeap.cs deleted file mode 100644 index cc66b37..0000000 --- a/ZeroLevel.HNSW/Services/MaxHeap.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace ZeroLevel.HNSW.Services -{ - /// - /// Max element always on top - /// - public class MaxHeap : - IEnumerable<(int, float)> - { - private readonly List<(int, float)> _elements; - - public MaxHeap(int size = -1) - { - if (size > 0) - _elements = new List<(int, float)>(size); - else - _elements = new List<(int, float)>(); - } - - private int GetLeftChildIndex(int elementIndex) => 2 * elementIndex + 1; - private int GetRightChildIndex(int elementIndex) => 2 * elementIndex + 2; - private int GetParentIndex(int elementIndex) => (elementIndex - 1) / 2; - - private bool HasLeftChild(int elementIndex) => GetLeftChildIndex(elementIndex) < _elements.Count; - private bool HasRightChild(int elementIndex) => GetRightChildIndex(elementIndex) < _elements.Count; - private bool IsRoot(int elementIndex) => elementIndex == 0; - - private (int, float) GetLeftChild(int elementIndex) => _elements[GetLeftChildIndex(elementIndex)]; - private (int, float) GetRightChild(int elementIndex) => _elements[GetRightChildIndex(elementIndex)]; - private (int, float) GetParent(int elementIndex) => _elements[GetParentIndex(elementIndex)]; - - public int Count => _elements.Count; - - public void Clear() - { - _elements.Clear(); - } - - private void Swap(int firstIndex, int secondIndex) - { - var temp = _elements[firstIndex]; - _elements[firstIndex] = _elements[secondIndex]; - _elements[secondIndex] = temp; - } - - public bool IsEmpty() - { - return _elements.Count == 0; - } - - public bool TryPeek(out int id, out float value) - { - if (_elements.Count == 0) - { - id = -1; - value = 0; - return false; - } - id = _elements[0].Item1; - value = _elements[0].Item2; - return true; - } - - public (int, float) Pop() - { - if (_elements.Count == 0) - throw new IndexOutOfRangeException(); - - var result = _elements[0]; - _elements[0] = _elements[_elements.Count - 1]; - _elements.RemoveAt(_elements.Count - 1); - - ReCalculateDown(); - - return result; - } - - public void Push((int, float) element) - { - _elements.Add(element); - - ReCalculateUp(); - } - - private void ReCalculateDown() - { - int index = 0; - while (HasLeftChild(index)) - { - var biggerIndex = GetLeftChildIndex(index); - if (HasRightChild(index) && GetRightChild(index).Item2 > GetLeftChild(index).Item2) - { - biggerIndex = GetRightChildIndex(index); - } - - if (_elements[biggerIndex].Item2 < _elements[index].Item2) - { - break; - } - - Swap(biggerIndex, index); - index = biggerIndex; - } - } - - private void ReCalculateUp() - { - var index = _elements.Count - 1; - while (!IsRoot(index) && _elements[index].Item2 > GetParent(index).Item2) - { - var parentIndex = GetParentIndex(index); - Swap(parentIndex, index); - index = parentIndex; - } - } - - public IEnumerator<(int, float)> GetEnumerator() - { - return _elements.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _elements.GetEnumerator(); - } - } -} diff --git a/ZeroLevel.HNSW/Services/MinHeap.cs b/ZeroLevel.HNSW/Services/MinHeap.cs deleted file mode 100644 index c860da2..0000000 --- a/ZeroLevel.HNSW/Services/MinHeap.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace ZeroLevel.HNSW.Services -{ - /// - /// Min element always on top - /// - public class MinHeap : - IEnumerable<(int, float)> - { - private readonly List<(int, float)> _elements; - - public MinHeap(int size = -1) - { - if (size > 0) - _elements = new List<(int, float)>(size); - else - _elements = new List<(int, float)>(); - } - - private int GetLeftChildIndex(int elementIndex) => 2 * elementIndex + 1; - private int GetRightChildIndex(int elementIndex) => 2 * elementIndex + 2; - private int GetParentIndex(int elementIndex) => (elementIndex - 1) / 2; - - private bool HasLeftChild(int elementIndex) => GetLeftChildIndex(elementIndex) < _elements.Count; - private bool HasRightChild(int elementIndex) => GetRightChildIndex(elementIndex) < _elements.Count; - private bool IsRoot(int elementIndex) => elementIndex == 0; - - private (int, float) GetLeftChild(int elementIndex) => _elements[GetLeftChildIndex(elementIndex)]; - private (int, float) GetRightChild(int elementIndex) => _elements[GetRightChildIndex(elementIndex)]; - private (int, float) GetParent(int elementIndex) => _elements[GetParentIndex(elementIndex)]; - - public int Count => _elements.Count; - - public void Clear() - { - _elements.Clear(); - } - - private void Swap(int firstIndex, int secondIndex) - { - var temp = _elements[firstIndex]; - _elements[firstIndex] = _elements[secondIndex]; - _elements[secondIndex] = temp; - } - - public bool IsEmpty() - { - return _elements.Count == 0; - } - - public bool TryPeek(out int id, out float value) - { - if (_elements.Count == 0) - { - id = -1; - value = 0; - return false; - } - id = _elements[0].Item1; - value = _elements[0].Item2; - return true; - } - - public (int, float) Pop() - { - if (_elements.Count == 0) - throw new IndexOutOfRangeException(); - - var result = _elements[0]; - _elements[0] = _elements[_elements.Count - 1]; - _elements.RemoveAt(_elements.Count - 1); - - ReCalculateDown(); - - return result; - } - - public void Push((int, float) element) - { - _elements.Add(element); - - ReCalculateUp(); - } - - private void ReCalculateDown() - { - int index = 0; - while (HasLeftChild(index)) - { - var smallerIndex = GetLeftChildIndex(index); - if (HasRightChild(index) && GetRightChild(index).Item2 < GetLeftChild(index).Item2) - { - smallerIndex = GetRightChildIndex(index); - } - - if (_elements[smallerIndex].Item2 >= _elements[index].Item2) - { - break; - } - - Swap(smallerIndex, index); - index = smallerIndex; - } - } - - private void ReCalculateUp() - { - var index = _elements.Count - 1; - while (!IsRoot(index) && _elements[index].Item2 < GetParent(index).Item2) - { - var parentIndex = GetParentIndex(index); - Swap(parentIndex, index); - index = parentIndex; - } - } - - public IEnumerator<(int, float)> GetEnumerator() - { - return _elements.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _elements.GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/ZeroLevel.HNSW/Services/Quantizator.cs b/ZeroLevel.HNSW/Services/Quantizator.cs deleted file mode 100644 index 8f031fb..0000000 --- a/ZeroLevel.HNSW/Services/Quantizator.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW.Services -{ - public class Quantizator - { - private readonly float _min; - private readonly float _max; - private readonly float _diff; - - public Quantizator(float min, float max) - { - _min = min; - _max = max; - _diff = _max - _min; - } - - public byte[] Quantize(float[] v) - { - var result = new byte[v.Length]; - for (int i = 0; i < v.Length; i++) - { - result[i] = _quantizeInRange(v[i]); - } - return result; - } - - public int[] QuantizeToInt(float[] v) - { - var diff = v.Length % 4; - int count = (v.Length - diff) / 4; - var result = new int[((diff == 0) ? 0 : 1) + (v.Length / 4)]; - byte[] buf = new byte[4]; - int i = 0; - for (; i < count * 4; i += 4) - { - buf[0] = _quantizeInRange(v[i]); - buf[1] = _quantizeInRange(v[i + 1]); - buf[2] = _quantizeInRange(v[i + 2]); - buf[3] = _quantizeInRange(v[i + 3]); - result[(i >> 2)] = BitConverter.ToInt32(buf); - } - if (diff != 0) - { - for (var j = 0; j < diff; j++) - { - buf[j] = _quantizeInRange(v[i + j]); - } - for (var j = diff; j < 4; j++) - { - buf[j] = 0; - } - result[(i >> 2)] = BitConverter.ToInt32(buf); - } - return result; - } - - public long[] QuantizeToLong(float[] v) - { - var diff = v.Length % 8; - int count = (v.Length - diff) / 8; - var result = new long[((diff == 0) ? 0 : 1) + (v.Length / 8)]; - byte[] buf = new byte[8]; - int i = 0; - for (; i < count * 8; i += 8) - { - buf[0] = _quantizeInRange(v[i + 0]); - buf[1] = _quantizeInRange(v[i + 1]); - buf[2] = _quantizeInRange(v[i + 2]); - buf[3] = _quantizeInRange(v[i + 3]); - buf[4] = _quantizeInRange(v[i + 4]); - buf[5] = _quantizeInRange(v[i + 5]); - buf[6] = _quantizeInRange(v[i + 6]); - buf[7] = _quantizeInRange(v[i + 7]); - - result[(i >> 3)] = BitConverter.ToInt64(buf); - } - if (diff != 0) - { - for (var j = 0; j < diff; j++) - { - buf[j] = _quantizeInRange(v[i + j]); - } - for (var j = diff; j < 8; j++) - { - buf[j] = 0; - } - result[(i >> 3)] = BitConverter.ToInt64(buf); - } - return result; - } - - //Map x in [0,1] to {0, 1, ..., 255} - private byte _quantize(float x) - { - x = (int)Math.Floor(256 * x); - if (x < 0) return 0; - else if (x > 255) return 255; - else return (byte)x; - } - - //Map x in [min,max] to {0, 1, ..., 255} - private byte _quantizeInRange(float x) - { - return _quantize((x - _min) / (_diff)); - } - } -} diff --git a/ZeroLevel.HNSW/Services/VectorSet.cs b/ZeroLevel.HNSW/Services/VectorSet.cs deleted file mode 100644 index 07d1550..0000000 --- a/ZeroLevel.HNSW/Services/VectorSet.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - internal sealed class VectorSet - : IEnumerable, IBinarySerializable - { - private List _set = new List(); - private SpinLock _lock = new SpinLock(); - - internal T this[int index] => _set[index]; - internal int Count => _set.Count; - - internal int Append(T vector) - { - bool gotLock = false; - gotLock = false; - try - { - _lock.Enter(ref gotLock); - _set.Add(vector); - return _set.Count - 1; - } - finally - { - // Only give up the lock if you actually acquired it - if (gotLock) _lock.Exit(); - } - } - - internal int[] Append(IEnumerable vectors) - { - bool gotLock = false; - int startIndex, endIndex; - gotLock = false; - try - { - _lock.Enter(ref gotLock); - startIndex = _set.Count; - _set.AddRange(vectors); - endIndex = _set.Count; - } - finally - { - // Only give up the lock if you actually acquired it - if (gotLock) _lock.Exit(); - } - var ids = new int[endIndex - startIndex]; - for (int i = startIndex, j = 0; i < endIndex; i++, j++) - { - ids[j] = i; - } - return ids; - } - - public void Deserialize(IBinaryReader reader) - { - int count = reader.ReadInt32(); - _set = new List(count + 1); - for (int i = 0; i < count; i++) - { - _set.Add(reader.ReadCompatible()); - } - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteInt32(_set.Count); - foreach (var r in _set) - { - writer.WriteCompatible(r); - } - } - - public IEnumerator GetEnumerator() - { - return _set.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _set.GetEnumerator(); - } - } -} diff --git a/ZeroLevel.HNSW/SmallWorld.cs b/ZeroLevel.HNSW/SmallWorld.cs deleted file mode 100644 index c7295cc..0000000 --- a/ZeroLevel.HNSW/SmallWorld.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using ZeroLevel.HNSW.Services; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.HNSW -{ - public class SmallWorld - { - private readonly NSWOptions _options; - private VectorSet _vectors; - private Layer[] _layers; - private int EntryPoint = 0; - private int MaxLayer = 0; - private readonly ProbabilityLayerNumberGenerator _layerLevelGenerator; - private ReaderWriterLockSlim _lockGraph = new ReaderWriterLockSlim(); - - public readonly Func DistanceFunction; - public TItem GetVector(int id) => _vectors[id]; - public IDictionary> GetLinks() => _layers[0].Links; - - public SmallWorld(NSWOptions options) - { - _options = options; - _vectors = new VectorSet(); - _layers = new Layer[_options.LayersCount]; - _layerLevelGenerator = new ProbabilityLayerNumberGenerator(_options.LayersCount, _options.M); - - DistanceFunction = new Func((id1, id2) => _options.Distance(_vectors[id1], _vectors[id2])); - - for (int i = 0; i < _options.LayersCount; i++) - { - _layers[i] = new Layer(_options, _vectors, i == 0); - } - } - - public SmallWorld(NSWOptions options, Stream stream) - { - _options = options; - _layerLevelGenerator = new ProbabilityLayerNumberGenerator(_options.LayersCount, _options.M); - DistanceFunction = new Func((id1, id2) => _options.Distance(_vectors[id1], _vectors[id2])); - Deserialize(stream); - } - - /// - /// Search in the graph K for vectors closest to a given vector - /// - /// Given vector - /// Count of elements for search - /// - /// - public IEnumerable<(int, TItem, float)> Search(TItem vector, int k) - { - foreach (var pair in KNearest(vector, k)) - { - yield return (pair.Item1, _vectors[pair.Item1], pair.Item2); - } - } - - public IEnumerable<(int, TItem, float)> Search(TItem vector, int k, SearchContext context) - { - if (context == null) - { - foreach (var pair in KNearest(vector, k)) - { - yield return (pair.Item1, _vectors[pair.Item1], pair.Item2); - } - } - else - { - foreach (var pair in KNearest(vector, k, context)) - { - yield return (pair.Item1, _vectors[pair.Item1], pair.Item2); - } - } - } - - public IEnumerable<(int, TItem, float)> Search(int k, SearchContext context) - { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - else - { - foreach (var pair in KNearest(k, context)) - { - yield return (pair.Item1, _vectors[pair.Item1], pair.Item2); - } - } - } - - /// - /// Adding vectors batch - /// - /// Vectors - /// Vector identifiers in a graph - public int[] AddItems(IEnumerable vectors) - { - _lockGraph.EnterWriteLock(); - try - { - var ids = _vectors.Append(vectors); - for (int i = 0; i < ids.Length; i++) - { - INSERT(ids[i]); - } - return ids; - } - finally - { - _lockGraph.ExitWriteLock(); - } - } - - #region https://arxiv.org/ftp/arxiv/papers/1603/1603.09320.pdf - /// - /// Algorithm 1 - /// - private void INSERT(int q) - { - var distance = new Func(candidate => _options.Distance(_vectors[q], _vectors[candidate])); - // W ← ∅ // list for the currently found nearest elements - var W = new MinHeap(_options.EFConstruction + 1); - // ep ← get enter point for hnsw - var ep = _layers[MaxLayer].FindEntryPointAtLayer(distance); - if (ep == -1) - ep = EntryPoint; - - var epDist = distance(ep); - // L ← level of ep // top layer for hnsw - var L = MaxLayer; - // l ← ⌊-ln(unif(0..1))∙mL⌋ // new element’s level - int l = _layerLevelGenerator.GetRandomLayer(); - - // Проход с верхнего уровня до уровня где появляется элемент, для нахождения точки входа - int id; - float value; - // for lc ← L … l+1 - for (int lc = L; lc > l; --lc) - { - // W ← SEARCH-LAYER(q, ep, ef = 1, lc) - foreach (var i in _layers[lc].KNearestAtLayer(ep, distance, 1)) - { - W.Push(i); - } - // ep ← get the nearest element from W to q - if (W.TryPeek(out id, out value)) - { - ep = id; - epDist = value; - } - W.Clear(); - } - //for lc ← min(L, l) … 0 - // connecting new node to the small world - for (int lc = Math.Min(L, l); lc >= 0; --lc) - { - _layers[lc].Push(q, ep, W, distance); - // ep ← W - if (W.TryPeek(out id, out value)) - { - ep = id; - epDist = value; - } - W.Clear(); - } - // if l > L - if (l > L) - { - // set enter point for hnsw to q - L = l; - MaxLayer = l; - EntryPoint = ep; - } - } - - public void TestWorld() - { - for (var v = 0; v < _vectors.Count; v++) - { - var nearest = _layers[0][v].ToArray(); - if (nearest.Length > _layers[0].M) - { - Console.WriteLine($"V{v}. Count of links ({nearest.Length}) more than max ({_layers[0].M})"); - } - } - // coverage test - var ep = 0; - var visited = new HashSet(); - var next = new Stack(); - next.Push(ep); - while (next.Count > 0) - { - ep = next.Pop(); - visited.Add(ep); - foreach (var n in _layers[0].GetNeighbors(ep)) - { - if (visited.Contains(n) == false) - { - next.Push(n); - } - } - } - if (visited.Count != _vectors.Count) - { - Console.Write($"Vectors count ({_vectors.Count}) less than BFS visited nodes count ({visited.Count})"); - } - } - - /// - /// Algorithm 5 - /// - private IEnumerable<(int, float)> KNearest(TItem q, int k) - { - _lockGraph.EnterReadLock(); - try - { - if (_vectors.Count == 0) - { - return Enumerable.Empty<(int, float)>(); - } - - int id; - float value; - var distance = new Func(candidate => _options.Distance(q, _vectors[candidate])); - - // W ← ∅ // set for the current nearest elements - var W = new MinHeap(k + 1); - // ep ← get enter point for hnsw - var ep = EntryPoint; - // L ← level of ep // top layer for hnsw - var L = MaxLayer; - // for lc ← L … 1 - for (int layer = L; layer > 0; --layer) - { - // W ← SEARCH-LAYER(q, ep, ef = 1, lc) - foreach (var i in _layers[layer].KNearestAtLayer(ep, distance, 1)) - { - W.Push(i); - } - // ep ← get nearest element from W to q - if (W.TryPeek(out id, out value)) - { - ep = id; - } - W.Clear(); - } - // W ← SEARCH-LAYER(q, ep, ef, lc =0) - foreach (var i in _layers[0].KNearestAtLayer(ep, distance, k)) - { - W.Push(i); - } - // return K nearest elements from W to q - return W; - } - finally - { - _lockGraph.ExitReadLock(); - } - } - - private IEnumerable<(int, float)> KNearest(TItem q, int k, SearchContext context) - { - _lockGraph.EnterReadLock(); - try - { - if (_vectors.Count == 0) - { - return Enumerable.Empty<(int, float)>(); - } - - int id; - float value; - var distance = new Func(candidate => _options.Distance(q, _vectors[candidate])); - - // W ← ∅ // set for the current nearest elements - var W = new MinHeap(k + 1); - // ep ← get enter point for hnsw - var ep = EntryPoint; - // L ← level of ep // top layer for hnsw - var L = MaxLayer; - // for lc ← L … 1 - for (int layer = L; layer > 0; --layer) - { - // W ← SEARCH-LAYER(q, ep, ef = 1, lc) - foreach (var i in _layers[layer].KNearestAtLayer(ep, distance, 1)) - { - W.Push(i); - } - // ep ← get nearest element from W to q - if (W.TryPeek(out id, out value)) - { - ep = id; - } - W.Clear(); - } - // W ← SEARCH-LAYER(q, ep, ef, lc =0) - foreach (var i in _layers[0].KNearestAtLayer(ep, distance, k, context)) - { - W.Push(i); - } - // return K nearest elements from W to q - return W; - } - finally - { - _lockGraph.ExitReadLock(); - } - } - - private IEnumerable<(int, float)> KNearest(int k, SearchContext context) - { - _lockGraph.EnterReadLock(); - try - { - if (_vectors.Count == 0) - { - return Enumerable.Empty<(int, float)>(); - } - // W ← ∅ // set for the current nearest elements - var W = new MinHeap(k + 1); - // W ← SEARCH-LAYER(q, ep, ef, lc =0) - foreach (var i in _layers[0].KNearestAtLayer(k, context)) - { - W.Push(i); - } - // return K nearest elements from W to q - return W; - } - finally - { - _lockGraph.ExitReadLock(); - } - } - - #endregion - - public void Serialize(Stream stream) - { - using (var writer = new MemoryStreamWriter(stream)) - { - writer.WriteInt32(EntryPoint); - writer.WriteInt32(MaxLayer); - _vectors.Serialize(writer); - writer.WriteInt32(_layers.Length); - foreach (var l in _layers) - { - l.Serialize(writer); - } - } - } - - public void Deserialize(Stream stream) - { - using (var reader = new MemoryStreamReader(stream)) - { - this.EntryPoint = reader.ReadInt32(); - this.MaxLayer = reader.ReadInt32(); - _vectors = new VectorSet(); - _vectors.Deserialize(reader); - var countLayers = reader.ReadInt32(); - _layers = new Layer[countLayers]; - for (int i = 0; i < countLayers; i++) - { - _layers[i] = new Layer(_options, _vectors, i == 0); - _layers[i].Deserialize(reader); - } - } - } - } -} diff --git a/ZeroLevel.HNSW/SmallWorldFactory.cs b/ZeroLevel.HNSW/SmallWorldFactory.cs deleted file mode 100644 index 4ec5a65..0000000 --- a/ZeroLevel.HNSW/SmallWorldFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO; - -namespace ZeroLevel.HNSW -{ - public static class SmallWorld - { - public static SmallWorld CreateWorld(NSWOptions options) - => new SmallWorld(options); - public static SmallWorld CreateWorldFrom(NSWOptions options, Stream stream) - => new SmallWorld(options, stream); - } -} diff --git a/ZeroLevel.HNSW/Utils/CosineDistance.cs b/ZeroLevel.HNSW/Utils/CosineDistance.cs deleted file mode 100644 index 9494676..0000000 --- a/ZeroLevel.HNSW/Utils/CosineDistance.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace ZeroLevel.HNSW -{ - /// - /// Calculates cosine similarity. - /// - /// - /// Intuition behind selecting float as a carrier. - /// - /// 1. In practice we work with vectors of dimensionality 100 and each component has value in range [-1; 1] - /// There certainly is a possibility of underflow. - /// But we assume that such cases are rare and we can rely on such underflow losses. - /// - /// 2. According to the article http://www.ti3.tuhh.de/paper/rump/JeaRu13.pdf - /// the floating point rounding error is less then 100 * 2^-24 * sqrt(100) * sqrt(100) < 0.0005960 - /// We deem such precision is satisfactory for out needs. - /// - public static class CosineDistance - { - /// - /// Calculates cosine distance with assumption that u and v are unit vectors. - /// - /// Left vector. - /// Right vector. - /// Cosine distance between u and v. - public static float ForUnits(float[] u, float[] v) - { - if (u.Length != v.Length) - { - throw new ArgumentException("Vectors have non-matching dimensions"); - } - - float dot = 0; - for (int i = 0; i < u.Length; ++i) - { - dot += u[i] * v[i]; - } - - return 1 - dot; - } - - /// - /// Calculates cosine distance optimized using SIMD instructions. - /// - /// Left vector. - /// Right vector. - /// Cosine distance between u and v. - public static float SIMD(float[] u, float[] v) - { - if (!Vector.IsHardwareAccelerated) - { - throw new NotSupportedException($"SIMD version of {nameof(CosineDistance)} is not supported"); - } - - if (u.Length != v.Length) - { - throw new ArgumentException("Vectors have non-matching dimensions"); - } - - float dot = 0; - var norm = default(Vector2); - int step = Vector.Count; - - int i, to = u.Length - step; - for (i = 0; i <= to; i += step) - { - var ui = new Vector(u, i); - var vi = new Vector(v, i); - dot += Vector.Dot(ui, vi); - norm.X += Vector.Dot(ui, ui); - norm.Y += Vector.Dot(vi, vi); - } - - for (; i < u.Length; ++i) - { - dot += u[i] * v[i]; - norm.X += u[i] * u[i]; - norm.Y += v[i] * v[i]; - } - - norm = Vector2.SquareRoot(norm); - float n = (norm.X * norm.Y); - - if (n == 0) - { - return 1f; - } - - var similarity = dot / n; - return 1f - similarity; - } - - /// - /// Calculates cosine distance with assumption that u and v are unit vectors using SIMD instructions. - /// - /// Left vector. - /// Right vector. - /// Cosine distance between u and v. - public static float SIMDForUnits(float[] u, float[] v) - { - return 1f - DotProduct(ref u, ref v); - } - - private static readonly int _vs1 = Vector.Count; - private static readonly int _vs2 = 2 * Vector.Count; - private static readonly int _vs3 = 3 * Vector.Count; - private static readonly int _vs4 = 4 * Vector.Count; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float DotProduct(ref float[] lhs, ref float[] rhs) - { - float result = 0f; - - var count = lhs.Length; - var offset = 0; - - while (count >= _vs4) - { - result += Vector.Dot(new Vector(lhs, offset), new Vector(rhs, offset)); - result += Vector.Dot(new Vector(lhs, offset + _vs1), new Vector(rhs, offset + _vs1)); - result += Vector.Dot(new Vector(lhs, offset + _vs2), new Vector(rhs, offset + _vs2)); - result += Vector.Dot(new Vector(lhs, offset + _vs3), new Vector(rhs, offset + _vs3)); - if (count == _vs4) return result; - count -= _vs4; - offset += _vs4; - } - - if (count >= _vs2) - { - result += Vector.Dot(new Vector(lhs, offset), new Vector(rhs, offset)); - result += Vector.Dot(new Vector(lhs, offset + _vs1), new Vector(rhs, offset + _vs1)); - if (count == _vs2) return result; - count -= _vs2; - offset += _vs2; - } - if (count >= _vs1) - { - result += Vector.Dot(new Vector(lhs, offset), new Vector(rhs, offset)); - if (count == _vs1) return result; - count -= _vs1; - offset += _vs1; - } - if (count > 0) - { - while (count > 0) - { - result += lhs[offset] * rhs[offset]; - offset++; count--; - } - } - return result; - } - } -} diff --git a/ZeroLevel.HNSW/Utils/FastRandom.cs b/ZeroLevel.HNSW/Utils/FastRandom.cs deleted file mode 100644 index 74ab8d5..0000000 --- a/ZeroLevel.HNSW/Utils/FastRandom.cs +++ /dev/null @@ -1,507 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace ZeroLevel.HNSW -{ - public sealed class DefaultRandomGenerator - { - /// - /// This is the default configuration (it supports the optimization process to be executed on multiple threads) - /// - public static DefaultRandomGenerator Instance { get; } = new DefaultRandomGenerator(allowParallel: true); - - /// - /// This uses the same random number generator but forces the optimization process to run on a single thread (which may be desirable if multiple requests may be processed concurrently - /// or if it is otherwise not desirable to let a single request access all of the CPUs) - /// - public static DefaultRandomGenerator DisableThreading { get; } = new DefaultRandomGenerator(allowParallel: false); - - private DefaultRandomGenerator(bool allowParallel) => IsThreadSafe = allowParallel; - - public bool IsThreadSafe { get; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Next(int minValue, int maxValue) => ThreadSafeFastRandom.Next(minValue, maxValue); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float NextFloat() => ThreadSafeFastRandom.NextFloat(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void NextFloats(Span buffer) => ThreadSafeFastRandom.NextFloats(buffer); - } - - internal static class ThreadSafeFastRandom - { - private static readonly Random _global = new Random(); - - [ThreadStatic] - private static FastRandom _local; - - private static int GetGlobalSeed() - { - int seed; - lock (_global) - { - seed = _global.Next(); - } - return seed; - } - - /// - /// Returns a non-negative random integer. - /// - /// A 32-bit signed integer that is greater than or equal to 0 and less than System.Int32.MaxValue. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Next() - { - var inst = _local; - if (inst == null) - { - int seed; - seed = GetGlobalSeed(); - _local = inst = new FastRandom(seed); - } - return inst.Next(); - } - - /// - /// Returns a non-negative random integer that is less than the specified maximum. - /// - /// The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to 0. - /// A 32-bit signed integer that is greater than or equal to 0, and less than maxValue; that is, the range of return values ordinarily includes 0 but not maxValue. However, - // if maxValue equals 0, maxValue is returned. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Next(int maxValue) - { - var inst = _local; - if (inst == null) - { - int seed; - seed = GetGlobalSeed(); - _local = inst = new FastRandom(seed); - } - int ans; - do - { - ans = inst.Next(maxValue); - } while (ans == maxValue); - - return ans; - } - - /// - /// Returns a random integer that is within a specified range. - /// - /// The inclusive lower bound of the random number returned. - /// The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. - /// A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue - // equals maxValue, minValue is returned. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Next(int minValue, int maxValue) - { - var inst = _local; - if (inst == null) - { - int seed; - seed = GetGlobalSeed(); - _local = inst = new FastRandom(seed); - } - return inst.Next(minValue, maxValue); - } - - /// - /// Generates a random float. Values returned are from 0.0 up to but not including 1.0. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float NextFloat() - { - var inst = _local; - if (inst == null) - { - int seed; - seed = GetGlobalSeed(); - _local = inst = new FastRandom(seed); - } - return inst.NextFloat(); - } - - /// - /// Fills the elements of a specified array of bytes with random numbers. - /// - /// An array of bytes to contain random numbers. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NextFloats(Span buffer) - { - var inst = _local; - if (inst == null) - { - int seed; - seed = GetGlobalSeed(); - _local = inst = new FastRandom(seed); - } - inst.NextFloats(buffer); - } - } - - /// - /// A fast random number generator for .NET, from https://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random - /// Colin Green, January 2005 - /// - /// September 4th 2005 - /// Added NextBytesUnsafe() - commented out by default. - /// Fixed bug in Reinitialise() - y,z and w variables were not being reset. - /// - /// Key points: - /// 1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in: - /// Marsaglia, George. (2003). Xorshift RNGs. - /// http://www.jstatsoft.org/v08/i14/xorshift.pdf - /// - /// This particular implementation of xorshift has a period of 2^128-1. See the above paper to see - /// how this can be easily extened if you need a longer period. At the time of writing I could find no - /// information on the period of System.Random for comparison. - /// - /// 2) Faster than System.Random. Up to 8x faster, depending on which methods are called. - /// - /// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random - /// does plus some additional methods. The like named methods are functionally equivalent. - /// - /// 4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction - /// time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement - /// if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same - /// sequence many times. An alternative might be to cache random numbers in an array, but that approach is limited - /// by memory capacity and the fact that you may also want a large number of different sequences cached. Each sequence - /// can each be represented by a single seed value (int) when using FastRandom. - /// - /// Notes. - /// A further performance improvement can be obtained by declaring local variables as static, thus avoiding - /// re-allocation of variables on each call. However care should be taken if multiple instances of - /// FastRandom are in use or if being used in a multi-threaded environment. - /// - /// - internal class FastRandom - { - // The +1 ensures NextDouble doesn't generate 1.0 - const float FLOAT_UNIT_INT = 1.0f / ((float)int.MaxValue + 1.0f); - - const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0); - const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0); - const uint Y = 842502087, Z = 3579807591, W = 273326509; - - uint x, y, z, w; - - /// - /// Initialises a new instance using time dependent seed. - /// - public FastRandom() - { - // Initialise using the system tick count. - Reinitialise(Environment.TickCount); - } - - /// - /// Initialises a new instance using an int value as seed. - /// This constructor signature is provided to maintain compatibility with - /// System.Random - /// - public FastRandom(int seed) - { - Reinitialise(seed); - } - - /// - /// Reinitialises using an int value as a seed. - /// - public void Reinitialise(int seed) - { - // The only stipulation stated for the xorshift RNG is that at least one of - // the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing - // resetting of the x seed - x = (uint)seed; - y = Y; - z = Z; - w = W; - } - - /// - /// Generates a random int over the range 0 to int.MaxValue-1. - /// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next(). - /// This does slightly eat into some of the performance gain over System.Random, but not much. - /// For better performance see: - /// - /// Call NextInt() for an int over the range 0 to int.MaxValue. - /// - /// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range - /// including negative values. - /// - public int Next() - { - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - // Handle the special case where the value int.MaxValue is generated. This is outside of - // the range of permitted values, so we therefore call Next() to try again. - uint rtn = w & 0x7FFFFFFF; - if (rtn == 0x7FFFFFFF) - return Next(); - return (int)rtn; - } - - /// - /// Generates a random int over the range 0 to upperBound-1, and not including upperBound. - /// - public int Next(int upperBound) - { - if (upperBound < 0) - throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0"); - - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - - // The explicit int cast before the first multiplication gives better performance. - // See comments in NextDouble. - return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound); - } - - /// - /// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound. - /// upperBound must be >= lowerBound. lowerBound may be negative. - /// - public int Next(int lowerBound, int upperBound) - { - if (lowerBound > upperBound) - throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound"); - - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - - // The explicit int cast before the first multiplication gives better performance. - // See comments in NextDouble. - int range = upperBound - lowerBound; - if (range < 0) - { // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower). - // We also must use all 32 bits of precision, instead of the normal 31, which again is slower. - return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound)); - } - - // 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain - // a little more performance. - return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range); - } - - /// - /// Generates a random double. Values returned are from 0.0 up to but not including 1.0. - /// - public double NextDouble() - { - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - - // Here we can gain a 2x speed improvement by generating a value that can be cast to - // an int instead of the more easily available uint. If we then explicitly cast to an - // int the compiler will then cast the int to a double to perform the multiplication, - // this final cast is a lot faster than casting from a uint to a double. The extra cast - // to an int is very fast (the allocated bits remain the same) and so the overall effect - // of the extra cast is a significant performance improvement. - // - // Also note that the loss of one bit of precision is equivalent to what occurs within - // System.Random. - return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))); - } - - /// - /// Generates a random double. Values returned are from 0.0 up to but not including 1.0. - /// - public float NextFloat() - { - uint x = this.x, y = this.y, z = this.z, w = this.w; - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - var value = FLOAT_UNIT_INT * (int)(0x7FFFFFFF & w); - this.x = x; this.y = y; this.z = z; this.w = w; - return value; - } - - /// - /// Fills the provided byte array with random floats. - /// - public void NextFloats(Span buffer) - { - uint x = this.x, y = this.y, z = this.z, w = this.w; - int i = 0; - uint t; - for (int bound = buffer.Length; i < bound;) - { - t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - buffer[i++] = FLOAT_UNIT_INT * (int)(0x7FFFFFFF & w); - } - - this.x = x; this.y = y; this.z = z; this.w = w; - } - - - /// - /// Fills the provided byte array with random bytes. - /// This method is functionally equivalent to System.Random.NextBytes(). - /// - public void NextBytes(byte[] buffer) - { - // Fill up the bulk of the buffer in chunks of 4 bytes at a time. - uint x = this.x, y = this.y, z = this.z, w = this.w; - int i = 0; - uint t; - for (int bound = buffer.Length - 3; i < bound;) - { - // Generate 4 bytes. - // Increased performance is achieved by generating 4 random bytes per loop. - // Also note that no mask needs to be applied to zero out the higher order bytes before - // casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out. - t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - buffer[i++] = (byte)w; - buffer[i++] = (byte)(w >> 8); - buffer[i++] = (byte)(w >> 16); - buffer[i++] = (byte)(w >> 24); - } - - // Fill up any remaining bytes in the buffer. - if (i < buffer.Length) - { - // Generate 4 bytes. - t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - buffer[i++] = (byte)w; - if (i < buffer.Length) - { - buffer[i++] = (byte)(w >> 8); - if (i < buffer.Length) - { - buffer[i++] = (byte)(w >> 16); - if (i < buffer.Length) - { - buffer[i] = (byte)(w >> 24); - } - } - } - } - this.x = x; this.y = y; this.z = z; this.w = w; - } - - /// - /// Fills the provided byte array with random bytes. - /// This method is functionally equivalent to System.Random.NextBytes(). - /// - public void NextBytes(Span buffer) - { - // Fill up the bulk of the buffer in chunks of 4 bytes at a time. - uint x = this.x, y = this.y, z = this.z, w = this.w; - int i = 0; - uint t; - for (int bound = buffer.Length - 3; i < bound;) - { - // Generate 4 bytes. - // Increased performance is achieved by generating 4 random bytes per loop. - // Also note that no mask needs to be applied to zero out the higher order bytes before - // casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out. - t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - buffer[i++] = (byte)w; - buffer[i++] = (byte)(w >> 8); - buffer[i++] = (byte)(w >> 16); - buffer[i++] = (byte)(w >> 24); - } - - // Fill up any remaining bytes in the buffer. - if (i < buffer.Length) - { - // Generate 4 bytes. - t = (x ^ (x << 11)); - x = y; y = z; z = w; - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - buffer[i++] = (byte)w; - if (i < buffer.Length) - { - buffer[i++] = (byte)(w >> 8); - if (i < buffer.Length) - { - buffer[i++] = (byte)(w >> 16); - if (i < buffer.Length) - { - buffer[i] = (byte)(w >> 24); - } - } - } - } - this.x = x; this.y = y; this.z = z; this.w = w; - } - - /// - /// Generates a uint. Values returned are over the full range of a uint, - /// uint.MinValue to uint.MaxValue, inclusive. - /// - /// This is the fastest method for generating a single random number because the underlying - /// random number generator algorithm generates 32 random bits that can be cast directly to - /// a uint. - /// - public uint NextUInt() - { - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))); - } - - /// - /// Generates a random int over the range 0 to int.MaxValue, inclusive. - /// This method differs from Next() only in that the range is 0 to int.MaxValue - /// and not 0 to int.MaxValue-1. - /// - /// The slight difference in range means this method is slightly faster than Next() - /// but is not functionally equivalent to System.Random.Next(). - /// - public int NextInt() - { - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))); - } - - - // Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned - // with bitBufferIdx. - uint bitBuffer; - uint bitMask = 1; - - /// - /// Generates a single random bit. - /// This method's performance is improved by generating 32 bits in one operation and storing them - /// ready for future calls. - /// - public bool NextBool() - { - if (bitMask == 1) - { - // Generate 32 more bits. - uint t = (x ^ (x << 11)); - x = y; y = z; z = w; - bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); - - // Reset the bitMask that tells us which bit to read next. - bitMask = 0x80000000; - return (bitBuffer & bitMask) == 0; - } - - return (bitBuffer & (bitMask >>= 1)) == 0; - } - } -} diff --git a/ZeroLevel.HNSW/Utils/ProbabilityLayerNumberGenerator.cs b/ZeroLevel.HNSW/Utils/ProbabilityLayerNumberGenerator.cs deleted file mode 100644 index 89a4a7c..0000000 --- a/ZeroLevel.HNSW/Utils/ProbabilityLayerNumberGenerator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW.Services -{ - internal sealed class ProbabilityLayerNumberGenerator - { - private readonly float[] _probabilities; - - internal ProbabilityLayerNumberGenerator(int maxLayers, int M) - { - _probabilities = new float[maxLayers]; - var m_L = 1.0f / Math.Log(M); - for (int i = 0; i < maxLayers; i++) - { - _probabilities[i] = (float)(Math.Exp(-i / m_L) * (1 - Math.Exp(-1 / m_L))); - } - } - - internal int GetRandomLayer() - { - var probability = DefaultRandomGenerator.Instance.NextFloat(); - for (int i = 0; i < _probabilities.Length; i++) - { - if (probability > _probabilities[i]) - return i; - } - return 0; - } - } -} diff --git a/ZeroLevel.HNSW/Utils/VectorUtils.cs b/ZeroLevel.HNSW/Utils/VectorUtils.cs deleted file mode 100644 index 4a96c9e..0000000 --- a/ZeroLevel.HNSW/Utils/VectorUtils.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; - -namespace ZeroLevel.HNSW -{ - public static class VectorUtils - { - public static List RandomVectors(int vectorSize, int vectorsCount) - { - var vectors = new List(); - for (int i = 0; i < vectorsCount; i++) - { - var vector = new float[vectorSize]; - DefaultRandomGenerator.Instance.NextFloats(vector); - VectorUtils.NormalizeSIMD(vector); - vectors.Add(vector); - } - return vectors; - } - - public static float Magnitude(IList vector) - { - float magnitude = 0.0f; - for (int i = 0; i < vector.Count; ++i) - { - magnitude += vector[i] * vector[i]; - } - - return (float)Math.Sqrt(magnitude); - } - - public static void Normalize(IList vector) - { - float normFactor = 1f / Magnitude(vector); - for (int i = 0; i < vector.Count; ++i) - { - vector[i] *= normFactor; - } - } - - public static float MagnitudeSIMD(float[] vector) - { - if (!Vector.IsHardwareAccelerated) - { - throw new NotSupportedException($"{nameof(VectorUtils.NormalizeSIMD)} is not supported"); - } - - float magnitude = 0.0f; - int step = Vector.Count; - - int i, to = vector.Length - step; - for (i = 0; i <= to; i += Vector.Count) - { - var vi = new Vector(vector, i); - magnitude += Vector.Dot(vi, vi); - } - - for (; i < vector.Length; ++i) - { - magnitude += vector[i] * vector[i]; - } - - return (float)Math.Sqrt(magnitude); - } - - public static void NormalizeSIMD(float[] vector) - { - if (!Vector.IsHardwareAccelerated) - { - throw new NotSupportedException($"{nameof(VectorUtils.NormalizeSIMD)} is not supported"); - } - - float normFactor = 1f / MagnitudeSIMD(vector); - int step = Vector.Count; - - int i, to = vector.Length - step; - for (i = 0; i <= to; i += step) - { - var vi = new Vector(vector, i); - vi = Vector.Multiply(normFactor, vi); - vi.CopyTo(vector, i); - } - - for (; i < vector.Length; ++i) - { - vector[i] *= normFactor; - } - } - } -} diff --git a/ZeroLevel.HNSW/Utils/VisitedBitSet.cs b/ZeroLevel.HNSW/Utils/VisitedBitSet.cs deleted file mode 100644 index 2e2469b..0000000 --- a/ZeroLevel.HNSW/Utils/VisitedBitSet.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace ZeroLevel.HNSW -{ - public class VisitedBitSet - { - // bit map - private int[] Buffer; - - public VisitedBitSet(int nodesCount, int M) - { - Buffer = new int[(nodesCount >> 5) + M + 1]; - } - - public bool Contains(int nodeId) - { - int carrier = Buffer[nodeId >> 5]; - return ((1 << (nodeId & 31)) & carrier) != 0; - } - - public void Add(int nodeId) - { - int mask = 1 << (nodeId & 31); - Buffer[nodeId >> 5] |= mask; - } - - public void Clear() - { - Array.Clear(Buffer, 0, Buffer.Length); - } - } -} diff --git a/ZeroLevel.HNSW/ZeroLevel.HNSW.csproj b/ZeroLevel.HNSW/ZeroLevel.HNSW.csproj deleted file mode 100644 index 63d56ea..0000000 --- a/ZeroLevel.HNSW/ZeroLevel.HNSW.csproj +++ /dev/null @@ -1,51 +0,0 @@ - - - - net6.0 - AnyCPU;x64 - x64 - full - 1.0.0.5 - ogoun - Ogoun - Copyright Ogoun 2022 - https://github.com/ogoun/Zero/wiki - zero.png - https://github.com/ogoun/Zero - git - - MIT - - - - False - - - - False - - - - True - - - - True - - - - - True - \ - - - - - - - - - - - - diff --git a/ZeroLevel.Logger/ZeroLevel.Logger.csproj b/ZeroLevel.Logger/ZeroLevel.Logger.csproj index 21317bc..f9b6f57 100644 --- a/ZeroLevel.Logger/ZeroLevel.Logger.csproj +++ b/ZeroLevel.Logger/ZeroLevel.Logger.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 AnyCPU;x64;x86 diff --git a/ZeroLevel.ML/CameraPixelSizes.cs b/ZeroLevel.ML/CameraPixelSizes.cs new file mode 100644 index 0000000..2f2ae49 --- /dev/null +++ b/ZeroLevel.ML/CameraPixelSizes.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace ZeroLevel.ML +{ + public static class CameraPixelSizes + { + /// + /// В микрометрах + /// + private static Dictionary _pixels = new Dictionary + { + { "ZenmuseP1", 4.4d }, + { "M3E", 3.35821d }, + { "L1D-20c", 2.41d }, + { "F230", 1.55d }, + { "FC3411", 2.4d }, + { "XT702", 2.4d }, + { "FC7303", 1.334d}, + }; + + public static double GetPixelSizeByModel(string model) + { + if (_pixels.ContainsKey(model)) + { + return _pixels[model]; + } + return 3.3d; + } + } +} diff --git a/ZeroLevel.ML/Clusterization/FeatureCluster.cs b/ZeroLevel.ML/Clusterization/FeatureCluster.cs new file mode 100644 index 0000000..aa0ece1 --- /dev/null +++ b/ZeroLevel.ML/Clusterization/FeatureCluster.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ZeroLevel.ML +{ + public class FeatureCluster + { + private readonly List _features = new List(); + private readonly Func _vectorExtractor; + + public float[] CenterOfMass => _centerOfMass; + + private float[] _centerOfMass; + + public FeatureCluster(Func vectorExtractor) + { + _vectorExtractor = vectorExtractor; + } + + public IList Features => _features; + + internal void Append(T item) + { + _features.Add(item); + _centerOfMass = _vectorExtractor.Invoke(_features[0]); + if (_features.Count > 1) + { + foreach (var f in _features.Skip(1)) + { + var f_vector = _vectorExtractor(f); + for (int i = 0; i < f_vector.Length; i++) + { + _centerOfMass[i] += f_vector[i]; + } + } + for (int i = 0; i < _centerOfMass.Length; i++) + { + _centerOfMass[i] /= _features.Count; + } + } + } + public bool IsNeighbor(T feature, Func similarityFunction, float threshold) + { + if (_features.Count == 0) return true; + var similarity = similarityFunction(_vectorExtractor(feature), _centerOfMass); + return similarity <= threshold; + } + } +} diff --git a/ZeroLevel.ML/Clusterization/FeatureClusterBulder.cs b/ZeroLevel.ML/Clusterization/FeatureClusterBulder.cs new file mode 100644 index 0000000..7b5b2d7 --- /dev/null +++ b/ZeroLevel.ML/Clusterization/FeatureClusterBulder.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace ZeroLevel.ML +{ + public class FeatureClusterBulder + { + public FeatureClusterCollection Build(IEnumerable items, Func vectorExtractor, Func similarityFunction, float threshold) + { + var collection = new FeatureClusterCollection(); + foreach (var item in items) + { + bool isAdded = false; + foreach (var cluster in collection.Clusters) + { + if (cluster.Value.IsNeighbor(item, similarityFunction, threshold)) + { + cluster.Value.Append(item); + isAdded = true; + break; + } + } + if (false == isAdded) + { + var cluster = new FeatureCluster(vectorExtractor); + cluster.Append(item); + collection.Add(cluster); + } + } + return collection; + } + } +} diff --git a/ZeroLevel.ML/Clusterization/FeatureClusterCollection.cs b/ZeroLevel.ML/Clusterization/FeatureClusterCollection.cs new file mode 100644 index 0000000..d251a75 --- /dev/null +++ b/ZeroLevel.ML/Clusterization/FeatureClusterCollection.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace ZeroLevel.ML +{ + public class FeatureClusterCollection + { + private int _clusterKey = 0; + private IDictionary> _clusters = new Dictionary>(); + + public IDictionary> Clusters => _clusters; + + internal void Add(FeatureCluster cluster) + { + _clusters.Add(Interlocked.Increment(ref _clusterKey), cluster); + } + + public void RemoveByDistance(Func similarityFunction, Func, double> winnerValue, double distance) + { + bool removed = false; + do + { + removed = false; + var keys = _clusters.Keys.ToArray(); + + var to_remove = new HashSet(); + + for (int i = 0; i < keys.Length - 1; i++) + { + for (int j = i + 1; j < keys.Length; j++) + { + if (to_remove.Contains(j)) continue; + if(i == j) continue; + var ki = keys[i]; + var kj = keys[j]; + + var sim = similarityFunction.Invoke(_clusters[ki].CenterOfMass, _clusters[kj].CenterOfMass); + if (sim < distance) + { + var scorei = winnerValue(_clusters[ki]); + var scorej = winnerValue(_clusters[kj]); + if (scorei < scorej) + { + to_remove.Add(ki); + } + else + { + to_remove.Add(kj); + } + } + } + } + + if (to_remove.Any()) + { + removed = true; + foreach (var k in to_remove) + { + _clusters.Remove(k); + } + } + } while (removed == true); + } + } +} diff --git a/ZeroLevel.ML/DNN/Classify/EfficientnetLiteClassifier.cs b/ZeroLevel.ML/DNN/Classify/EfficientnetLiteClassifier.cs new file mode 100644 index 0000000..6a5773b --- /dev/null +++ b/ZeroLevel.ML/DNN/Classify/EfficientnetLiteClassifier.cs @@ -0,0 +1,103 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using ZeroLevel.ML.DNN.Models; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace ZeroLevel.ML.DNN.Classify +{ + public sealed class EfficientnetLiteClassifier + : SSDNN, IClassifier + { + public int InputSize => 224; + public float[] MEAN_RGB = new float[3] { 0.498f, 0.498f, 0.498f }; + public float[] STDDEV_RGB = new float[3] { 0.502f, 0.502f, 0.502f }; + + public float RNorm(float x) => ImageConverter.MeanStdNormilize(x, MEAN_RGB[0], STDDEV_RGB[0]); + public float GNorm(float x) => ImageConverter.MeanStdNormilize(x, MEAN_RGB[1], STDDEV_RGB[1]); + public float BNorm(float x) => ImageConverter.MeanStdNormilize(x, MEAN_RGB[2], STDDEV_RGB[2]); + + public EfficientnetLiteClassifier(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float[] Softmax(float[] input) + { + var sum = 0f; + var dst = new float[input.Length]; + for (var i = 0; i < input.Length; ++i) + { + var e = (float)Math.Exp(input[i]); + dst[i] = e; + sum += e; + } + var sumInv = 1f / sum; + for (var i = 0; i < input.Length; ++i) + dst[i] *= sumInv; + + return dst; + } + + public List Predict(FastTensorPool inputs) + { + var result = new List(); + Extract(new Dictionary> { { "input", inputs.Tensor } }, d => + { + Tensor output; + if (d.ContainsKey("output")) + { + output = d["output"]; + } + else + { + output = d.First().Value; + } + if (output != null && output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var scores = new float[output.Dimensions[1]]; + for (int objclass = 0; objclass < output.Dimensions[1]; objclass++) + { + scores[objclass] = output[tensorIndex, objclass]; + } + var probs = Softmax(scores); + result.Add(probs); + } + } + }); + return result; + } + + public List<(int, float)> DetectClass(FastTensorPool inputs) + { + var classes = new List<(int, float)>(); + var scores = Predict(inputs); + foreach (var score in scores) + { + if (score.Length > 0) + { + int index = 0; + float max = score[0]; + for (int i = 1; i < score.Length; i++) + { + if (score[i] > max) + { + max = score[i]; + index = i; + } + } + classes.Add((index, max)); + } + else + { + classes.Add((-1, 0f)); + } + } + return classes.OrderByDescending(x => x.Item2).ToList(); + } + } +} diff --git a/ZeroLevel.ML/DNN/Classify/IClassifier.cs b/ZeroLevel.ML/DNN/Classify/IClassifier.cs new file mode 100644 index 0000000..8182962 --- /dev/null +++ b/ZeroLevel.ML/DNN/Classify/IClassifier.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Classify +{ + public interface IClassifier + : IDisposable + { + float RNorm(float x); + float GNorm(float x); + float BNorm(float x); + int InputSize { get; } + List Predict(FastTensorPool inputs); + List<(int, float)> DetectClass(FastTensorPool inputs); + } +} diff --git a/ZeroLevel.ML/DNN/Classify/Yolov8Classifier.cs b/ZeroLevel.ML/DNN/Classify/Yolov8Classifier.cs new file mode 100644 index 0000000..831125f --- /dev/null +++ b/ZeroLevel.ML/DNN/Classify/Yolov8Classifier.cs @@ -0,0 +1,79 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using System.Linq; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Classify +{ + public class Yolov8Classifier + : SSDNN, IClassifier + { + public int InputSize => 224; + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public Yolov8Classifier(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + public List Predict(FastTensorPool inputs) + { + var result = new List(); + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output; + if (d.ContainsKey("output0")) + { + output = d["output0"]; + } + else + { + output = d.First().Value; + } + if (output != null && output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var scores = new float[output.Dimensions[1]]; + for (int objclass = 0; objclass < output.Dimensions[1]; objclass++) + { + scores[objclass] = output[tensorIndex, objclass]; + } + result.Add(scores); + } + } + }); + return result; + } + + public List<(int, float)> DetectClass(FastTensorPool inputs) + { + var classes = new List<(int, float)>(); + var scores = Predict(inputs); + foreach (var score in scores) + { + if (score.Length > 0) + { + int index = 0; + float max = score[0]; + for (int i = 1; i < score.Length; i++) + { + if (score[i] > max) + { + max = score[i]; + index = i; + } + } + classes.Add((index, max)); + } + else + { + classes.Add((-1, 0f)); + } + } + return classes; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/DamoYoloDetector.cs b/ZeroLevel.ML/DNN/Detectors/DamoYoloDetector.cs new file mode 100644 index 0000000..62729d1 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/DamoYoloDetector.cs @@ -0,0 +1,70 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class DamoYoloDetector + : SSDNN, IObjectDetector + { + public float BNorm(float x) => x;// ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => x;// ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => x;// ImageConverter.StandartNormalizator(x); + + public DamoYoloDetector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + + } + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor scores = d["scores"]; + Tensor boxes = d["boxes"]; + + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < scores.Dimensions[1]; box++) + { + var conf = scores[tensorIndex, box, 0]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var x1 = boxes[tensorIndex, box, 1]; + var y1 = boxes[tensorIndex, box, 0]; + var x2 = boxes[tensorIndex, box, 3]; + var y2 = boxes[tensorIndex, box, 2]; + + var cx = (x1 + x2) / 2; + var cy = (y1 + y2) / 2; + var w = x2 - x1; + var h = y2 - y1; + + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/DamodetDetector.cs b/ZeroLevel.ML/DNN/Detectors/DamodetDetector.cs new file mode 100644 index 0000000..cb447b6 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/DamodetDetector.cs @@ -0,0 +1,142 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + /// + /// DamoYolo and FastestDet models combination + /// + public class DamodetDetector + : SSDNN, IObjectDetector + { + private const float SIZE = 640; + + public DamodetDetector(string modelPath, int deviceId) + : base(modelPath, deviceId) + { + } + + public float RNorm(float x) => x; + public float BNorm(float x) => x; + public float GNorm(float x) => x; + + #region FastestDet + private static double sigmoid(double x) + { + return 1d / (1d + Math.Exp(-x)); + } + + private static double tanh(double x) + { + return 2d / (1d + Math.Exp(-2d * x)) - 1d; + } + + private void FastestDetPostprocess(FastTensorPool inputs, Tensor output, List result, float threshold) + { + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + var feature_map_height = output.Dimensions[2]; + var feature_map_width = output.Dimensions[3]; + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + + for (int h = 0; h < feature_map_height; h++) + { + for (int w = 0; w < feature_map_width; w++) + { + var obj_score = output[tensorIndex, 0, h, w]; + var cls_score = output[tensorIndex, 5, h, w]; + var score = Math.Pow(obj_score, 0.6) * Math.Pow(cls_score, 0.4); + if (score > threshold) + { + var x_offset = tanh(output[tensorIndex, 1, h, w]); + var y_offset = tanh(output[tensorIndex, 2, h, w]); + + var box_width = sigmoid(output[tensorIndex, 3, h, w]) * SIZE; + var box_height = sigmoid(output[tensorIndex, 4, h, w]) * SIZE; + + var box_cx = ((w + x_offset) / feature_map_width) * SIZE + tensor.StartX; + var box_cy = ((h + y_offset) / feature_map_height) * SIZE + tensor.StartY; + + result.Add(new YoloPrediction + { + Cx = (float)box_cx * relative_koef_x, + Cy = (float)box_cy * relative_koef_y, + W = (float)box_width * relative_koef_x, + H = (float)box_height * relative_koef_y, + Class = 0, + Score = (float)score + }); + } + } + } + } + } + #endregion + + #region DamoYolo + private void DamoYoloPostprocess(FastTensorPool inputs, Tensor scores, Tensor boxes, List result, float threshold) + { + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < scores.Dimensions[1]; box++) + { + var conf = scores[tensorIndex, box, 0]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var x1 = boxes[tensorIndex, box, 1]; + var y1 = boxes[tensorIndex, box, 0]; + var x2 = boxes[tensorIndex, box, 3]; + var y2 = boxes[tensorIndex, box, 2]; + + var cx = (x1 + x2) / 2; + var cy = (y1 + y2) / 2; + var w = x2 - x1; + var h = y2 - y1; + + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + #endregion + + private static float _fastest_threshold = 0.932f; + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor damo_scores = d["scores"]; + Tensor damo_boxes = d["boxes"]; + Tensor fastest_output = d["output"]; + + DamoYoloPostprocess(inputs, damo_scores, damo_boxes, result, threshold); + FastestDetPostprocess(inputs, fastest_output, result, _fastest_threshold); + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/Detector.cs b/ZeroLevel.ML/DNN/Detectors/Detector.cs new file mode 100644 index 0000000..70d9ab2 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/Detector.cs @@ -0,0 +1,76 @@ +extern alias CoreDrawing; + +using System.Collections.Generic; +using System.IO; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + internal sealed class Detector + : IDetector + { + private readonly IImageConverter _imageConverter; + private readonly IObjectDetector _model; + private readonly float _threshold; + private readonly bool _invertAxes = false; + internal Detector(IObjectDetector model, + float threshold, + ImageToTensorConversionOptions imageConverterOptions, + bool invertAxes = false) + { + _imageConverter = new ImageConverter(imageConverterOptions); + _model = model; + _threshold = threshold; + _invertAxes = invertAxes; + } + + public FastTensorPool CreateInput(string filePath) + { + FastTensorPool input; + if (_invertAxes) + input = _imageConverter.ImageToFastTensorsV2Inverted(filePath); + else + input = _imageConverter.ImageToFastTensorsV2(filePath); + input.Name = Path.GetFileNameWithoutExtension(filePath); + input.Path = filePath; + return input; + } + + public FastTensorPool CreateInput(CoreDrawing.System.Drawing.Bitmap image, string filePath = null!) + { + var input = _imageConverter.ImageToFastTensorsV2(image); + if (string.IsNullOrWhiteSpace(filePath) == false) + { + input.Name = Path.GetFileNameWithoutExtension(filePath); + input.Path = filePath; + } + return input; + } + + public List Detect(string filePath) + { + var input = _imageConverter.ImageToFastTensorsV2(filePath); + input.Name = Path.GetFileNameWithoutExtension(filePath); + input.Path = filePath; + return _model.Predict(input, _threshold); + } + + public List Detect(CoreDrawing.System.Drawing.Bitmap image, string filePath = null!) + { + var input = _imageConverter.ImageToFastTensorsV2(image); + if (string.IsNullOrWhiteSpace(filePath) == false) + { + input.Name = Path.GetFileNameWithoutExtension(filePath); + input.Path = filePath; + } + return _model.Predict(input, _threshold); + } + + public List Detect(FastTensorPool input) => _model.Predict(input, _threshold); + + public void Dispose() + { + _model.Dispose(); + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/DetectorFactory.cs b/ZeroLevel.ML/DNN/Detectors/DetectorFactory.cs new file mode 100644 index 0000000..fe328dc --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/DetectorFactory.cs @@ -0,0 +1,78 @@ +using System; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public static class DetectorFactory + { + public static IDetector Create(IObjectDetector model, float threshold, ImageToTensorConversionOptions imageConverterOptions) + { + return new Detector(model, threshold, imageConverterOptions); + } + + public static ObjectDetectionModels GetDetectorModel(string depectorPath) + { + var detectorType = ObjectDetectionModels.YoloV7; + if (depectorPath.Contains("nanodet", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.Nanodet; + } + else if (depectorPath.Contains("yolov8", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.YoloV8; + } + else if (depectorPath.Contains("yolov6", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.YoloV6; + } + else if (depectorPath.Contains("yolov5", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.YoloV5; + } + else if (depectorPath.Contains("mmyolo", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.MMYolo; + } + else if (depectorPath.Contains("damoyolo", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.DamoYolo; + } + else if (depectorPath.Contains("edgeyolo", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.EdgeYolo; + } + else if (depectorPath.Contains("fastestdet", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.FastestDet; + } + else if (depectorPath.Contains("damodet", StringComparison.OrdinalIgnoreCase)) + { + detectorType = ObjectDetectionModels.DamoDet; + } + + return detectorType; + } + + public static IDetector Create(ObjectDetectionModels modelType, float threshold, string modelPath, ImageToTensorConversionOptions imageConverterOptions, int deviceId = 0) + { + IObjectDetector model; + bool invertAxes = false; + switch (modelType) + { + case ObjectDetectionModels.YoloV5: { model = new Yolov5Detector(modelPath, deviceId); break; } + case ObjectDetectionModels.YoloV6: { model = new Yolov6Detector(modelPath, deviceId); break; } + case ObjectDetectionModels.YoloV7: { model = new Yolov7Detector(modelPath, deviceId); break; } + case ObjectDetectionModels.YoloV8: { model = new Yolov8Detector(modelPath, deviceId); break; } + case ObjectDetectionModels.MMYolo: { model = new MMYoloDetector(modelPath, deviceId); break; } + case ObjectDetectionModels.Nanodet: { model = new NanodetDetector(modelPath, deviceId); break; } + case ObjectDetectionModels.DamoYolo: { model = new DamoYoloDetector(modelPath, deviceId); break; } + case ObjectDetectionModels.EdgeYolo: { model = new EdgeYoloDetector(modelPath, deviceId); break; } + case ObjectDetectionModels.DamoDet: { model = new DamodetDetector(modelPath, deviceId); break; } + case ObjectDetectionModels.FastestDet: { model = new FastestDetDetector(modelPath, deviceId); invertAxes = modelPath.Contains("modified", StringComparison.OrdinalIgnoreCase) == false; break; } + default: + throw new Exception($"Model type '{modelType}' not implemented yet"); + } + return new Detector(model, threshold, imageConverterOptions, invertAxes); + + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/EdgeYoloDetector.cs b/ZeroLevel.ML/DNN/Detectors/EdgeYoloDetector.cs new file mode 100644 index 0000000..24e8eae --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/EdgeYoloDetector.cs @@ -0,0 +1,68 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class EdgeYoloDetector + : SSDNN, IObjectDetector + { + public EdgeYoloDetector(string modelPath, int deviceId) + : base(modelPath, deviceId) + { + } + + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output = d["output"]; + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < output.Dimensions[1]; box++) + { + var conf = output[tensorIndex, box, 4]; // уверенность в наличии любого объекта + if (conf > threshold) + { + var class_score = output[tensorIndex, box, 5]; + if (class_score > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, box, 1]; + var cy = output[tensorIndex, box, 0]; + var w = output[tensorIndex, box, 3]; + var h = output[tensorIndex, box, 2]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/FastestDetDetector.cs b/ZeroLevel.ML/DNN/Detectors/FastestDetDetector.cs new file mode 100644 index 0000000..57d615d --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/FastestDetDetector.cs @@ -0,0 +1,87 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System; +using System.Collections.Generic; +using System.Linq; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class FastestDetDetector + : SSDNN, IObjectDetector + { + private const float SIZE = 640; + + public FastestDetDetector(string modelPath, int deviceId) + : base(modelPath, deviceId) + { + } + + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + + private static double sigmoid(double x) + { + return 1d / (1d + Math.Exp(-x)); + } + + private static double tanh(double x) + { + return 2d / (1d + Math.Exp(-2d * x)) - 1d; + } + + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + var output = d.First().Value; + var feature_map_height = output.Dimensions[2]; + var feature_map_width = output.Dimensions[3]; + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + + for (int h = 0; h < feature_map_height; h++) + { + for (int w = 0; w < feature_map_width; w++) + { + var obj_score = output[tensorIndex, 0, h, w]; + var cls_score = output[tensorIndex, 5, h, w]; + var score = Math.Pow(obj_score, 0.6) * Math.Pow(cls_score, 0.4); + if (score > threshold) + { + var x_offset = tanh(output[tensorIndex, 1, h, w]); + var y_offset = tanh(output[tensorIndex, 2, h, w]); + + var box_width = sigmoid(output[tensorIndex, 3, h, w]) * SIZE; + var box_height = sigmoid(output[tensorIndex, 4, h, w]) * SIZE; + + var box_cx = ((w + x_offset) / feature_map_width) * SIZE + tensor.StartX; + var box_cy = ((h + y_offset) / feature_map_height) * SIZE + tensor.StartY; + + result.Add(new YoloPrediction + { + Cx = (float)box_cx * relative_koef_x, + Cy = (float)box_cy * relative_koef_y, + W = (float)box_width * relative_koef_x, + H = (float)box_height * relative_koef_y, + Class = 0, + Score = (float)score + }); + } + } + } + } + + }); + NMS.Apply(result); + return result; + } + + + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/IDetector.cs b/ZeroLevel.ML/DNN/Detectors/IDetector.cs new file mode 100644 index 0000000..0f20b85 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/IDetector.cs @@ -0,0 +1,18 @@ +extern alias CoreDrawing; + +using System; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public interface IDetector + : IDisposable + { + FastTensorPool CreateInput(string filePath); + FastTensorPool CreateInput(CoreDrawing.System.Drawing.Bitmap image, string filePath = null!); + List Detect(FastTensorPool input); + List Detect(string filePath); + List Detect(CoreDrawing.System.Drawing.Bitmap image, string filePath = null!); + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/IObjectDetector.cs b/ZeroLevel.ML/DNN/Detectors/IObjectDetector.cs new file mode 100644 index 0000000..84cf290 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/IObjectDetector.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public interface IObjectDetector + : IDisposable + { + float RNorm(float x); + float GNorm(float x); + float BNorm(float x); + List Predict(FastTensorPool inputs, float threshold); + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/MMYoloDetector.cs b/ZeroLevel.ML/DNN/Detectors/MMYoloDetector.cs new file mode 100644 index 0000000..4674c5a --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/MMYoloDetector.cs @@ -0,0 +1,72 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public sealed class MMYoloDetector + : SSDNN, IObjectDetector + { + public MMYoloDetector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor boxes = d["boxes"]; + Tensor scores = d["scores"]; + + if (boxes != null && scores != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < scores.Dimensions[1]; box++) + { + var conf = scores[tensorIndex, box]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var tlx = boxes[tensorIndex, box, 1]; + var tly = boxes[tensorIndex, box, 0]; + var brx = boxes[tensorIndex, box, 3]; + var bry = boxes[tensorIndex, box, 2]; + + var cx = (tlx + brx) * 0.5f; + var cy = (tly + bry) * 0.5f; + var w = brx - tlx; + var h = bry - tly; + + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.HNSW/PHNSW/UWLevel.cs b/ZeroLevel.ML/DNN/Detectors/NanodetDamoyoloComposeDetector.cs similarity index 58% rename from ZeroLevel.HNSW/PHNSW/UWLevel.cs rename to ZeroLevel.ML/DNN/Detectors/NanodetDamoyoloComposeDetector.cs index 298ac96..c5e1aef 100644 --- a/ZeroLevel.HNSW/PHNSW/UWLevel.cs +++ b/ZeroLevel.ML/DNN/Detectors/NanodetDamoyoloComposeDetector.cs @@ -4,10 +4,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace ZeroLevel.HNSW.PHNSW +namespace ZeroLevel.ML.DNN.Detectors { - public class UWLevel - :IPHNSWLevel + internal class NanodetDamoyoloComposeDetector { } } diff --git a/ZeroLevel.ML/DNN/Detectors/NanodetDetector.cs b/ZeroLevel.ML/DNN/Detectors/NanodetDetector.cs new file mode 100644 index 0000000..0cc1e60 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/NanodetDetector.cs @@ -0,0 +1,421 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using ZeroLevel.ML.DNN.Models; +using System.Runtime.CompilerServices; +using System; +using System.Collections.Generic; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class NanodetDetector + : SSDNN, IObjectDetector + { + private static float[] mean_vals = new float[3] { 103.53f, 116.28f, 123.675f }; + private static float[] norm_vals = new float[3] { 0.017429f, 0.017507f, 0.017125f }; + private static int[] strides = new int[4] { 8, 16, 32, 64 }; + private static int reg_max = 7; + private static int input_s = 640; + + public float RNorm(float x) => (x + mean_vals[0]) * norm_vals[0]; + public float GNorm(float x) => (x + mean_vals[1]) * norm_vals[1]; + public float BNorm(float x) => (x + mean_vals[2]) * norm_vals[2]; + + public NanodetDetector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + #region Fastexp dict + static double[] ExpAdjustment = new double[256] { + 1.040389835, + 1.039159306, + 1.037945888, + 1.036749401, + 1.035569671, + 1.034406528, + 1.033259801, + 1.032129324, + 1.031014933, + 1.029916467, + 1.028833767, + 1.027766676, + 1.02671504, + 1.025678708, + 1.02465753, + 1.023651359, + 1.022660049, + 1.021683458, + 1.020721446, + 1.019773873, + 1.018840604, + 1.017921503, + 1.017016438, + 1.016125279, + 1.015247897, + 1.014384165, + 1.013533958, + 1.012697153, + 1.011873629, + 1.011063266, + 1.010265947, + 1.009481555, + 1.008709975, + 1.007951096, + 1.007204805, + 1.006470993, + 1.005749552, + 1.005040376, + 1.004343358, + 1.003658397, + 1.002985389, + 1.002324233, + 1.001674831, + 1.001037085, + 1.000410897, + 0.999796173, + 0.999192819, + 0.998600742, + 0.998019851, + 0.997450055, + 0.996891266, + 0.996343396, + 0.995806358, + 0.995280068, + 0.99476444, + 0.994259393, + 0.993764844, + 0.993280711, + 0.992806917, + 0.992343381, + 0.991890026, + 0.991446776, + 0.991013555, + 0.990590289, + 0.990176903, + 0.989773325, + 0.989379484, + 0.988995309, + 0.988620729, + 0.988255677, + 0.987900083, + 0.987553882, + 0.987217006, + 0.98688939, + 0.98657097, + 0.986261682, + 0.985961463, + 0.985670251, + 0.985387985, + 0.985114604, + 0.984850048, + 0.984594259, + 0.984347178, + 0.984108748, + 0.983878911, + 0.983657613, + 0.983444797, + 0.983240409, + 0.983044394, + 0.982856701, + 0.982677276, + 0.982506066, + 0.982343022, + 0.982188091, + 0.982041225, + 0.981902373, + 0.981771487, + 0.981648519, + 0.981533421, + 0.981426146, + 0.981326648, + 0.98123488, + 0.981150798, + 0.981074356, + 0.981005511, + 0.980944219, + 0.980890437, + 0.980844122, + 0.980805232, + 0.980773726, + 0.980749562, + 0.9807327, + 0.9807231, + 0.980720722, + 0.980725528, + 0.980737478, + 0.980756534, + 0.98078266, + 0.980815817, + 0.980855968, + 0.980903079, + 0.980955475, + 0.981017942, + 0.981085714, + 0.981160303, + 0.981241675, + 0.981329796, + 0.981424634, + 0.981526154, + 0.981634325, + 0.981749114, + 0.981870489, + 0.981998419, + 0.982132873, + 0.98227382, + 0.982421229, + 0.982575072, + 0.982735318, + 0.982901937, + 0.983074902, + 0.983254183, + 0.983439752, + 0.983631582, + 0.983829644, + 0.984033912, + 0.984244358, + 0.984460956, + 0.984683681, + 0.984912505, + 0.985147403, + 0.985388349, + 0.98563532, + 0.98588829, + 0.986147234, + 0.986412128, + 0.986682949, + 0.986959673, + 0.987242277, + 0.987530737, + 0.987825031, + 0.988125136, + 0.98843103, + 0.988742691, + 0.989060098, + 0.989383229, + 0.989712063, + 0.990046579, + 0.990386756, + 0.990732574, + 0.991084012, + 0.991441052, + 0.991803672, + 0.992171854, + 0.992545578, + 0.992924825, + 0.993309578, + 0.993699816, + 0.994095522, + 0.994496677, + 0.994903265, + 0.995315266, + 0.995732665, + 0.996155442, + 0.996583582, + 0.997017068, + 0.997455883, + 0.99790001, + 0.998349434, + 0.998804138, + 0.999264107, + 0.999729325, + 1.000199776, + 1.000675446, + 1.001156319, + 1.001642381, + 1.002133617, + 1.002630011, + 1.003131551, + 1.003638222, + 1.00415001, + 1.004666901, + 1.005188881, + 1.005715938, + 1.006248058, + 1.006785227, + 1.007327434, + 1.007874665, + 1.008426907, + 1.008984149, + 1.009546377, + 1.010113581, + 1.010685747, + 1.011262865, + 1.011844922, + 1.012431907, + 1.013023808, + 1.013620615, + 1.014222317, + 1.014828902, + 1.01544036, + 1.016056681, + 1.016677853, + 1.017303866, + 1.017934711, + 1.018570378, + 1.019210855, + 1.019856135, + 1.020506206, + 1.02116106, + 1.021820687, + 1.022485078, + 1.023154224, + 1.023828116, + 1.024506745, + 1.025190103, + 1.02587818, + 1.026570969, + 1.027268461, + 1.027970647, + 1.02867752, + 1.029389072, + 1.030114973, + 1.030826088, + 1.03155163, + 1.032281819, + 1.03301665, + 1.033756114, + 1.034500204, + 1.035248913, + 1.036002235, + 1.036760162, + 1.037522688, + 1.038289806, + 1.039061509, + 1.039837792, + 1.040618648 + }; + #endregion + static double FastExp(double x) + { + var tmp = (long)(1512775 * x + 1072632447); + int index = (int)(tmp >> 12) & 0xFF; + return BitConverter.Int64BitsToDouble(tmp << 32) * ExpAdjustment[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sigmoid(double value) + { + float k = (float)Math.Exp(value); + return k / (1.0f + k); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void Softmax(float[] input, int start, float[] dst) + { + var sum = 0f; + for (var i = 0; i < 8; ++i) + { + var e = (float)Math.Exp(input[start + i]); + dst[i] = e; + sum += e; + } + var sumInv = 1f / sum; + for (var i = 0; i < 8; ++i) + dst[i] *= sumInv; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private float[] GetCurrentRow(Tensor output, int b, int s) + { + float[] row = new float[33]; + for (int i = 0; i < row.Length; i++) + { + row[i] = output[b, s, i]; + } + return row; + } + + private List Decoder(int batchIndex, Tensor output, float threshold) + { + var result = new List(); + var proceedLength = 0; + for (int i = 0; i < strides.Length; i++) + { + var stride = strides[i]; + int feature_size = (int)Math.Ceiling((float)input_s / stride); + for (int y = 0; y < feature_size; y++) + { + for (int x = 0; x < feature_size; x++) + { + var startIndex = proceedLength + y * feature_size + x; + var row = GetCurrentRow(output, batchIndex, startIndex); + var score = row[0]; + if (score > threshold) + { + float ct_y = (x + 0.5f) * stride; + float ct_x = (y + 0.5f) * stride; + float[] dis_pred = new float[4]; + for (int di = 0; di < 4; di++) + { + float dis = 0; + float[] dis_after_sm = new float[reg_max + 1]; + Softmax(row, 1 + (reg_max + 1) * di, dis_after_sm); + for (int j = 0; j < reg_max + 1; j++) + { + dis += j * dis_after_sm[j]; + } + dis *= stride; + dis_pred[di] = dis; + } + float xmin = Math.Max(ct_x - dis_pred[0], .0f); + float ymin = Math.Max(ct_y - dis_pred[1], .0f); + + float xmax = Math.Min(ct_x + dis_pred[2], (float)(input_s)); + float ymax = Math.Min(ct_y + dis_pred[3], (float)(input_s)); + + var cx = (xmin + xmax) * 0.5f; + var cy = (ymin + ymax) * 0.5f; + var h = (xmax - xmin); + var w = (ymax - ymin); + + result.Add(new YoloPrediction + { + Cx = cx, + Cy = cy, + W = w, + H = h, + Class = 0, + Label = "0", + Score = score + }); + } + } + } + proceedLength += feature_size * feature_size; + } + return result; + } + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + + float normalization_koef_x = 1.0f / inputs.Width; + float normalization_koef_y = 1.0f / inputs.Height; + + Extract(new Dictionary> { { "data", inputs.Tensor } }, d => + { + Tensor output = d["output"]; + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + foreach (var box in Decoder(tensorIndex, output, threshold)) + { + box.Cx += tensor.StartX; + box.Cy += tensor.StartY; + + box.Cx *= normalization_koef_x; + box.Cy *= normalization_koef_y; + box.W *= normalization_koef_x; + box.H *= normalization_koef_y; + + result.Add(box); + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/ObjectDetectionModels.cs b/ZeroLevel.ML/DNN/Detectors/ObjectDetectionModels.cs new file mode 100644 index 0000000..2da14bd --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/ObjectDetectionModels.cs @@ -0,0 +1,16 @@ +namespace ZeroLevel.ML.DNN.Detectors +{ + public enum ObjectDetectionModels + { + YoloV5, + YoloV6, + YoloV7, + YoloV8, + MMYolo, + Nanodet, + DamoYolo, + EdgeYolo, + FastestDet, + DamoDet + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/Yolov5Detector.cs b/ZeroLevel.ML/DNN/Detectors/Yolov5Detector.cs new file mode 100644 index 0000000..7102e01 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/Yolov5Detector.cs @@ -0,0 +1,68 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class Yolov5Detector + : SSDNN, IObjectDetector + { + public Yolov5Detector(string modelPath, int deviceId) + : base(modelPath, deviceId) + { + } + + public float BNorm(float x) => x; + public float GNorm(float x) => x; + public float RNorm(float x) => x; + + public List Predict(FastTensorPool inputs, float threshold = 0.6f) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output = d["outputs"]; + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < output.Dimensions[1]; box++) + { + var conf = output[tensorIndex, box, 4]; // уверенность в наличии любого объекта + if (conf > threshold) + { + var class_score = output[tensorIndex, box, 5]; + if (class_score > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, box, 1]; + var cy = output[tensorIndex, box, 0]; + var w = output[tensorIndex, box, 3]; + var h = output[tensorIndex, box, 2]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/Yolov6Detector.cs b/ZeroLevel.ML/DNN/Detectors/Yolov6Detector.cs new file mode 100644 index 0000000..976cd30 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/Yolov6Detector.cs @@ -0,0 +1,69 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class Yolov6Detector + : SSDNN, IObjectDetector + { + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public Yolov6Detector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + // 30 x 8400 x 6 + // B x rows x boxes + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output = d["outputs"]; + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < output.Dimensions[1]; box++) + { + var conf = output[tensorIndex, box, 4]; // уверенность в наличии любого объекта + if (conf > threshold) + { + var class_score = output[tensorIndex, box, 5]; + if (class_score > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, box, 1]; + var cy = output[tensorIndex, box, 0]; + var w = output[tensorIndex, box, 3]; + var h = output[tensorIndex, box, 2]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/Yolov7Detector.cs b/ZeroLevel.ML/DNN/Detectors/Yolov7Detector.cs new file mode 100644 index 0000000..08a47b4 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/Yolov7Detector.cs @@ -0,0 +1,64 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class Yolov7Detector + : SSDNN, IObjectDetector + { + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public Yolov7Detector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output = d["output"]; + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < output.Dimensions[1]; box++) + { + var conf = output[tensorIndex, box, 4]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, box, 1]; + var cy = output[tensorIndex, box, 0]; + var w = output[tensorIndex, box, 3]; + var h = output[tensorIndex, box, 2]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/Detectors/Yolov8Detector.cs b/ZeroLevel.ML/DNN/Detectors/Yolov8Detector.cs new file mode 100644 index 0000000..8a822e0 --- /dev/null +++ b/ZeroLevel.ML/DNN/Detectors/Yolov8Detector.cs @@ -0,0 +1,73 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Collections.Generic; +using System.Linq; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN.Detectors +{ + public class Yolov8Detector + : SSDNN, IObjectDetector + { + public float BNorm(float x) => ImageConverter.StandartNormalizator(x); + public float GNorm(float x) => ImageConverter.StandartNormalizator(x); + public float RNorm(float x) => ImageConverter.StandartNormalizator(x); + + public Yolov8Detector(string modelPath, int deviceId = 0) + : base(modelPath, deviceId) + { + } + + public List Predict(FastTensorPool inputs, float threshold) + { + var result = new List(); + var relative_koef_x = 1.0f / inputs.Width; + var relative_koef_y = 1.0f / inputs.Height; + Extract(new Dictionary> { { "images", inputs.Tensor } }, d => + { + Tensor output; + if (d.ContainsKey("output0")) + { + output = d["output0"]; + } + else + { + output = d.First().Value; + } + if (output != null) + { + for (int tensorIndex = 0; tensorIndex < inputs.TensorSize; tensorIndex++) + { + var tensor = inputs.GetTensor(tensorIndex); + for (int box = 0; box < output.Dimensions[2]; box++) + { + var conf = output[tensorIndex, 4, box]; // уверенность в наличии любого объекта + if (conf > threshold) + { + // Перевод относительно входа модели в относительные координаты + var cx = output[tensorIndex, 1, box]; + var cy = output[tensorIndex, 0, box]; + var w = output[tensorIndex, 3, box]; + var h = output[tensorIndex, 2, box]; + // Перевод в координаты отнисительно текущего смещения + cx += tensor.StartX; + cy += tensor.StartY; + result.Add(new YoloPrediction + { + Cx = cx * relative_koef_x, + Cy = cy * relative_koef_y, + W = w * relative_koef_x, + H = h * relative_koef_y, + Class = 0, + Label = "0", + Score = conf + }); + } + } + } + } + }); + NMS.Apply(result); + return result; + } + } +} diff --git a/ZeroLevel.ML/DNN/ImageConvertor.cs b/ZeroLevel.ML/DNN/ImageConvertor.cs new file mode 100644 index 0000000..542859a --- /dev/null +++ b/ZeroLevel.ML/DNN/ImageConvertor.cs @@ -0,0 +1,162 @@ +extern alias CoreDrawing; + +using System; +using System.Runtime.CompilerServices; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN +{ + public interface IImageConverter + { + FastTensorPool ImageToFastTensorsV2(string imagePath); + FastTensorPool ImageToFastTensorsV2Inverted(string imagePath); + FastTensorPool ImageToFastTensorsV2(CoreDrawing.System.Drawing.Bitmap image); + FastTensorPool ImageToFastTensorsV2Inverted(CoreDrawing.System.Drawing.Bitmap image); + } + + public sealed class ImageToTensorConversionOptions + { + /// + /// Размер кропа который хранится в одном срезе тензора + /// [batchSize, TensorCropSize, TensorCropSize, 3] + /// или + /// [batchSize, 3, TensorCropSize, TensorCropSize] + /// + public int TensorCropSize { get; set; } = 640; + + /// + /// Множитель размера изображения в препроцессинге, например, мы хотим разрезать изображения на части размером 960*960 + /// которые затем отресайзить на вход сети к 640 на 640, в этом случае производительнее сначала сделать ресайз изображения + /// на множитель side = side * (640 / 960) = side * 0.666, и затем разрезать сразу на части 640*640 + /// + public float Multiplier { get; set; } = 1.0f; + + /// + /// Размер батча для сетей с фиксированным размером, при -1 батч будет равен количеству кропов + /// + public int BatchSize { get; set; } = -1; + + /// + /// Использовать BRG порядок пикселей вместо RGB + /// + public bool UseBRG { get; set; } = false; + + /// + /// Кратность входного размера + /// + public int SizeMultiplicity { get; set; } = 64; + } + + public sealed class ImageConverter + : IImageConverter + { + + static float standart_normalizator_koef = 1.0f / 255.0f; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float StandartNormalizator(float x) => x * standart_normalizator_koef; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MeanStdNormilize(float x, float mean, float std) => (standart_normalizator_koef * x - mean) / std; + + + private readonly ImageToTensorConversionOptions _options; + + private readonly bool _noPreresize; + public ImageConverter(ImageToTensorConversionOptions options) + { + if (options.Multiplier <= float.Epsilon) + { + throw new ArgumentException("Multiplier must be positive number"); + } + + _options = options; + _noPreresize = Math.Abs(1.0f - options.Multiplier) <= float.Epsilon; + } + + private CoreDrawing.System.Drawing.Bitmap PrepareBitmap(string imagePath) + { + CoreDrawing.System.Drawing.Bitmap image; + image = new CoreDrawing.System.Drawing.Bitmap(imagePath); + return PrepareBitmap(image); + } + + private CoreDrawing.System.Drawing.Bitmap PrepareBitmap(CoreDrawing.System.Drawing.Bitmap image) + { + int sourceWidth = image.Width, sourceHeight = image.Height; + + if (_noPreresize == false) + { + sourceWidth = (int)(sourceWidth * _options.Multiplier); + sourceHeight = (int)(sourceHeight * _options.Multiplier); + } + if (_options.SizeMultiplicity > 1) + { + //Сделать размеры изображения кратным числу + sourceWidth = ((int)Math.Ceiling((double)(sourceWidth / _options.SizeMultiplicity))) * _options.SizeMultiplicity; + sourceHeight = ((int)Math.Ceiling((double)(sourceHeight / _options.SizeMultiplicity))) * _options.SizeMultiplicity; + } + if (sourceWidth != image.Width || sourceHeight != image.Height) + { + using (var buf = image) + { + image = new CoreDrawing.System.Drawing.Bitmap(buf, new System.Drawing.Size(sourceWidth, sourceHeight)); + } + } + return image; + } + + public FastTensorPool ImageToFastTensorsV2(string imagePath) + { + var image = PrepareBitmap(imagePath); + return Convert(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); + } + + public FastTensorPool ImageToFastTensorsV2Inverted(string imagePath) + { + var image = PrepareBitmap(imagePath); + return ConvertInverted(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); + } + public FastTensorPool ImageToFastTensorsV2(CoreDrawing.System.Drawing.Bitmap image) + { + image = PrepareBitmap(image); + return Convert(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); + } + + public FastTensorPool ImageToFastTensorsV2Inverted(CoreDrawing.System.Drawing.Bitmap image) + { + image = PrepareBitmap(image); + return ConvertInverted(image, image.Width, image.Height, _options.TensorCropSize, _options.BatchSize, _options.UseBRG); + } + + public static FastTensorPool Convert(CoreDrawing.System.Drawing.Bitmap image, int sourceWidth, int sourceHeight, int cropSize, int batchSize, bool bgr) + { + var pool = new FastTensorPool(sourceWidth, sourceHeight, image.Width, image.Height, cropSize); + pool.BatchSize = batchSize; + if (bgr) + { + pool.FillFromImageBGR(image); + } + else + { + pool.FillFromImage(image); + } + return pool; + } + + public static FastTensorPool ConvertInverted(CoreDrawing.System.Drawing.Bitmap image, int sourceWidth, int sourceHeight, int cropSize, int batchSize, bool bgr) + { + var pool = new FastTensorPool(sourceWidth, sourceHeight, image.Width, image.Height, cropSize); + pool.BatchSize = batchSize; + if(bgr) + { + pool.FillFromImageInvertAxeBGR(image); + } + else + { + pool.FillFromImageInvertAxe(image); + } + return pool; + } + } +} diff --git a/ZeroLevel.ML/DNN/Models/ImagePrediction.cs b/ZeroLevel.ML/DNN/Models/ImagePrediction.cs new file mode 100644 index 0000000..6449c36 --- /dev/null +++ b/ZeroLevel.ML/DNN/Models/ImagePrediction.cs @@ -0,0 +1,11 @@ +namespace ZeroLevel.ML.DNN.Models +{ + public sealed class ImagePrediction + { + public string Name { get; set; } = null!; + public string Path { get; set; } = null!; + public int Width { get; set; } + public int Height { get; set; } + public YoloPredictionWithGeo[] Predictions { get; set; } = null!; + } +} diff --git a/ZeroLevel.ML/DNN/Models/TensorPool.cs b/ZeroLevel.ML/DNN/Models/TensorPool.cs new file mode 100644 index 0000000..41afb04 --- /dev/null +++ b/ZeroLevel.ML/DNN/Models/TensorPool.cs @@ -0,0 +1,151 @@ +extern alias CoreDrawing; +using Microsoft.ML.OnnxRuntime.Tensors; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using ZeroLevel.ML.Services; + +namespace ZeroLevel.ML.DNN.Models +{ + public sealed class FastTensorPool + : IDisposable + { + public Tensor Tensor; + public string Name = null!; + public string Path = null!; + public int CropSize; + public int Width; + public int Height; + public int SourceWidth; + public int SourceHeight; + public int BatchSize = -1; // -1 dynamic + public int TensorSize { get; private set; } + + private Dictionary _index; + + public FastTensorPool() + { + } + + public FastTensorPool(int sourceWidth, int sourceHeight, int fullWidth, int fullHeight, int cropSize) + { + SourceWidth = sourceWidth; + SourceHeight = sourceHeight; + Width = fullWidth; + Height = fullHeight; + CropSize = cropSize; + } + + public TensorPoolItem GetTensor(int tensorIndex) => _index[tensorIndex]; + + public void FillFromImage(CoreDrawing.System.Drawing.Bitmap image) + { + using (var scanner = new ImageScanner(image, CropSize)) + { + _index = new Dictionary(scanner.TotalRegions); + TensorSize = scanner.TotalRegions; + var diff = BatchSize - scanner.TotalRegions; + var tensorSize = BatchSize == -1 ? scanner.TotalRegions : BatchSize; + Tensor = new DenseTensor(new[] { tensorSize, 3, scanner.CropSizeX, scanner.CropSizeY }); + var tasks = new Task[scanner.TotalRegions]; + foreach (var regionReader in scanner.ScanByRegions()) + { + var tensor = new TensorPoolItem(Tensor, regionReader.TensorIndex, regionReader.X, regionReader.Y, scanner.CropSizeX, scanner.CropSizeY); + _index[regionReader.TensorIndex] = tensor; + tasks[regionReader.TensorIndex] = Task.Factory.StartNew((_reader) => + { + var reader = (ImageRegionReader)_reader; + reader.FillTensor(_index[reader.TensorIndex]); + }, regionReader); + } + Task.WaitAll(tasks); + } + } + + public void FillFromImageInvertAxe(CoreDrawing.System.Drawing.Bitmap image) + { + using (var scanner = new ImageScanner(image, CropSize)) + { + _index = new Dictionary(scanner.TotalRegions); + TensorSize = scanner.TotalRegions; + var diff = BatchSize - scanner.TotalRegions; + var tensorSize = BatchSize == -1 ? scanner.TotalRegions : BatchSize; + Tensor = new DenseTensor(new[] { tensorSize, 3, scanner.CropSizeX, scanner.CropSizeY }); + var tasks = new Task[scanner.TotalRegions]; + foreach (var regionReader in scanner.ScanByRegions()) + { + var tensor = new TensorPoolItem(Tensor, regionReader.TensorIndex, regionReader.X, regionReader.Y, scanner.CropSizeX, scanner.CropSizeY); + _index[regionReader.TensorIndex] = tensor; + tasks[regionReader.TensorIndex] = Task.Factory.StartNew((_reader) => + { + var reader = (ImageRegionReader)_reader; + reader.Read((x, y, r, g, b) => + { + _index[reader.TensorIndex].FastSet(y, x, r, g, b); + }); + }, regionReader); + } + Task.WaitAll(tasks); + } + } + + public void FillFromImageBGR(CoreDrawing.System.Drawing.Bitmap image) + { + using (var scanner = new ImageScanner(image, CropSize)) + { + _index = new Dictionary(scanner.TotalRegions); + TensorSize = scanner.TotalRegions; + var diff = BatchSize - scanner.TotalRegions; + var tensorSize = BatchSize == -1 ? scanner.TotalRegions : BatchSize; + Tensor = new DenseTensor(new[] { tensorSize, 3, scanner.CropSizeX, scanner.CropSizeY }); + var tasks = new Task[scanner.TotalRegions]; + foreach (var regionReader in scanner.ScanByRegions()) + { + var tensor = new TensorPoolItem(Tensor, regionReader.TensorIndex, regionReader.X, regionReader.Y, scanner.CropSizeX, scanner.CropSizeY); + _index[regionReader.TensorIndex] = tensor; + tasks[regionReader.TensorIndex] = Task.Factory.StartNew((_reader) => + { + var reader = (ImageRegionReader)_reader; + reader.Read((x, y, r, g, b) => + { + _index[reader.TensorIndex].FastSet(x, y, b, g, r); + }); + }, regionReader); + } + Task.WaitAll(tasks); + } + } + + public void FillFromImageInvertAxeBGR(CoreDrawing.System.Drawing.Bitmap image) + { + using (var scanner = new ImageScanner(image, CropSize)) + { + _index = new Dictionary(scanner.TotalRegions); + TensorSize = scanner.TotalRegions; + var diff = BatchSize - scanner.TotalRegions; + var tensorSize = BatchSize == -1 ? scanner.TotalRegions : BatchSize; + Tensor = new DenseTensor(new[] { tensorSize, 3, scanner.CropSizeX, scanner.CropSizeY }); + var tasks = new Task[scanner.TotalRegions]; + foreach (var regionReader in scanner.ScanByRegions()) + { + var tensor = new TensorPoolItem(Tensor, regionReader.TensorIndex, regionReader.X, regionReader.Y, scanner.CropSizeX, scanner.CropSizeY); + _index[regionReader.TensorIndex] = tensor; + tasks[regionReader.TensorIndex] = Task.Factory.StartNew((_reader) => + { + var reader = (ImageRegionReader)_reader; + reader.Read((x, y, r, g, b) => + { + _index[reader.TensorIndex].FastSet(y, x, b, g, r); + }); + }, regionReader); + } + Task.WaitAll(tasks); + } + } + + public void Dispose() + { + Tensor = null!; + } + } +} diff --git a/ZeroLevel.ML/DNN/Models/TensorPoolItem.cs b/ZeroLevel.ML/DNN/Models/TensorPoolItem.cs new file mode 100644 index 0000000..7eebb40 --- /dev/null +++ b/ZeroLevel.ML/DNN/Models/TensorPoolItem.cs @@ -0,0 +1,67 @@ +using Microsoft.ML.OnnxRuntime.Tensors; +using System.Runtime.CompilerServices; +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.ML.DNN.Models +{ + public sealed class TensorPoolItem + : IBinarySerializable + { + public int StartX; + public int StartY; + public int Width; + public int Height; + public int TensorIndex; + public Tensor Tensor; + + public TensorPoolItem() + { + } + public TensorPoolItem(Tensor tensor, int tensorIndex, int startX, int startY, int width, int height) + { + Tensor = tensor; + TensorIndex = tensorIndex; + StartX = startX; + StartY = startY; + Width = width; + Height = height; + } + + public void Set(int x, int y, float valueR, float valueG, float valueB) + { + var tx = x - StartX; + if (tx < 0 || tx >= Width) return; + var ty = y - StartY; + + Tensor[TensorIndex, 0, tx, ty] = valueR; + Tensor[TensorIndex, 1, tx, ty] = valueG; + Tensor[TensorIndex, 2, tx, ty] = valueB; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FastSet(int x, int y, float valueR, float valueG, float valueB) + { + Tensor[TensorIndex, 0, x, y] = valueR; + Tensor[TensorIndex, 1, x, y] = valueG; + Tensor[TensorIndex, 2, x, y] = valueB; + } + + public void Serialize(IBinaryWriter writer) + { + writer.WriteInt32(StartX); + writer.WriteInt32(StartY); + writer.WriteInt32(Width); + writer.WriteInt32(Height); + writer.WriteInt32(TensorIndex); + } + + public void Deserialize(IBinaryReader reader) + { + StartX = reader.ReadInt32(); + StartY = reader.ReadInt32(); + Width = reader.ReadInt32(); + Height = reader.ReadInt32(); + TensorIndex = reader.ReadInt32(); + } + } +} diff --git a/ZeroLevel.NN/Models/YoloPrediction.cs b/ZeroLevel.ML/DNN/Models/YoloPrediction.cs similarity index 64% rename from ZeroLevel.NN/Models/YoloPrediction.cs rename to ZeroLevel.ML/DNN/Models/YoloPrediction.cs index 2ae7f87..be042e1 100644 --- a/ZeroLevel.NN/Models/YoloPrediction.cs +++ b/ZeroLevel.ML/DNN/Models/YoloPrediction.cs @@ -1,6 +1,6 @@ using ZeroLevel.Services.Serialization; -namespace ZeroLevel.NN.Models +namespace ZeroLevel.ML.DNN.Models { public class YoloPrediction : IBinarySerializable @@ -11,7 +11,7 @@ namespace ZeroLevel.NN.Models public float W { get; set; } public float H { get; set; } public float Score { get; set; } - public string Label { get; set; } + public string Label { get; set; } = string.Empty; public float X { get { return Cx - W / 2.0f; } } public float Y { get { return Cy - W / 2.0f; } } @@ -41,26 +41,26 @@ namespace ZeroLevel.NN.Models } } - public void Deserialize(IBinaryReader reader) + public void Serialize(IBinaryWriter writer) { - this.Cx = reader.ReadFloat(); - this.Cy = reader.ReadFloat(); - this.W = reader.ReadFloat(); - this.H = reader.ReadFloat(); - this.Class = reader.ReadInt32(); - this.Score = reader.ReadFloat(); - this.Label = reader.ReadString(); + writer.WriteInt32(Class); + writer.WriteFloat(Cx); + writer.WriteFloat(Cy); + writer.WriteFloat(W); + writer.WriteFloat(H); + writer.WriteFloat(Score); + writer.WriteString(Label); } - public void Serialize(IBinaryWriter writer) + public void Deserialize(IBinaryReader reader) { - writer.WriteFloat(this.Cx); - writer.WriteFloat(this.Cy); - writer.WriteFloat(this.W); - writer.WriteFloat(this.H); - writer.WriteInt32(this.Class); - writer.WriteFloat(this.Score); - writer.WriteString(this.Label); + Class = reader.ReadInt32(); + Cx = reader.ReadFloat(); + Cy = reader.ReadFloat(); + W = reader.ReadFloat(); + H = reader.ReadFloat(); + Score = reader.ReadFloat(); + Label = reader.ReadString(); } } } diff --git a/ZeroLevel.ML/DNN/Models/YoloPredictionWithGeo.cs b/ZeroLevel.ML/DNN/Models/YoloPredictionWithGeo.cs new file mode 100644 index 0000000..2319a87 --- /dev/null +++ b/ZeroLevel.ML/DNN/Models/YoloPredictionWithGeo.cs @@ -0,0 +1,33 @@ +namespace ZeroLevel.ML.DNN.Models +{ + public sealed class YoloPredictionWithGeo + { + private readonly YoloPrediction _yoloPrediction; + public double Lat { get; private set; } + public double Lon { get; private set; } + + public void UpdateGeo(double lat, double lon) + { + Lat = lat; + Lon = lon; + } + + public int Class => _yoloPrediction.Class; + public float Cx => _yoloPrediction.Cx; + public float Cy => _yoloPrediction.Cy; + public float W => _yoloPrediction.W; + public float H => _yoloPrediction.H; + public float Score => _yoloPrediction.Score; + public string Label => _yoloPrediction.Label; + + public float X => _yoloPrediction.X; + public float Y => _yoloPrediction.Y; + public float Area => _yoloPrediction.Area; + public string Description => _yoloPrediction.Description; + + public YoloPredictionWithGeo(YoloPrediction yoloPrediction) + { + _yoloPrediction = yoloPrediction; + } + } +} diff --git a/ZeroLevel.ML/DNN/NMS.cs b/ZeroLevel.ML/DNN/NMS.cs new file mode 100644 index 0000000..1092499 --- /dev/null +++ b/ZeroLevel.ML/DNN/NMS.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.DNN +{ + public static class NMS + { + private const float IOU_THRESHOLD = .01f; + + public static void Apply(List boxes) + { + for (int i = 0; i < boxes.Count - 1; i++) + { + var left = boxes[i]; + for (int j = i + 1; j < boxes.Count; j++) + { + var right = boxes[j]; + if (left.Class == right.Class) + { + var iou = IOU(left, right); + if (iou > IOU_THRESHOLD) + { + if (left.Score > right.Score) + { + boxes.RemoveAt(j); + j--; + } + else + { + boxes.RemoveAt(i); + i--; + break; + } + } + } + } + } + } + + static float IOU(YoloPrediction box1, YoloPrediction box2) + { + var left = (float)Math.Max(box1[0], box2[0]); + var right = (float)Math.Min(box1[2], box2[2]); + + var top = (float)Math.Max(box1[1], box2[1]); + var bottom = (float)Math.Min(box1[3], box2[3]); + + var width = right - left; + var height = bottom - top; + var intersectionArea = width * height; + + return intersectionArea / (float)(box1.Area + box2.Area - intersectionArea); + } + } +} diff --git a/ZeroLevel.ML/DNN/ObjectDetectionFolderAnalizer.cs b/ZeroLevel.ML/DNN/ObjectDetectionFolderAnalizer.cs new file mode 100644 index 0000000..fb6d650 --- /dev/null +++ b/ZeroLevel.ML/DNN/ObjectDetectionFolderAnalizer.cs @@ -0,0 +1,266 @@ +extern alias CoreDrawing; + +using ZeroLevel.ML.DNN.Detectors; +using ZeroLevel.ML.DNN.Models; +using System.Collections.Concurrent; +using System.Threading; +using System; +using System.IO; +using System.Linq; + +namespace ZeroLevel.ML.DNN +{ + public sealed class ObjectDetectionOptions + { + public string DetectionModelPath { get; set; } = null!; + public int ChunksCount { get; set; } = 4; + public int PredictionThreadsCount { get; set; } = 3; + public int PreprocessThreadsCount { get; set; } = 3; + public int PostprocessThreadsCount { get; set; } = 3; + public int CropSize { get; set; } = 960; + public float ImageScaleValue { get; set; } = 0.8f; + public int TensorBufferSize { get; set; } = 24; + public float WithoutClassifyDetectionTreshold { get; set; } = 0.5f; + public string AfterProcessingTargetFolder { get; set; } = null!; + public int SizeMultiplicity { get; set; } = 1; + } + + public static class ObjectDetectionFolderAnalizer + { + private sealed class LocalThreadsSet + : IDisposable + { + private readonly CountdownEvent _threadsCountdown; + private readonly Thread[] _threads; + private class WrapperContext + { + public Action ThreadBody = null!; + public object Obj = null!; + } + public LocalThreadsSet(int threadsCount) + { + _threadsCountdown = new CountdownEvent(threadsCount); + _threads = new Thread[threadsCount]; + for (int i = 0; i < threadsCount; i++) + { + _threads[i] = new Thread(ThreadBodyWrapper!); + } + } + + public void Run(Action threadBody, object state) + { + var context = new WrapperContext { ThreadBody = threadBody, Obj = state }; + for (int i = 0; i < _threads.Length; i++) + { + _threads[i].Start(context); + } + } + + private void ThreadBodyWrapper(object ctx) + { + try + { + var context = (WrapperContext)ctx; + context.ThreadBody.Invoke(context.Obj); + } + catch (Exception ex) + { + + } + _threadsCountdown.Signal(); + } + + public void Wait() + { + _threadsCountdown.Wait(); + } + + public void Dispose() + { + _threadsCountdown.Dispose(); + } + } + + private sealed class ObjectDetectionContext + : IDisposable + { + private readonly ConcurrentQueue _files; + private readonly BlockingCollection _preprocessedItems = new BlockingCollection(); + private readonly BlockingCollection _proceedItems = new BlockingCollection(); + + private readonly IDetector _detector; + private readonly ObjectDetectionOptions _options; + private readonly Action _postprocessAction; + + private int _handled = 0; + private readonly Action _progressHandler; + public ObjectDetectionContext(IDetector detector, string folder, ObjectDetectionOptions options, Action imagePredictionHandler, Action progressHandler) + { + _detector = detector; + _options = options; + _progressHandler = progressHandler; + _postprocessAction = imagePredictionHandler; + var files = Directory.GetFiles(folder)?.Where(f => KnownImageFormats.IsKnownFormat(f)) ?? Enumerable.Empty(); + _files = new ConcurrentQueue(files); + } + + private readonly object _lockProgressUpdate = new object(); + private void UpdateProgress() + { + lock (_lockProgressUpdate) + { + _progressHandler.Invoke(_files.Count, _preprocessedItems.Count, _proceedItems.Count, _handled); + } + } + + public string TakeFile() + { + if (_files.Count > 0) + { + if (_files.TryDequeue(out var r)) + { + return r; + } + } + return null!; + } + + public void CreateInput(string file) + { + var input = _detector.CreateInput(file); + while (_preprocessedItems.Count > _options.TensorBufferSize) + { + Thread.Sleep(200); + } + _preprocessedItems.Add(input); + UpdateProgress(); + } + + public void NoMoreInputs() + { + _preprocessedItems.CompleteAdding(); + } + + public FastTensorPool GetInput() + { + while (_preprocessedItems.IsCompleted == false) + { + if (_preprocessedItems.TryTake(out var tensor, 200)) + { + return tensor; + } + } + return null!; + } + + public void Predict(FastTensorPool tensor) + { + var predictions = _detector.Detect(tensor) + ?.Select(p => new YoloPredictionWithGeo(p)); + + _proceedItems.Add(new ImagePrediction + { + Width = tensor.SourceWidth, + Height = tensor.SourceHeight, + Name = tensor.Name, + Path = tensor.Path, + Predictions = predictions?.ToArray()! + }); + + tensor.Dispose(); + + UpdateProgress(); + + GC.Collect(2); + GC.WaitForPendingFinalizers(); + } + + public void NoMorePredictions() + { + _proceedItems.CompleteAdding(); + } + + public ImagePrediction GetPrediction() + { + while (_proceedItems.IsCompleted == false) + { + if (_proceedItems.TryTake(out var prediction, 200)) + { + return prediction; + } + } + return null!; + } + + public void HandlePrediction(ImagePrediction prediction) + { + _postprocessAction.Invoke(prediction); + Interlocked.Increment(ref _handled); + UpdateProgress(); + } + + public void Dispose() + { + _preprocessedItems.Dispose(); + _proceedItems.Dispose(); + } + } + + public static void Analize(IDetector detector, string folder, ObjectDetectionOptions options, Action imagePredictionHandler, Action progressHandler) + { + using (var context = new ObjectDetectionContext(detector, folder, options, imagePredictionHandler, progressHandler)) + { + var preprocessThreadsSet = new LocalThreadsSet(options.PreprocessThreadsCount); + var processThreadSet = new LocalThreadsSet(options.PredictionThreadsCount); + var postprocessThreadSet = new LocalThreadsSet(options.PostprocessThreadsCount); + + preprocessThreadsSet.Run(PreprocessThreadBody, context); + processThreadSet.Run(ProcessThreadBody, context); + postprocessThreadSet.Run(PostprocessThreadBody, context); + + preprocessThreadsSet.Wait(); + preprocessThreadsSet.Dispose(); + + context.NoMoreInputs(); + + processThreadSet.Wait(); + processThreadSet.Dispose(); + + context.NoMorePredictions(); + + postprocessThreadSet.Wait(); + postprocessThreadSet.Dispose(); + } + } + + private static void PreprocessThreadBody(object ctx) + { + string file; + var context = (ObjectDetectionContext)ctx; + while ((file = context.TakeFile()) != null) + { + context.CreateInput(file); + } + } + + private static void ProcessThreadBody(object ctx) + { + var context = (ObjectDetectionContext)ctx; + FastTensorPool input; + while ((input = context.GetInput()) != null) + { + context.Predict(input); + } + } + + private static void PostprocessThreadBody(object ctx) + { + var context = (ObjectDetectionContext)ctx; + ImagePrediction prediction; + while ((prediction = context.GetPrediction()) != null) + { + context.HandlePrediction(prediction); + } + } + } +} diff --git a/ZeroLevel.ML/DNN/SSDNN.cs b/ZeroLevel.ML/DNN/SSDNN.cs new file mode 100644 index 0000000..068539f --- /dev/null +++ b/ZeroLevel.ML/DNN/SSDNN.cs @@ -0,0 +1,44 @@ +using Microsoft.ML.OnnxRuntime; +using Microsoft.ML.OnnxRuntime.Tensors; +using System; +using System.Collections.Generic; + +namespace ZeroLevel.ML.DNN +{ + public abstract class SSDNN + : IDisposable + { + protected readonly InferenceSession _session; + + public SSDNN(string modelPath, int deviceId) + { + var so = SessionOptions.MakeSessionOptionWithCudaProvider(deviceId); + so.RegisterOrtExtensions(); + so.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_FATAL; + so.GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL; + so.ExecutionMode = ExecutionMode.ORT_PARALLEL; + _session = new InferenceSession(modelPath, so); + } + protected void Extract(IDictionary> input, Action>> inputHandler) + { + var container = new List(); + foreach (var pair in input) + { + container.Add(NamedOnnxValue.CreateFromTensor(pair.Key, pair.Value)); + } + using (var output = _session.Run(container)) + { + var result = new Dictionary>(); + foreach (var o in output) + { + result.Add(o.Name, o.AsTensor()); + } + inputHandler.Invoke(result); + } + } + public void Dispose() + { + _session?.Dispose(); + } + } +} diff --git a/ZeroLevel.ML/ImageMetainfo.cs b/ZeroLevel.ML/ImageMetainfo.cs new file mode 100644 index 0000000..260cc83 --- /dev/null +++ b/ZeroLevel.ML/ImageMetainfo.cs @@ -0,0 +1,355 @@ +using MetadataExtractor; +using MetadataExtractor.Formats.Exif; +using MetadataExtractor.Formats.Xmp; +using System; +using System.IO; +using System.Linq; +using System.Text; +using XmpCore; +using ZeroLevel.ML.LocationMath; + +namespace ZeroLevel.ML +{ + public sealed class DewarpData + { + /* +For the parameter values conversion I used the following formulas (from Luhmann et al., 2019): +k1 pixel units = k1 focal units / focal length^2 +k2 pixel units = k2 focal units / focal length^4 +k3 pixel units = k3 focal units / focal length^6 +p1 pixel units = p1 focal units / focal length +p2 pixel units = -p2 focal units / focal length + */ + + public string Date = null!; + /// + /// Focal length + /// + public float Fx; + /// + /// Focal length + /// + public float Fy; + /// + /// Principal points + /// + public float Cx; + /// + /// Principal points + /// + public float Cy; + /// + /// Lens distortion + /// + public float K1; + /// + /// Lens distortion + /// + public float K2; + /// + /// Lens distortion + /// + public float P1; + /// + /// Lens distortion + /// + public float P2; + /// + /// Lens distortion + /// + public float K3; + + public DewarpData(string data) + { + if (!string.IsNullOrWhiteSpace(data)) + { + var parts = data.Split(';'); + if (parts.Length == 2) + { + Date = parts[0]; + var par_parts = parts[1].Split(","); + + if (par_parts.Length >= 9) + { + Fx = ToFloat(par_parts[0]); + Fy = ToFloat(par_parts[1]); + Cx = ToFloat(par_parts[2]); + Cy = ToFloat(par_parts[3]); + K1 = ToFloat(par_parts[4]); + K2 = ToFloat(par_parts[5]); + P1 = ToFloat(par_parts[6]); + P2 = ToFloat(par_parts[7]); + K3 = ToFloat(par_parts[8]); + } + } + } + } + + private static float ToFloat(string s) + { + var ns = ImageMetainfo.GetNumber(s); + if (float.TryParse(ns, out var f)) + { + return f; + } + return 0f; + } + + public float CalculateDistortedX(float x) + { + return x * (1 + K1); + } + + } + + public sealed class ImageMetainfo + { + private CameraMath _camera = null; + public CameraMath CreateCamera() + { + if (_camera == null) + { + _camera = new CameraMath(CameraPixelSizes.GetPixelSizeByModel(this.Model), this.FocalLength, this.ImageWidth, this.ImageHeight); + } + return _camera; + } + + public DateTime Created { get; set; } + public string Make { get; set; } = null!; + public string Model { get; set; } = null!; + public string SerialNumber { get; set; } = null!; + + public double FocalLength { get; set; } + public double ImageWidth { get; set; } + public double ImageHeight { get; set; } + + public double GPSLatitude { get; set; } + public double GPSLongitude { get; set; } + public double GPSAltitude { get; set; } + + public double DJILatitude { get; set; } + public double DJILongitude { get; set; } + public double DJIAltitude { get; set; } + + private double GimbalYawDegree { get; set; } + private double FlightYawDegree { get; set; } + + private static double Diff(double a, double b) + { + var a1 = Math.Abs(a - b); + var a2 = 360 - Math.Max(a, b) + Math.Min(a, b); + return Math.Min(a1, a2); + } + + public double Yaw + { + get + { + var fn = (FlightYawDegree + 360) % 360; + var gn = (GimbalYawDegree + 360) % 360; + var gni = (GimbalYawDegree + 540) % 360; + + var gy = GimbalYawDegree; + if (gy < 0) + { + gy = 180 + GimbalYawDegree; + } + else + { + gy = -180 + GimbalYawDegree; + } + + var d1 = Diff(fn, gn); + var d2 = Diff(fn, gni); + + if (d2 < d1) + { + return gy; + } + return GimbalYawDegree; + } + } + + public double CalibratedFocalLength { get; set; } + + public DewarpData Dewarp { get; set; } = null!; + + public double Latitude => Math.Max(DJILatitude, GPSLatitude); + public double Longitude => Math.Max(DJILongitude, GPSLongitude); + public double Altitude => DJIAltitude; + + internal static string GetNumber(string line) + { + var ns = new StringBuilder(); + var sep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + bool started = false; + bool separated = false; + foreach (var ch in line) + { + if (char.IsDigit(ch)) + { + started = true; + ns.Append(ch); + } + else if (ch == '.' || ch == ',' && started) + { + if (separated) + { + break; + } + separated = true; + ns.Append(sep); + } + else if (ch == '-' && !started) + { + started = true; + ns.Append(ch); + } + else if (started) + { + break; + } + } + return ns.ToString(); + } + + public void ReadMetadata(Stream stream) + { + double dBuf; + try + { + var metadata = ImageMetadataReader.ReadMetadata(stream); + var exif0 = metadata.OfType().FirstOrDefault(); + if (exif0 != null) + { + foreach (var t in exif0.Tags) + { + if (t.Name.Equals("Make")) + { + this.Make = t.Description!; + } + else if (t.Name.Equals("Model")) + { + this.Model = t.Description!; + } + } + } + + var exif = metadata.OfType().FirstOrDefault(); + if (exif != null) + { + foreach (var t in exif.Tags) + { + if (t.Name.Equals("Focal Length")) + { + if (double.TryParse(GetNumber(t.Description ?? string.Empty), out dBuf)) + { + this.FocalLength = dBuf; + } + } + else if (t.Name.Equals("Exif Image Width")) + { + if (double.TryParse(GetNumber(t.Description ?? string.Empty), out dBuf)) + { + this.ImageWidth = dBuf; + } + } + else if (t.Name.Equals("Exif Image Height")) + { + if (double.TryParse(GetNumber(t.Description ?? string.Empty), out dBuf)) + { + this.ImageHeight = dBuf; + } + } + else if (t.Name.Equals("Body Serial Number")) + { + this.SerialNumber = t.Description!; + } + else if (t.Name.Equals("CreateDate")) + { + this.Created = DateTime.Parse(t.Description ?? string.Empty); + } + } + } + + var gps = metadata.OfType().FirstOrDefault(); + if (gps != null) + { + if (gps.TryGetGeoLocation(out var location)) + { + this.GPSLatitude = location.Latitude; + this.GPSLongitude = location.Longitude; + double alt = 0.0d; + if (gps.TryGetDouble(GpsDirectory.TagAltitude, out alt)) + { + this.GPSAltitude = alt; + } + } + } + + + var xmp = metadata.OfType().FirstOrDefault(); + if (xmp != null) + { + foreach (var p in xmp.XmpMeta?.Properties ?? Enumerable.Empty()) + { + if (!string.IsNullOrEmpty(p.Path)) + { + if (p.Path.IndexOf("RelativeAltitude") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.DJIAltitude = dBuf; + } + } + else if (p.Path.IndexOf("GpsLatitude") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.DJILatitude = dBuf; + } + } + else if (p.Path.IndexOf("GpsLongitude") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.DJILongitude = dBuf; + } + } + else if (p.Path.IndexOf("GimbalYawDegree") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.GimbalYawDegree = dBuf; + } + } + else if (p.Path.IndexOf("FlightYawDegree") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.FlightYawDegree = dBuf; + } + } + else if (p.Path.IndexOf("CalibratedFocalLength") >= 0) + { + if (double.TryParse(GetNumber(p.Value), out dBuf)) + { + this.CalibratedFocalLength = dBuf; + } + } + else if (p.Path.IndexOf("DewarpData") >= 0) + { + this.Dewarp = new DewarpData(p.Value); + } + } + } + } + } + catch + { + stream.Close(); + throw; + } + } + } +} diff --git a/ZeroLevel.ML/KnownImageFormats.cs b/ZeroLevel.ML/KnownImageFormats.cs new file mode 100644 index 0000000..e78bc35 --- /dev/null +++ b/ZeroLevel.ML/KnownImageFormats.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.IO; + +namespace ZeroLevel.ML +{ + /// + /// Форматы изображений + /// + public static class KnownImageFormats + { + private static HashSet _formats = new HashSet() { ".bmp", ".jpeg", ".jpg", ".png", ".tiff", ".webp" }; + /// + /// Проверка, является ли файл обрабатываемым приложением как изображение + /// + public static bool IsKnownFormat(string filePath) + { + var ext = Path.GetExtension(filePath)?.ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(ext)) return false; + return _formats.Contains(ext); + } + } +} diff --git a/ZeroLevel.ML/LocationMath/CameraMath.cs b/ZeroLevel.ML/LocationMath/CameraMath.cs new file mode 100644 index 0000000..b74568b --- /dev/null +++ b/ZeroLevel.ML/LocationMath/CameraMath.cs @@ -0,0 +1,97 @@ +using System; +using System.Runtime.CompilerServices; + +namespace ZeroLevel.ML.LocationMath +{ + public sealed class CameraMath + { + public string Id { get; set; } + + public readonly double ImageWidth; + public readonly double ImageHeight; + public readonly double FocalLength; + public readonly double PixelSize; + /* + public readonly double AOVhorizontal; + public readonly double AOVvertical; + public readonly double SensorWidth; + public readonly double SensorHeight; + */ + internal readonly double VerticalToDiagonalAngleOffset; + + private static bool useCustomAltitude = false; + private static double customAltitude; + public static void UseCustomAltitude(double altitude) + { + useCustomAltitude = true; + customAltitude = altitude; + } + + public double Altitude(double altitude) => useCustomAltitude ? customAltitude : altitude; + + public CameraMath(double pixelSize, double focalLength, double imageWidth, double imageHeight) + { + PixelSize = pixelSize; + FocalLength = focalLength; + + ImageWidth = imageWidth; + ImageHeight = imageHeight; + /* + SensorWidth = pixelSize * 0.001 * imageWidth; + SensorHeight = pixelSize * 0.001 * imageHeight; + + AOVhorizontal = 2d * Math.Atan(SensorWidth / (2d * focalLength)); + AOVvertical = 2d * Math.Atan(SensorHeight / (2d * focalLength)); + */ + VerticalToDiagonalAngleOffset = Math.Atan((ImageWidth * .5) / (ImageHeight * 0.5)) * 180d / Math.PI; + } + /* + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double CaclulateImageWidth(double altitude) => 2d * Altitude(altitude) * Math.Tan(0.5d * AOVhorizontal); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double CaclulateImageHeight(double altitude) => 2d * Altitude(altitude) * Math.Tan(0.5d * AOVvertical); + + public double WidthPixelToMeters(double pixels, double altitude) + { + var Kw = pixels / ImageWidth; + var Kh = pixels / ImageHeight; + + var fieldWidth = CaclulateImageWidth(Altitude(altitude)); + var fieldHeight = CaclulateImageHeight(Altitude(altitude)); + + var w = fieldWidth * Kw; + var h = fieldHeight * Kh; + + var dist = Math.Sqrt(w * w + h * h); + return dist; + } + + public double HeightPixelToMeters(double pixels, double altitude) + { + var Kw = pixels / ImageWidth; + var Kh = pixels / ImageHeight; + + var fieldWidth = CaclulateImageWidth(Altitude(altitude)); + var fieldHeight = CaclulateImageHeight(Altitude(altitude)); + + var w = fieldWidth * Kw; + var h = fieldHeight * Kh; + + var dist = Math.Sqrt(w * w + h * h); + return dist; + } + */ + // H = h * (D - f) / f + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double PixToMillimeters(double altitude, double pixels) => (altitude * 1000d - FocalLength) * pixels * PixelSize / (1000d * FocalLength); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double PixToMeters(double altitude, double pixels) => PixToMillimeters(altitude, pixels) / 1000; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double CaclulateImageWidth(double altitude) => PixToMeters(altitude, ImageWidth); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public double CaclulateImageHeight(double altitude) => PixToMeters(altitude, ImageHeight); + } +} diff --git a/ZeroLevel.ML/LocationMath/EarthRadiusMath.cs b/ZeroLevel.ML/LocationMath/EarthRadiusMath.cs new file mode 100644 index 0000000..b0de261 --- /dev/null +++ b/ZeroLevel.ML/LocationMath/EarthRadiusMath.cs @@ -0,0 +1,31 @@ +using System; +using ZeroLevel.ML.Models; + +namespace ZeroLevel.ML.LocationMath +{ + public static class EarthRadiusMath + { + private const double a = 6378137.0d; + private const double b = 6356752.3142d; + + private const double a2 = a * a; + private const double b2 = b * b; + + public static double CalculateEarthRadius(GeoPoint location) => CalculateEarthRadius(location.Longitude); + + public static double CalculateEarthRadius(double longitudeDegrees) + { + var radLongitude = GeoMath.toRadians(longitudeDegrees); + var sinB = Math.Sin(radLongitude); + var cosB = Math.Cos(radLongitude); + var top = a2 * cosB * a2 * cosB + b2 * sinB * b2 * sinB; + var bottom = a * cosB * a * cosB + b * sinB * b * sinB; + var R = Math.Sqrt(top / bottom); + return R; + } + } +} + + + + diff --git a/ZeroLevel.ML/LocationMath/GeoMath.cs b/ZeroLevel.ML/LocationMath/GeoMath.cs new file mode 100644 index 0000000..2d54e93 --- /dev/null +++ b/ZeroLevel.ML/LocationMath/GeoMath.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.CompilerServices; + +namespace ZeroLevel.ML.LocationMath +{ + public static class GeoMath + { + /// + /// Conversion factor degrees to radians + /// + public const double DegToRad = Math.PI / 180d; //0.01745329252; // Convert Degrees to Radians + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double toRadians(double v) => v * DegToRad; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double toDegree(double v) => v * 180 / Math.PI; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double wrap90(double degrees) + { + if (-90 <= degrees && degrees <= 90) return degrees; // avoid rounding due to arithmetic ops if within range + + // latitude wrapping requires a triangle wave function; a general triangle wave is + // f(x) = 4a/p ⋅ | (x-p/4)%p - p/2 | - a + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' + double x = degrees, a = 90, p = 360; + return 4 * a / p * Math.Abs(((x - p / 4) % p + p) % p - p / 2) - a; + } + /// + /// Constrain degrees to range -180..+180 (for longitude); e.g. -181 => 179, 181 => -179. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double wrap180(double degrees) + { + if (-180 <= degrees && degrees <= 180) return degrees; // avoid rounding due to arithmetic ops if within range + + // longitude wrapping requires a sawtooth wave function; a general sawtooth wave is + // f(x) = (2ax/p - p/2) % p - a + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' + double x = degrees, a = 180, p = 360; + return ((2 * a * x / p - p / 2) % p + p) % p - a; + } + /// + /// Constrain degrees to range 0..360 (for bearings); e.g. -1 => 359, 361 => 1. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double wrap360(double degrees) + { + if (0 <= degrees && degrees < 360) return degrees; // avoid rounding due to arithmetic ops if within range + + // bearing wrapping requires a sawtooth wave function with a vertical offset equal to the + // amplitude and a corresponding phase shift; this changes the general sawtooth wave function from + // f(x) = (2ax/p - p/2) % p - a + // to + // f(x) = (2ax/p) % p + // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator + // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n' + double x = degrees, a = 180, p = 360; + return (2 * a * x / p % p + p) % p; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double YawToBearing(double a) + { + return (a + 360) % 360; + } + + /// + /// Calculate the difference between two longitudal values constrained 0 - 180 deg + /// + /// The first longitue value in degrees + /// The second longitue value in degrees + /// The distance in degrees + public static double DiffLongitude(double lon1, double lon2) + { + double diff; + + if (lon1 > 180.0) + lon1 = 360.0 - lon1; + if (lon2 > 180.0) + lon2 = 360.0 - lon2; + + if (lon1 >= 0.0 && lon2 >= 0.0) + diff = lon2 - lon1; + else if (lon1 < 0.0 && lon2 < 0.0) + diff = lon2 - lon1; + else + { + // different hemispheres + if (lon1 < 0) + lon1 = -1 * lon1; + if (lon2 < 0) + lon2 = -1 * lon2; + diff = lon1 + lon2; + if (diff > 180.0) + diff = 360.0 - diff; + } + return diff; + } + } +} diff --git a/ZeroLevel.ML/LocationMath/Geometry.cs b/ZeroLevel.ML/LocationMath/Geometry.cs new file mode 100644 index 0000000..c8fee54 --- /dev/null +++ b/ZeroLevel.ML/LocationMath/Geometry.cs @@ -0,0 +1,55 @@ +using System; +using ZeroLevel.ML.Models; + +namespace ZeroLevel.ML.LocationMath +{ + public static class Geometry + { + public static Point2D GeoPointCoordinateInArea(GeoPoint point, GeoPoint[] corners) + { + var distanceToTLTR = Math.Abs(point.crossTrackDistanceTo(corners[0], corners[1])); + var distanceToTLBL = Math.Abs(point.crossTrackDistanceTo(corners[0], corners[3])); + + var distanceTLTR = Math.Abs(corners[0].DistanceToPoint(corners[1])); + var distanceTLBL = Math.Abs(corners[0].DistanceToPoint(corners[3])); + + return new Point2D(distanceToTLBL / distanceTLTR, distanceToTLTR / distanceTLBL); + } + + + public static bool IsGeoPointInArea(GeoPoint point, GeoPoint[] corners) + { + var toTop = point.crossTrackDistanceTo(corners[0], corners[1]); + var toBottom = point.crossTrackDistanceTo(corners[3], corners[1]); + + if (toTop < 0 && toBottom < 0) return false; + if (toTop > 0 && toBottom > 0) return false; + + + var toLeft = point.crossTrackDistanceTo(corners[3], corners[0]); + var toRight = point.crossTrackDistanceTo(corners[2], corners[1]); + + if (toLeft < 0 && toRight < 0) return false; + if (toLeft > 0 && toRight > 0) return false; + + return true; + } + + public static bool IsPointInPolygon(GeoPoint[] poly, GeoPoint point) + { + int i, j; + bool c = false; + for (i = 0, j = poly.Length - 1; i < poly.Length; j = i++) + { + if ((((poly[i].Latitude <= point.Latitude) && (point.Latitude < poly[j].Latitude)) + || ((poly[j].Latitude <= point.Latitude) && (point.Latitude < poly[i].Latitude))) + && (point.Longitude < (poly[j].Longitude - poly[i].Longitude) * (point.Latitude - poly[i].Latitude) + / (poly[j].Latitude - poly[i].Latitude) + poly[i].Longitude)) + { + c = !c; + } + } + return c; + } + } +} diff --git a/ZeroLevel.ML/LocationMath/ViewMath.cs b/ZeroLevel.ML/LocationMath/ViewMath.cs new file mode 100644 index 0000000..de740fd --- /dev/null +++ b/ZeroLevel.ML/LocationMath/ViewMath.cs @@ -0,0 +1,79 @@ +using System; +using ZeroLevel.ML.Models; + +namespace ZeroLevel.ML.LocationMath +{ + public static class ViewMath + { + public static GeoPoint[] CalculateCornerGeopoints(CameraMath camera, GeoPoint center, double flightYaw) + { + // True North direction + var bearing = GeoMath.YawToBearing(flightYaw); + + // Distances in meters + var imageWidthInMeters = camera.CaclulateImageWidth(center.Altditude); + var imageHeightInMeters = camera.CaclulateImageHeight(center.Altditude); + var diagonalInMeters = Math.Sqrt(imageWidthInMeters * imageWidthInMeters + imageHeightInMeters * imageHeightInMeters); + var distanceInMeters = diagonalInMeters * 0.5d; + + // Directions to corners + var topLeftDirection = GeoMath.YawToBearing(bearing - camera.VerticalToDiagonalAngleOffset); + var topRightDirection = GeoMath.YawToBearing(bearing + camera.VerticalToDiagonalAngleOffset); + var bottomLeftDirection = GeoMath.YawToBearing(bearing + 180d + camera.VerticalToDiagonalAngleOffset); + var bottomRightDirection = GeoMath.YawToBearing(bearing + 180d - camera.VerticalToDiagonalAngleOffset); + + // Corners locations + var topLeft = center.CalculateDestinationPoint(distanceInMeters, topLeftDirection); + var topRight = center.CalculateDestinationPoint(distanceInMeters, topRightDirection); + var bottomRight = center.CalculateDestinationPoint(distanceInMeters, bottomRightDirection); + var bottomLeft = center.CalculateDestinationPoint(distanceInMeters, bottomLeftDirection); + + return new[] { topLeft, topRight, bottomRight, bottomLeft }; + } + + public static GeoPoint CalculateBoxGeopoint(CameraMath camera, GeoPoint center, double flightYaw, Point2D point) + { + var dx = Math.Abs(camera.ImageWidth * 0.5 - point.x); + var dy = Math.Abs(camera.ImageHeight * 0.5 - point.y); + // Если точка находится вблизи центра снимка, нет смысла считать смещение + if (dx < 10f && dy < 10f) + { + return center; + } + + // Переход в 0; 0 + var xn = point.x - camera.ImageWidth * 0.5; + if (Math.Abs(xn) < double.Epsilon) + { + xn = 0.000001; + } + var yn = camera.ImageHeight * 0.5 - point.y; + var distanceToPointInPixels = Math.Sqrt(dx * dx + dy * dy); + + // Точка на окружности вертикально вверх + var x0 = 0; + var y0 = distanceToPointInPixels; + + var angle = GetAngle(x0, y0, xn, yn); + var bearing = GeoMath.YawToBearing(flightYaw); + var bearingToPoint = GeoMath.YawToBearing(bearing + angle); + + // рассчитать расстояние и пеленг бокса относительно центральной точки + var distanceInMeters = camera.PixToMeters(center.Altditude, distanceToPointInPixels); + + return center.CalculateDestinationPoint(distanceInMeters, bearingToPoint); + } + + /// + /// Угол между двумя отрезками, с общей точкой в 0;0 + /// + private static double GetAngle(double x0, double y0, double x2, double y2) + { + var x1 = 0; + var y1 = 0; + var a1 = -(Math.Atan2(y0 - y1, x0 - x1) * 180 / Math.PI - 90); + var a2 = -(Math.Atan2(y2 - y1, x2 - x1) * 180 / Math.PI - 90); + return (a2 - a1); + } + } +} diff --git a/ZeroLevel.ML/Models/DmsMs.cs b/ZeroLevel.ML/Models/DmsMs.cs new file mode 100644 index 0000000..f0e5fa1 --- /dev/null +++ b/ZeroLevel.ML/Models/DmsMs.cs @@ -0,0 +1,66 @@ +namespace ZeroLevel.ML.Models +{ + public enum PointType { Lat, Lon } + public class DmsMsPoint + { + public int Degrees { get; set; } + public int Minutes { get; set; } + public int Seconds { get; set; } + public int Milliseconds { get; set; } + public PointType Type { get; set; } + + public override string ToString() + { + var letter = Type == PointType.Lat + ? Degrees < 0 ? "S" : "N" + : Degrees < 0 ? "W" : "E"; + return $"{Degrees}°{Minutes}'{Seconds}.{Milliseconds.ToString("D3")}\"{letter}"; + } + } + + public sealed class DmsMsLocation + { + private DmsMsLocation() + { + Latitude = null!; + Longitude = null!; + } + public DmsMsPoint Latitude { get; set; } + public DmsMsPoint Longitude { get; set; } + + public override string ToString() + { + return string.Format("{0}, {1}", + Latitude, Longitude); + } + + public static DmsMsLocation CreateFromDouble(double latitude, double longitude) + { + return new DmsMsLocation + { + Latitude = Extract(latitude, PointType.Lat), + Longitude = Extract(longitude, PointType.Lon) + }; + } + + static DmsMsPoint Extract(double value, PointType type) + { + var d = (int)value; + var tmp = (value - d) * 3600; + var m = (int)(tmp / 60); + var st = tmp % 60; + var s = (int)st; + var ms = (int)((st - s) * 1000); + return new DmsMsPoint { Type = type, Degrees = d, Minutes = m, Seconds = s, Milliseconds = ms }; + } + + public static DmsMsLocation CreateFromDouble(GeoPoint decimalLocation) + { + if (decimalLocation == null) + { + return null!; + } + return CreateFromDouble(decimalLocation.Latitude, decimalLocation.Longitude); + } + } +} \ No newline at end of file diff --git a/ZeroLevel.ML/Models/GeoPoint.cs b/ZeroLevel.ML/Models/GeoPoint.cs new file mode 100644 index 0000000..c67902b --- /dev/null +++ b/ZeroLevel.ML/Models/GeoPoint.cs @@ -0,0 +1,113 @@ +using System; +using ZeroLevel.ML.LocationMath; + +namespace ZeroLevel.ML.Models +{ + public sealed class GeoPoint + { + /// + /// latitude in degrees + /// + public double Latitude { get; private set; } + /// + /// longitude in degrees + /// + public double Longitude { get; private set; } + /// + /// altitude in meters + /// + public double Altditude { get; private set; } + + public GeoPoint(double lat, double lon) + { + Latitude = GeoMath.wrap90(lat); + Longitude = GeoMath.wrap180(lon); + Altditude = 0; + } + + public GeoPoint(double lat, double lon, double alt) + { + Latitude = GeoMath.wrap90(lat); + Longitude = GeoMath.wrap180(lon); + Altditude = alt; + } + + public double RadLatitude => GeoMath.toRadians(Latitude); + public double RadLongitude => GeoMath.toRadians(Longitude); + + public GeoPoint CalculateDestinationPoint(double distance, double heading) + { + distance /= EarthRadiusMath.CalculateEarthRadius(Longitude); + heading = GeoMath.toRadians(heading); + // http://williams.best.vwh.net/avform.htm#LL + double fromLat = RadLatitude; + double fromLng = RadLongitude; + double cosDistance = Math.Cos(distance); + double sinDistance = Math.Sin(distance); + double sinFromLat = Math.Sin(fromLat); + double cosFromLat = Math.Cos(fromLat); + double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * Math.Cos(heading); + double dLng = Math.Atan2(sinDistance * cosFromLat * Math.Sin(heading), cosDistance - sinFromLat * sinLat); + return new GeoPoint(GeoMath.toDegree(Math.Asin(sinLat)), GeoMath.toDegree(fromLng + dLng)); + } + + public double DistanceToPoint(GeoPoint target) + { + var earthRadius = EarthRadiusMath.CalculateEarthRadius(target.Longitude); + var dLat = GeoMath.toRadians(target.Latitude - Latitude); + var dLon = GeoMath.toRadians(target.Longitude - Longitude); + var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(RadLatitude) * Math.Cos(target.RadLatitude) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2); + var c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a))); + var d = earthRadius * c; + return d; + } + + public GeoPoint ShortDistanceMidpointTo(GeoPoint point) + { + var lat = 0.5 * (Latitude + point.Latitude); + var lon = 0.5 * (Longitude + point.Longitude); + var alt = 0.5 * (Altditude + point.Altditude); + return new GeoPoint(lat, lon, alt); + } + + public GeoPoint MidpointTo(GeoPoint point) + { + var φ1 = RadLatitude; + var λ1 = RadLongitude; + var φ2 = point.RadLatitude; + var Δλ = GeoMath.toRadians(GeoMath.DiffLongitude(point.Longitude, Longitude)); + + + var A = new Point3D(x: Math.Cos(φ1), y: 0, z: Math.Sin(φ1)); + var B = new Point3D(x: Math.Cos(φ2) * Math.Cos(Δλ), y: Math.Cos(φ2) * Math.Sin(Δλ), z: Math.Sin(φ2)); + var C = new Point3D(x: A.x + B.x, y: A.y + B.y, z: A.z + B.z); + + var φm = Math.Atan2(C.z, Math.Sqrt(C.x * C.x + C.y * C.y)); + var λm = λ1 + Math.Atan2(C.y, C.x); + + var alt = 0.5 * (Altditude + point.Altditude); + + return new GeoPoint(GeoMath.toDegree(φm), GeoMath.toDegree(λm), alt); + } + + public double GetBearing(GeoPoint p2) + { + var latitude1 = RadLatitude; + var latitude2 = p2.RadLatitude; + var longitudeDifference = GeoMath.toRadians(p2.Longitude - Longitude); + var y = Math.Sin(longitudeDifference) * Math.Cos(latitude2); + var x = Math.Cos(latitude1) * Math.Sin(latitude2) - Math.Sin(latitude1) * Math.Cos(latitude2) * Math.Cos(longitudeDifference); + return (GeoMath.toDegree(Math.Atan2(y, x)) + 360) % 360; + } + + public double crossTrackDistanceTo(GeoPoint pathStart, GeoPoint pathEnd) + { + var R = EarthRadiusMath.CalculateEarthRadius(this); + var δ13 = pathStart.DistanceToPoint(this) / R; + var θ13 = GeoMath.toRadians(pathStart.GetBearing(this)); + var θ12 = GeoMath.toRadians(pathStart.GetBearing(pathEnd)); + var δxt = Math.Asin(Math.Sin(δ13) * Math.Sin(θ13 - θ12)); + return δxt * R; + } + } +} diff --git a/ZeroLevel.ML/Models/GeoPolygon.cs b/ZeroLevel.ML/Models/GeoPolygon.cs new file mode 100644 index 0000000..c2922fb --- /dev/null +++ b/ZeroLevel.ML/Models/GeoPolygon.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace ZeroLevel.ML.Models +{ + /// + /// Полигон из геоточек + /// + public class GeoPolygon + { + public List Points { get; set; } + } +} diff --git a/ZeroLevel.ML/Models/Points.cs b/ZeroLevel.ML/Models/Points.cs new file mode 100644 index 0000000..7be80e0 --- /dev/null +++ b/ZeroLevel.ML/Models/Points.cs @@ -0,0 +1,5 @@ +namespace ZeroLevel.ML.Models +{ + public record Point2D(double x, double y); + public record Point3D(double x, double y, double z); +} diff --git a/ZeroLevel.ML/Models/TrackPoint.cs b/ZeroLevel.ML/Models/TrackPoint.cs new file mode 100644 index 0000000..28ab181 --- /dev/null +++ b/ZeroLevel.ML/Models/TrackPoint.cs @@ -0,0 +1,6 @@ +using System; + +namespace ZeroLevel.ML.Models +{ + public sealed record TrackPoint(double Latitude, double Longitude, double Altitude, DateTime DateTime); +} diff --git a/ZeroLevel.ML/Services/ImageRegionReader.cs b/ZeroLevel.ML/Services/ImageRegionReader.cs new file mode 100644 index 0000000..79b5083 --- /dev/null +++ b/ZeroLevel.ML/Services/ImageRegionReader.cs @@ -0,0 +1,147 @@ +extern alias CoreDrawing; + +using System; +using ZeroLevel.ML.DNN.Models; + +namespace ZeroLevel.ML.Services +{ + internal sealed class ImageRegionReader + { + private readonly CoreDrawing.System.Drawing.Imaging.BitmapData _bitmapData; + private readonly RegionInfo _regionInfo; + + public int TensorIndex => _regionInfo.Index; + public int X => _regionInfo.X; + public int Y => _regionInfo.Y; + + public ImageRegionReader(CoreDrawing.System.Drawing.Imaging.BitmapData bitmapData, RegionInfo regionInfo) + { + _bitmapData = bitmapData; + _regionInfo = regionInfo; + } + + /// + /// dx, dy, r, g, b + /// + /// + public void Read(Action handler) + { + throw new NotImplementedException(); + unsafe + { + var cropSizeX = _regionInfo.CropSizeX; + var cropSizeY = _regionInfo.CropSizeY; + var right = _regionInfo.X + _regionInfo.CropSizeX; + if (right > _bitmapData.Width) + { + cropSizeX = _bitmapData.Width - _regionInfo.X; + } + var bottom = _regionInfo.Y + _regionInfo.CropSizeY; + if (bottom > _bitmapData.Height) + { + cropSizeY = _bitmapData.Height - _regionInfo.Y; + } + byte* scan0 = (byte*)_bitmapData.Scan0.ToPointer(); + for (var dy = 0; dy < cropSizeY; dy++) + { + byte* row = scan0 + (_regionInfo.Y + dy + 0) * _regionInfo.Stride; + for (var dx = 0; dx < cropSizeX; dx++) + { + int bIndex = (dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + int gIndex = bIndex + 1; + int rIndex = bIndex + 2; + int b = row[rIndex]; + int g = row[gIndex]; + int r = row[bIndex]; + handler.Invoke(dx, dy, r, g, b); + } + } + } + } + + + public void FillTensor(TensorPoolItem tensor) + { + + unsafe + { + var cropSizeX = _regionInfo.CropSizeX; + var cropSizeY = _regionInfo.CropSizeY; + var right = _regionInfo.X + _regionInfo.CropSizeX; + if (right > _bitmapData.Width) + { + cropSizeX = _bitmapData.Width - _regionInfo.X; + } + var bottom = _regionInfo.Y + _regionInfo.CropSizeY; + if (bottom > _bitmapData.Height) + { + cropSizeY = _bitmapData.Height - _regionInfo.Y; + } + byte* scan0 = (byte*)_bitmapData.Scan0.ToPointer(); + + var x_l = ((int)Math.Ceiling((double)(cropSizeX / 8))) * 8; + var y_l = ((int)Math.Ceiling((double)(cropSizeY / 8))) * 8; + int idx; + + for (var dy = 0; dy < cropSizeY; dy++) + { + byte* row = scan0 + (_regionInfo.Y + dy + 0) * _regionInfo.Stride; + + for (var dx = 0; dx < x_l; dx += 8) + { + idx = (0 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 0, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 0, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 0, dy] = row[idx + 2]; + + idx = (1 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 1, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 1, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 1, dy] = row[idx + 2]; + + idx = (2 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 2, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 2, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 2, dy] = row[idx + 2]; + + idx = (3 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 3, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 3, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 3, dy] = row[idx + 2]; + + idx = (4 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 4, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 4, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 4, dy] = row[idx + 2]; + + idx = (5 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 5, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 5, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 5, dy] = row[idx + 2]; + + idx = (6 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 6, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 6, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 6, dy] = row[idx + 2]; + + idx = (7 + dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx + 7, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx + 7, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx + 7, dy] = row[idx + 2]; + } + + if (x_l < cropSizeX) + { + for(var dx = x_l; dx <= cropSizeX; dx++) + { + idx = (dx + _regionInfo.X) * _regionInfo.BytesPerPixel; + tensor.Tensor[tensor.TensorIndex, 0, dx, dy] = row[idx + 0]; + tensor.Tensor[tensor.TensorIndex, 1, dx, dy] = row[idx + 1]; + tensor.Tensor[tensor.TensorIndex, 2, dx, dy] = row[idx + 2]; + } + } + } + } + } + } +} diff --git a/ZeroLevel.ML/Services/ImageScanner.cs b/ZeroLevel.ML/Services/ImageScanner.cs new file mode 100644 index 0000000..0796067 --- /dev/null +++ b/ZeroLevel.ML/Services/ImageScanner.cs @@ -0,0 +1,84 @@ +extern alias CoreDrawing; + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ZeroLevel.ML.Services +{ + internal sealed class ImageScanner + : IDisposable + { + private readonly CoreDrawing.System.Drawing.Bitmap _image; + private readonly CoreDrawing.System.Drawing.Imaging.BitmapData _bitmapData; + private readonly int _stride; + private readonly int _bytesPerPixel; + private readonly int _cropSizeX; + private readonly int _cropSizeY; + private readonly int _total; + private readonly int[] _x_points; + private readonly int[] _y_points; + + public int CropSizeX => _cropSizeX; + public int CropSizeY => _cropSizeY; + public int TotalRegions => _total; + public ImageScanner(CoreDrawing.System.Drawing.Bitmap image, int cropSize) + { + _image = image; + _bitmapData = _image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height), + CoreDrawing.System.Drawing.Imaging.ImageLockMode.ReadOnly, + CoreDrawing.System.Drawing.Imaging.PixelFormat.Format24bppRgb); + _stride = Math.Abs(_bitmapData.Stride); + _bytesPerPixel = _stride / image.Width; + _cropSizeX = cropSize > 0 ? cropSize : _image.Width; + _cropSizeY = cropSize > 0 ? cropSize : _image.Height; + _x_points = SplitRange(_image.Width, _cropSizeX, 0).ToArray(); + _y_points = SplitRange(_image.Height, _cropSizeY, 0).ToArray(); + _total = _x_points.Length * _y_points.Length; + } + + public IEnumerable ScanByRegions() + { + var tensorIndex = 0; + foreach (var x in _x_points) + { + foreach (var y in _y_points) + { + var region = new RegionInfo { Index = tensorIndex, X = x, Y = y, CropSizeX = _cropSizeX, CropSizeY = _cropSizeY, Stride = _stride, BytesPerPixel = _bytesPerPixel }; + yield return new ImageRegionReader(_bitmapData, region); + tensorIndex++; + } + } + } + + private IEnumerable SplitRange(int size, int cropSize, float overlapProportion) + { + var stride = (int)(cropSize * (1f - overlapProportion)); + var counter = 0; + while (true) + { + var pt = stride * counter; + if (pt + cropSize > size) + { + if (cropSize == size || pt == size) + { + break; + } + yield return size - cropSize; + break; + } + else + { + yield return pt; + } + counter++; + } + } + + public void Dispose() + { + _image.UnlockBits(_bitmapData); + _image.Dispose(); + } + } +} diff --git a/ZeroLevel.ML/Services/RegionInfo.cs b/ZeroLevel.ML/Services/RegionInfo.cs new file mode 100644 index 0000000..e779166 --- /dev/null +++ b/ZeroLevel.ML/Services/RegionInfo.cs @@ -0,0 +1,13 @@ +namespace ZeroLevel.ML.Services +{ + internal struct RegionInfo + { + public int Index; + public int X; + public int Y; + public int Stride; + public int CropSizeX; + public int CropSizeY; + public int BytesPerPixel; + } +} diff --git a/ZeroLevel.ML/ZeroLevel.ML.csproj b/ZeroLevel.ML/ZeroLevel.ML.csproj new file mode 100644 index 0000000..ed005df --- /dev/null +++ b/ZeroLevel.ML/ZeroLevel.ML.csproj @@ -0,0 +1,23 @@ + + + + latest + netstandard2.1 + enable + True + + + + + CoreDrawing + + + + + + + + + + + diff --git a/ZeroLevel.SQL/BaseSqlDbMapper.cs b/ZeroLevel.MsSql/BaseSqlDbMapper.cs similarity index 99% rename from ZeroLevel.SQL/BaseSqlDbMapper.cs rename to ZeroLevel.MsSql/BaseSqlDbMapper.cs index c943058..985e108 100644 --- a/ZeroLevel.SQL/BaseSqlDbMapper.cs +++ b/ZeroLevel.MsSql/BaseSqlDbMapper.cs @@ -4,7 +4,7 @@ using System.Data; using System.Data.SqlClient; using System.Text; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public abstract class BaseSqlDbMapper { diff --git a/ZeroLevel.SQL/Contracts/BaseEntity.cs b/ZeroLevel.MsSql/Contracts/BaseEntity.cs similarity index 98% rename from ZeroLevel.SQL/Contracts/BaseEntity.cs rename to ZeroLevel.MsSql/Contracts/BaseEntity.cs index 34283ee..5d4dc41 100644 --- a/ZeroLevel.SQL/Contracts/BaseEntity.cs +++ b/ZeroLevel.MsSql/Contracts/BaseEntity.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { [DataContract] [Serializable] diff --git a/ZeroLevel.SQL/Contracts/BaseVersionedEntity.cs b/ZeroLevel.MsSql/Contracts/BaseVersionedEntity.cs similarity index 97% rename from ZeroLevel.SQL/Contracts/BaseVersionedEntity.cs rename to ZeroLevel.MsSql/Contracts/BaseVersionedEntity.cs index e8d9b33..a9c982a 100644 --- a/ZeroLevel.SQL/Contracts/BaseVersionedEntity.cs +++ b/ZeroLevel.MsSql/Contracts/BaseVersionedEntity.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { [DataContract] [Serializable] diff --git a/ZeroLevel.SQL/Contracts/DbIndexAttribute.cs b/ZeroLevel.MsSql/Contracts/DbIndexAttribute.cs similarity index 79% rename from ZeroLevel.SQL/Contracts/DbIndexAttribute.cs rename to ZeroLevel.MsSql/Contracts/DbIndexAttribute.cs index 31f6008..8449d75 100644 --- a/ZeroLevel.SQL/Contracts/DbIndexAttribute.cs +++ b/ZeroLevel.MsSql/Contracts/DbIndexAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbIndexAttribute : Attribute { diff --git a/ZeroLevel.SQL/Contracts/DbMemberAttribute.cs b/ZeroLevel.MsSql/Contracts/DbMemberAttribute.cs similarity index 97% rename from ZeroLevel.SQL/Contracts/DbMemberAttribute.cs rename to ZeroLevel.MsSql/Contracts/DbMemberAttribute.cs index c93c7de..de6f375 100644 --- a/ZeroLevel.SQL/Contracts/DbMemberAttribute.cs +++ b/ZeroLevel.MsSql/Contracts/DbMemberAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbMemberAttribute : Attribute { diff --git a/ZeroLevel.SQL/Contracts/IDbField.cs b/ZeroLevel.MsSql/Contracts/IDbField.cs similarity index 91% rename from ZeroLevel.SQL/Contracts/IDbField.cs rename to ZeroLevel.MsSql/Contracts/IDbField.cs index f8d41cf..239bd9c 100644 --- a/ZeroLevel.SQL/Contracts/IDbField.cs +++ b/ZeroLevel.MsSql/Contracts/IDbField.cs @@ -1,7 +1,7 @@ using System.Data; using ZeroLevel.Services.ObjectMapping; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface IDbField: IMemberInfo diff --git a/ZeroLevel.SQL/Contracts/IDbMapper.cs b/ZeroLevel.MsSql/Contracts/IDbMapper.cs similarity index 96% rename from ZeroLevel.SQL/Contracts/IDbMapper.cs rename to ZeroLevel.MsSql/Contracts/IDbMapper.cs index b9206e0..5c34afd 100644 --- a/ZeroLevel.SQL/Contracts/IDbMapper.cs +++ b/ZeroLevel.MsSql/Contracts/IDbMapper.cs @@ -2,7 +2,7 @@ using System.Data; using System.Data.Common; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface IDbMapper { diff --git a/ZeroLevel.SQL/Contracts/IDbProvider.cs b/ZeroLevel.MsSql/Contracts/IDbProvider.cs similarity index 97% rename from ZeroLevel.SQL/Contracts/IDbProvider.cs rename to ZeroLevel.MsSql/Contracts/IDbProvider.cs index fccba2c..87789c1 100644 --- a/ZeroLevel.SQL/Contracts/IDbProvider.cs +++ b/ZeroLevel.MsSql/Contracts/IDbProvider.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface IDbProvider { diff --git a/ZeroLevel.SQL/Contracts/IEntity.cs b/ZeroLevel.MsSql/Contracts/IEntity.cs similarity index 86% rename from ZeroLevel.SQL/Contracts/IEntity.cs rename to ZeroLevel.MsSql/Contracts/IEntity.cs index ef188d7..05a1ff3 100644 --- a/ZeroLevel.SQL/Contracts/IEntity.cs +++ b/ZeroLevel.MsSql/Contracts/IEntity.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface IEntity : ICloneable { diff --git a/ZeroLevel.SQL/Contracts/ISqlServerSpecification.cs b/ZeroLevel.MsSql/Contracts/ISqlServerSpecification.cs similarity index 84% rename from ZeroLevel.SQL/Contracts/ISqlServerSpecification.cs rename to ZeroLevel.MsSql/Contracts/ISqlServerSpecification.cs index f09a5e5..9e7b2a4 100644 --- a/ZeroLevel.SQL/Contracts/ISqlServerSpecification.cs +++ b/ZeroLevel.MsSql/Contracts/ISqlServerSpecification.cs @@ -1,6 +1,6 @@ using System.Data.SqlClient; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface ISqlServerSpecification { diff --git a/ZeroLevel.SQL/Contracts/IVersionedEntity.cs b/ZeroLevel.MsSql/Contracts/IVersionedEntity.cs similarity index 74% rename from ZeroLevel.SQL/Contracts/IVersionedEntity.cs rename to ZeroLevel.MsSql/Contracts/IVersionedEntity.cs index c69b174..d760c9c 100644 --- a/ZeroLevel.SQL/Contracts/IVersionedEntity.cs +++ b/ZeroLevel.MsSql/Contracts/IVersionedEntity.cs @@ -1,4 +1,4 @@ -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public interface IVersionedEntity : IEntity { diff --git a/ZeroLevel.SQL/Contracts/ZSqlCommand.cs b/ZeroLevel.MsSql/Contracts/ZSqlCommand.cs similarity index 82% rename from ZeroLevel.SQL/Contracts/ZSqlCommand.cs rename to ZeroLevel.MsSql/Contracts/ZSqlCommand.cs index 98f8012..08a7642 100644 --- a/ZeroLevel.SQL/Contracts/ZSqlCommand.cs +++ b/ZeroLevel.MsSql/Contracts/ZSqlCommand.cs @@ -1,6 +1,6 @@ using System.Data.Common; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class ZSqlCommand { diff --git a/ZeroLevel.SQL/DBReader.cs b/ZeroLevel.MsSql/DBReader.cs similarity index 99% rename from ZeroLevel.SQL/DBReader.cs rename to ZeroLevel.MsSql/DBReader.cs index 832b373..8ab19d1 100644 --- a/ZeroLevel.SQL/DBReader.cs +++ b/ZeroLevel.MsSql/DBReader.cs @@ -3,7 +3,7 @@ using System.Data; using System.Data.Common; using System.Linq; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbReader : IDisposable diff --git a/ZeroLevel.SQL/DDD/IdentitySpecification.cs b/ZeroLevel.MsSql/DDD/IdentitySpecification.cs similarity index 97% rename from ZeroLevel.SQL/DDD/IdentitySpecification.cs rename to ZeroLevel.MsSql/DDD/IdentitySpecification.cs index ed1642a..d288b7a 100644 --- a/ZeroLevel.SQL/DDD/IdentitySpecification.cs +++ b/ZeroLevel.MsSql/DDD/IdentitySpecification.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using ZeroLevel.Specification; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { [DataContract] [Serializable] diff --git a/ZeroLevel.SQL/DDD/SqlIdentitySpecification.cs b/ZeroLevel.MsSql/DDD/SqlIdentitySpecification.cs similarity index 96% rename from ZeroLevel.SQL/DDD/SqlIdentitySpecification.cs rename to ZeroLevel.MsSql/DDD/SqlIdentitySpecification.cs index 04b6fed..d33dadb 100644 --- a/ZeroLevel.SQL/DDD/SqlIdentitySpecification.cs +++ b/ZeroLevel.MsSql/DDD/SqlIdentitySpecification.cs @@ -2,7 +2,7 @@ using System.Data.SqlClient; using ZeroLevel.Specification; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class SqlIdentitySpecification : IdentitySpecification, ISqlServerSpecification diff --git a/ZeroLevel.SQL/DbField.cs b/ZeroLevel.MsSql/DbField.cs similarity index 99% rename from ZeroLevel.SQL/DbField.cs rename to ZeroLevel.MsSql/DbField.cs index bf6f780..408ec16 100644 --- a/ZeroLevel.SQL/DbField.cs +++ b/ZeroLevel.MsSql/DbField.cs @@ -4,7 +4,7 @@ using System.Reflection; using ZeroLevel.Services.ObjectMapping; using ZeroLevel.Services.Reflection; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbField : MapMemberInfo, IDbField { diff --git a/ZeroLevel.SQL/DbMapper.cs b/ZeroLevel.MsSql/DbMapper.cs similarity index 99% rename from ZeroLevel.SQL/DbMapper.cs rename to ZeroLevel.MsSql/DbMapper.cs index c5b8065..c05da1c 100644 --- a/ZeroLevel.SQL/DbMapper.cs +++ b/ZeroLevel.MsSql/DbMapper.cs @@ -5,7 +5,7 @@ using System.Data.Common; using System.Linq; using System.Reflection; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbMapper: IDbMapper { diff --git a/ZeroLevel.SQL/DbMapperFactory.cs b/ZeroLevel.MsSql/DbMapperFactory.cs similarity index 98% rename from ZeroLevel.SQL/DbMapperFactory.cs rename to ZeroLevel.MsSql/DbMapperFactory.cs index fc1fad8..edcf79b 100644 --- a/ZeroLevel.SQL/DbMapperFactory.cs +++ b/ZeroLevel.MsSql/DbMapperFactory.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public static class DbMapperFactory { diff --git a/ZeroLevel.SQL/DbTypeMapper.cs b/ZeroLevel.MsSql/DbTypeMapper.cs similarity index 99% rename from ZeroLevel.SQL/DbTypeMapper.cs rename to ZeroLevel.MsSql/DbTypeMapper.cs index e6787aa..8d1b336 100644 --- a/ZeroLevel.SQL/DbTypeMapper.cs +++ b/ZeroLevel.MsSql/DbTypeMapper.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Data.SqlClient; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public static class DbTypeMapper { diff --git a/ZeroLevel.SQL/GenericDbMapper.cs b/ZeroLevel.MsSql/GenericDbMapper.cs similarity index 97% rename from ZeroLevel.SQL/GenericDbMapper.cs rename to ZeroLevel.MsSql/GenericDbMapper.cs index b69a3d7..d6ce641 100644 --- a/ZeroLevel.SQL/GenericDbMapper.cs +++ b/ZeroLevel.MsSql/GenericDbMapper.cs @@ -2,7 +2,7 @@ using System.Data; using System.Data.Common; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class DbMapper : DbMapper, IDbMapper { diff --git a/ZeroLevel.SQL/GenericSqlDbMapper.cs b/ZeroLevel.MsSql/GenericSqlDbMapper.cs similarity index 97% rename from ZeroLevel.SQL/GenericSqlDbMapper.cs rename to ZeroLevel.MsSql/GenericSqlDbMapper.cs index ca8d6a8..fe83ba5 100644 --- a/ZeroLevel.SQL/GenericSqlDbMapper.cs +++ b/ZeroLevel.MsSql/GenericSqlDbMapper.cs @@ -2,7 +2,7 @@ using System.Data; using System.Data.Common; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class SqlDbMapper : BaseSqlDbMapper { diff --git a/ZeroLevel.SQL/SqlDbConnectionFactory.cs b/ZeroLevel.MsSql/SqlDbConnectionFactory.cs similarity index 99% rename from ZeroLevel.SQL/SqlDbConnectionFactory.cs rename to ZeroLevel.MsSql/SqlDbConnectionFactory.cs index 4bfa71f..cd817ac 100644 --- a/ZeroLevel.SQL/SqlDbConnectionFactory.cs +++ b/ZeroLevel.MsSql/SqlDbConnectionFactory.cs @@ -3,7 +3,7 @@ using System.Data.SqlClient; using System.Globalization; using System.Security.Permissions; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public sealed class SqlDbConnectionFactory { diff --git a/ZeroLevel.SQL/SqlDbInfo.cs b/ZeroLevel.MsSql/SqlDbInfo.cs similarity index 99% rename from ZeroLevel.SQL/SqlDbInfo.cs rename to ZeroLevel.MsSql/SqlDbInfo.cs index 3354c86..d97c98d 100644 --- a/ZeroLevel.SQL/SqlDbInfo.cs +++ b/ZeroLevel.MsSql/SqlDbInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Globalization; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public sealed class SqlDbInfo { diff --git a/ZeroLevel.SQL/SqlDbMapper.cs b/ZeroLevel.MsSql/SqlDbMapper.cs similarity index 95% rename from ZeroLevel.SQL/SqlDbMapper.cs rename to ZeroLevel.MsSql/SqlDbMapper.cs index c8ca8af..26b840d 100644 --- a/ZeroLevel.SQL/SqlDbMapper.cs +++ b/ZeroLevel.MsSql/SqlDbMapper.cs @@ -1,7 +1,7 @@ using System; using System.Data; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class SqlDbMapper : BaseSqlDbMapper { diff --git a/ZeroLevel.SQL/SqlDbProvider.cs b/ZeroLevel.MsSql/SqlDbProvider.cs similarity index 99% rename from ZeroLevel.SQL/SqlDbProvider.cs rename to ZeroLevel.MsSql/SqlDbProvider.cs index 98a5c79..732eebf 100644 --- a/ZeroLevel.SQL/SqlDbProvider.cs +++ b/ZeroLevel.MsSql/SqlDbProvider.cs @@ -5,7 +5,7 @@ using System.Data.Common; using System.Data.SqlClient; using System.Linq; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class SqlDbProvider : IDbProvider diff --git a/ZeroLevel.SQL/SqlDbRepository.cs b/ZeroLevel.MsSql/SqlDbRepository.cs similarity index 99% rename from ZeroLevel.SQL/SqlDbRepository.cs rename to ZeroLevel.MsSql/SqlDbRepository.cs index da01f8f..a9f06b0 100644 --- a/ZeroLevel.SQL/SqlDbRepository.cs +++ b/ZeroLevel.MsSql/SqlDbRepository.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading; using ZeroLevel.Specification; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class SqlDbRepository { diff --git a/ZeroLevel.SQL/SqlServerEntities/ColumnInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/ColumnInfo.cs similarity index 98% rename from ZeroLevel.SQL/SqlServerEntities/ColumnInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/ColumnInfo.cs index f47bc9f..76c132b 100644 --- a/ZeroLevel.SQL/SqlServerEntities/ColumnInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/ColumnInfo.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class ColumnInfo: IEquatable { diff --git a/ZeroLevel.SQL/SqlServerEntities/IndexInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/IndexInfo.cs similarity index 96% rename from ZeroLevel.SQL/SqlServerEntities/IndexInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/IndexInfo.cs index b86cb2b..df3d92e 100644 --- a/ZeroLevel.SQL/SqlServerEntities/IndexInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/IndexInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using ZeroLevel.Services.Collections; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public class IndexInfo : IEquatable { diff --git a/ZeroLevel.SQL/SqlServerEntities/SqlDbForeignKeyInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/SqlDbForeignKeyInfo.cs similarity index 98% rename from ZeroLevel.SQL/SqlServerEntities/SqlDbForeignKeyInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/SqlDbForeignKeyInfo.cs index 3f2ae35..97a86ca 100644 --- a/ZeroLevel.SQL/SqlServerEntities/SqlDbForeignKeyInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/SqlDbForeignKeyInfo.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public sealed class SqlDbForeignKeyInfo : IEquatable { diff --git a/ZeroLevel.SQL/SqlServerEntities/SqlDbObjectInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/SqlDbObjectInfo.cs similarity index 97% rename from ZeroLevel.SQL/SqlServerEntities/SqlDbObjectInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/SqlDbObjectInfo.cs index 04f2e4f..fed0e09 100644 --- a/ZeroLevel.SQL/SqlServerEntities/SqlDbObjectInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/SqlDbObjectInfo.cs @@ -1,4 +1,4 @@ -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public struct SqlDbObjectInfo { diff --git a/ZeroLevel.SQL/SqlServerEntities/SqlDbPrimaryKeyInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/SqlDbPrimaryKeyInfo.cs similarity index 95% rename from ZeroLevel.SQL/SqlServerEntities/SqlDbPrimaryKeyInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/SqlDbPrimaryKeyInfo.cs index 7d3d294..ab5e064 100644 --- a/ZeroLevel.SQL/SqlServerEntities/SqlDbPrimaryKeyInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/SqlDbPrimaryKeyInfo.cs @@ -1,6 +1,6 @@ using System; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public sealed class SqlDbPrimaryKeyInfo : IEquatable { diff --git a/ZeroLevel.SQL/SqlServerEntities/SqlDbTableInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/SqlDbTableInfo.cs similarity index 99% rename from ZeroLevel.SQL/SqlServerEntities/SqlDbTableInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/SqlDbTableInfo.cs index 577b311..9cea20c 100644 --- a/ZeroLevel.SQL/SqlServerEntities/SqlDbTableInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/SqlDbTableInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { /// /// Описание таблицы в БД diff --git a/ZeroLevel.SQL/SqlServerEntities/TableInfo.cs b/ZeroLevel.MsSql/SqlServerEntities/TableInfo.cs similarity index 99% rename from ZeroLevel.SQL/SqlServerEntities/TableInfo.cs rename to ZeroLevel.MsSql/SqlServerEntities/TableInfo.cs index 22c1767..1e29f46 100644 --- a/ZeroLevel.SQL/SqlServerEntities/TableInfo.cs +++ b/ZeroLevel.MsSql/SqlServerEntities/TableInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace ZeroLevel.SqlServer +namespace ZeroLevel.MsSql { public abstract class TableInfo : IEquatable { diff --git a/ZeroLevel.MsSql/ZeroLevel.MsSql.csproj b/ZeroLevel.MsSql/ZeroLevel.MsSql.csproj new file mode 100644 index 0000000..42b7706 --- /dev/null +++ b/ZeroLevel.MsSql/ZeroLevel.MsSql.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.1 + enable + + + + + + + + + + + diff --git a/ZeroLevel.NN/Architectures/AgeDetectors/GenderAgeEstimator.cs b/ZeroLevel.NN/Architectures/AgeDetectors/GenderAgeEstimator.cs deleted file mode 100644 index 8315304..0000000 --- a/ZeroLevel.NN/Architectures/AgeDetectors/GenderAgeEstimator.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public class GenderAgeEstimator - : SSDNN - { - private const int INPUT_WIDTH = 64; - private const int INPUT_HEIGHT = 64; - - public GenderAgeEstimator(string modelPath, bool gpu = false) : base(modelPath, gpu) - { - } - - public (Gender, int) Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyNormilization() - .ApplyAxeInversion()); - return Predict(input); - } - - public (Gender, int) Predict(Tensor input) - { - float[] variances = null; - Extract(new Dictionary> { { "input", input } }, d => - { - variances = d.First().Value.ToArray(); - }); - var gender = Argmax(variances[0..2]) == 0 ? Gender.Male : Gender.Feemale; - return (gender, (int)variances[2]); - } - } -} diff --git a/ZeroLevel.NN/Architectures/AgeDetectors/GoogleAgeEstimator.cs b/ZeroLevel.NN/Architectures/AgeDetectors/GoogleAgeEstimator.cs deleted file mode 100644 index 53f3a6d..0000000 --- a/ZeroLevel.NN/Architectures/AgeDetectors/GoogleAgeEstimator.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public enum Age - { - From0To2, - From4To6, - From8To12, - From15To20, - From25To32, - From38To43, - From48To53, - From60To100 - } - - /// - /// Input tensor is 1 x 3 x height x width with mean values 104, 117, 123. Input image have to be previously resized to 224 x 224 pixels and converted to BGR format. - /// - public class GoogleAgeEstimator - : SSDNN - { - private const int INPUT_WIDTH = 224; - private const int INPUT_HEIGHT = 224; - private static float[] MEAN = new[] { 104f, 117f, 123f }; - - private Age[] _ageList = new[] { Age.From0To2, Age.From4To6, Age.From8To12, Age.From15To20, Age.From25To32, Age.From38To43, Age.From48To53, Age.From60To100 }; - - public GoogleAgeEstimator(string modelPath, bool gpu = false) : base(modelPath, gpu) - { - } - - public Age Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyCorrection((c, px) => px - MEAN[c]) - .ApplyAxeInversion()); - return Predict(input); - } - - public Age Predict(Tensor input) - { - float[] variances = null; - Extract(new Dictionary> { { "input", input } }, d => - { - variances = d.First().Value.ToArray(); - }); - var index = Argmax(variances); - return _ageList[index]; - } - } -} diff --git a/ZeroLevel.NN/Architectures/FaceDetectors/DBFace.cs b/ZeroLevel.NN/Architectures/FaceDetectors/DBFace.cs deleted file mode 100644 index db6d4fc..0000000 --- a/ZeroLevel.NN/Architectures/FaceDetectors/DBFace.cs +++ /dev/null @@ -1,112 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -//https://github.com/iwatake2222/play_with_tflite/blob/master/pj_tflite_face_dbface/image_processor/face_detection_engine.cpp - -namespace ZeroLevel.NN -{ - public sealed class DBFace - : SSDNN , IFaceDetector - { - private const int STRIDE = 4; - private const int INPUT_WIDTH = 1216; - private const int INPUT_HEIGHT = 960; - private const float THESHOLD = 0.4f; - private const float IOU_THESHOLD = 0.6f; - private static float[] MEAN = new[] { 0.408f, 0.447f, 0.47f }; - private static float[] STD = new[] { 0.289f, 0.274f, 0.278f }; - - public DBFace(string model_path) - :base(model_path) - { - } - - private static float exp(float v) - { - var gate = 1.0f; - var _base = Math.Exp(gate); - if (Math.Abs(v) < gate) - return (float)(v * _base); - if (v > 0) - { - return (float)Math.Exp(v); - } - return (float)-Math.Exp(-v); - } - - private static FacePoint Landmark(float cx, float cy, - float x, float y, - float scale_w, float scale_h) - { - var p = new FacePoint(); - p.X = (exp(x * 4) + cx) * STRIDE * scale_w; - p.Y = (exp(y * 4) + cy) * STRIDE * scale_h; - return p; - } - - private List Parse(Tensor hm, - Tensor boxes, Tensor landmarks, - int width, int height) - { - float x, y, r, b; - float scale_w = width / (float)(INPUT_WIDTH); - float scale_h = height / (float)(INPUT_HEIGHT); - List bbox_list = new List(); - for (int cx = 0; cx < hm.Dimensions[3]; cx++) - { - for (int cy = 0; cy < hm.Dimensions[2]; cy++) - { - float score = hm[0, 0, cy, cx]; - if (score >= THESHOLD) - { - x = boxes[0, 0, cy, cx]; - y = boxes[0, 1, cy, cx]; - r = boxes[0, 2, cy, cx]; - b = boxes[0, 3, cy, cx]; - - x = (cx - x) * STRIDE; - y = (cy - y) * STRIDE; - r = (cx + r) * STRIDE; - b = (cy + b) * STRIDE; - - var bbox = new Face(); - bbox.X1 = (int)(x * scale_w); - bbox.Y1 = (int)(y * scale_h); - bbox.X2 = (int)(r * scale_w); - bbox.Y2 = (int)(b * scale_h); - bbox.Score = score; - - bbox.Landmarks.LeftEye = Landmark(cx, cy, landmarks[0, 0, cy, cx], landmarks[0, 5, cy, cx], scale_w, scale_h); - bbox.Landmarks.RightEye = Landmark(cx, cy, landmarks[0, 1, cy, cx], landmarks[0, 6, cy, cx], scale_w, scale_h); - bbox.Landmarks.Nose = Landmark(cx, cy, landmarks[0, 2, cy, cx], landmarks[0, 7, cy, cx], scale_w, scale_h); - bbox.Landmarks.LeftMouth = Landmark(cx, cy, landmarks[0, 3, cy, cx], landmarks[0, 8, cy, cx], scale_w, scale_h); - bbox.Landmarks.RightMouth = Landmark(cx, cy, landmarks[0, 4, cy, cx], landmarks[0, 9, cy, cx], scale_w, scale_h); - - bbox_list.Add(bbox); - } - } - } - return bbox_list; - } - - public IList Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyNormilization() - .ApplyCorrection(MEAN, STD) - .ApplyAxeInversion()); - List result = null; - Extract(new Dictionary> { { "input", input } }, output => - { - var hm = output["hm"]; - var boxes = output["boxes"]; - var landmark = output["landmarks"]; - result = Parse(hm, boxes, landmark, image.Width, image.Height); - }); - var cleaned_result = Face.Nms(result, IOU_THESHOLD, false); - return cleaned_result; - } - } -} diff --git a/ZeroLevel.NN/Architectures/FaceRecognition/ArcFaceNet.cs b/ZeroLevel.NN/Architectures/FaceRecognition/ArcFaceNet.cs deleted file mode 100644 index 6d4fb21..0000000 --- a/ZeroLevel.NN/Architectures/FaceRecognition/ArcFaceNet.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -/* -INPUT - -Image, name: data, shape: 1, 3, 112, 112, format: B, C, H, W, where: -B - batch size -C - channel -H - height -W - width -Channel order is BGR. - -OUTPUT - -Face embeddings, name: fc1, shape: 1, 512, output data format: B, C, where: -B - batch size -C - row-vector of 512 floating points values, face embeddings - -INPUT NORMALIZATION -img -= 127.5 -img /= 128 - -OUTPUT NORMALIZATION -NORM - vector length = 1 - */ - -namespace ZeroLevel.NN -{ - public sealed class ArcFaceNet - : SSDNN, IEncoder - { - private const int INPUT_WIDTH = 112; - private const int INPUT_HEIGHT = 112; - public ArcFaceNet(string modelPath) - : base(modelPath) - { - } - - public int InputW => INPUT_WIDTH; - - public int InputH => INPUT_HEIGHT; - - public float[] Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyAxeInversion()); - return Predict(input); - } - - public float[] Predict(Tensor input) - { - float[] embedding = null; - Extract(new Dictionary> { { "data", input } }, d => - { - embedding = d.First().Value.ToArray(); - }); - Norm(embedding); - return embedding; - } - } -} diff --git a/ZeroLevel.NN/Architectures/FaceRecognition/FaceNet.cs b/ZeroLevel.NN/Architectures/FaceRecognition/FaceNet.cs deleted file mode 100644 index 448f5cb..0000000 --- a/ZeroLevel.NN/Architectures/FaceRecognition/FaceNet.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public sealed class FaceNet - : SSDNN, IEncoder - { - private const int INPUT_WIDTH = 160; - private const int INPUT_HEIGHT = 160; - public FaceNet(string modelPath) - : base(modelPath) - { - } - - public int InputW => INPUT_WIDTH; - public int InputH => INPUT_HEIGHT; - - public float[] Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyCorrection((c,px) => (px / 127.5f) - 1f) - .ApplyAxeInversion()); - return Predict(input); - } - - public float[] Predict(Tensor input) - { - float[] embedding = null; - Extract(new Dictionary> { { "input.1", input } }, d => - { - embedding = d.First().Value.ToArray(); - }); - Norm(embedding); - return embedding; - } - } -} diff --git a/ZeroLevel.NN/Architectures/FaceRecognition/Resnet27.cs b/ZeroLevel.NN/Architectures/FaceRecognition/Resnet27.cs deleted file mode 100644 index 01c6d6b..0000000 --- a/ZeroLevel.NN/Architectures/FaceRecognition/Resnet27.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public class Resnet27 - : SSDNN, IEncoder - { - private const int INPUT_WIDTH = 128; - private const int INPUT_HEIGHT = 128; - public Resnet27(string modelPath) - : base(modelPath) - { - } - - public int InputW => INPUT_WIDTH; - public int InputH => INPUT_HEIGHT; - - public float[] Predict(Image image) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyCorrection((c, px) => (px - 127.5f) / 128f) - .ApplyAxeInversion()); - return Predict(input); - } - - public float[] Predict(Tensor input) - { - float[] embedding = null; - Extract(new Dictionary> { { "input.1", input } }, d => - { - embedding = d.First().Value.ToArray(); - }); - Norm(embedding); - return embedding; - } - } -} diff --git a/ZeroLevel.NN/Architectures/FaceSeacrhService.cs b/ZeroLevel.NN/Architectures/FaceSeacrhService.cs deleted file mode 100644 index 68543a7..0000000 --- a/ZeroLevel.NN/Architectures/FaceSeacrhService.cs +++ /dev/null @@ -1,226 +0,0 @@ -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using ZeroLevel; -using ZeroLevel.NN; -using ZeroLevel.NN.Models; - -namespace Zero.NN.Services -{ - public class FaceSeacrhService - { - private readonly IFaceDetector _detector; - private readonly IEncoder _encoder; - private readonly bool _useFaceAlign; - public FaceSeacrhService(IFaceDetector detector, IEncoder encoder, bool useFaceAlign = true) - { - _useFaceAlign = useFaceAlign; - _detector = detector; - _encoder = encoder; - } - - public static Image MakeEyesHorizontal(Image source, Face face) - { - // положение глаз для определения угла поворота - var leftEye = face.Landmarks.LeftEye; - var rightEye = face.Landmarks.RightEye; - var dY = rightEye.Y - leftEye.Y; - var dX = rightEye.X - leftEye.X; - // угол на который нужно повернуть изображение чтбы выравнять глаза - var ra = (float)Math.Atan2(dY, dX); - - // определить размеры и центр лица - var minX = face.Landmarks.Left(); - var minY = face.Landmarks.Top(); - var maxX = face.Landmarks.Right(); - var maxY = face.Landmarks.Bottom(); - - var centerFaceX = (maxX + minX) / 2.0f; - var centerFaceY = (maxY + minY) / 2.0f; - - // определить описывающий лицо прямоугольник с центром в centerFaceX;centerFaceY - var distanceX = face.X2 - face.X1; - var distanceY = face.Y2 - face.Y1; - - var dx = (face.X1 + distanceX / 2.0f) - centerFaceX; - var dy = (face.Y1 + distanceY / 2.0f) - centerFaceY; - - var x1 = face.X1 - dx; - var y1 = face.Y1 - dy; - var x2 = face.X2 - dx; - var y2 = face.Y2 - dy; - - // определить квадрат описывающий прямоугольник с лицом повернутый на 45 градусов - var radius = (float)Math.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) / 2.0f; - x1 = centerFaceX - radius; - x2 = centerFaceX + radius; - y1 = centerFaceY - radius; - y2 = centerFaceY + radius; - - var cropDx = radius - distanceX / 2.0f; - var cropDy = radius - distanceY / 2.0f; - - using (var fullCrop = ImagePreprocessor.Crop(source, x1, y1, x2, y2)) - { - fullCrop.Mutate(img => img.Rotate((float)(-ra * (180.0f / Math.PI)), KnownResamplers.Bicubic)); - var crop = ImagePreprocessor.Crop(fullCrop, cropDx, cropDy, fullCrop.Width - cropDx, fullCrop.Height - cropDy); - crop.Mutate(img => img.Resize(112, 112, KnownResamplers.Bicubic)); - return crop; - } - } - private Image SpecialCrop(Image image, Face face) - { - var left = face.Landmarks.Left(); // 0.3 - var right = face.Landmarks.Right(); // 0.7 - var top = face.Landmarks.Top(); // 0.4 - var bottom = face.Landmarks.Bottom(); // 0.8 - - var newWidth = (right - left) / 0.4f; - var newHeight = (bottom - top) / 0.4f; - - // привести к квадрату ! - - var cx1 = left - (newWidth * 0.3f); - var cy1 = top - (newHeight * 0.4f); - var cx2 = cx1 + newWidth; - var cy2 = cy1 + newHeight; - - var clipX = new Func(x => - { - if (x < 0) return 0; - if (x > image.Width) return image.Width; - return x; - }); - var clipY = new Func(y => - { - if (y < 0) return 0; - if (y > image.Height) return image.Height; - return y; - }); - - cx1 = clipX(cx1); - cx2 = clipX(cx2); - cy1 = clipY(cy1); - cy2 = clipY(cy2); - - return ImagePreprocessor.Crop(image, cx1, cy1, cx2, cy2); - } - - public IEnumerable GetEmbeddings(Image image) - { - int width = image.Width; - int heigth = image.Height; - var faces = _detector.Predict(image); - foreach (var face in faces) - { - Face.FixInScreen(face, width, heigth); - float[] vector; - if (_useFaceAlign) - { - int x = (int)face.X1; - int y = (int)face.Y1; - int w = (int)(face.X2 - face.X1); - int h = (int)(face.Y2 - face.Y1); - var radius = (float)Math.Sqrt(w * w + h * h) / 2f; - var centerFaceX = (face.X2 + face.X1) / 2.0f; - var centerFaceY = (face.Y2 + face.Y1) / 2.0f; - var around_x1 = centerFaceX - radius; - var around_x2 = centerFaceX + radius; - var around_y1 = centerFaceY - radius; - var around_y2 = centerFaceY + radius; - try - { - using (var faceImage = ImagePreprocessor.Crop(image, around_x1, around_y1, around_x2, around_y2)) - { - var matrix = Face.GetTransformMatrix(face); - var builder = new AffineTransformBuilder(); - builder.AppendMatrix(matrix); - faceImage.Mutate(x => x.Transform(builder, KnownResamplers.Bicubic)); - vector = _encoder.Predict(faceImage); - /*var aligned_faces = detector.Predict(faceImage); - if (aligned_faces != null && aligned_faces.Count == 1) - { - using (var ci = SpecialCrop(faceImage, aligned_faces[0])) - { - vector = encoder.Predict(faceImage); - } - } - else - { - vector = encoder.Predict(faceImage); - }*/ - } - } - catch (Exception ex) - { - Log.SystemError(ex, "[FaceSeacrhService.GetEmbeddings]"); - using (var faceImage = ImagePreprocessor.Crop(image, face.X1, face.Y1, face.X2, face.Y2)) - { - vector = _encoder.Predict(faceImage); - } - } - } - else - { - using (var faceImage = ImagePreprocessor.Crop(image, face.X1, face.Y1, face.X2, face.Y2)) - { - vector = _encoder.Predict(faceImage); - } - } - yield return new FaceEmbedding - { - Face = face, - Vector = vector - }; - } - } - - - public IEnumerable<(FaceEmbedding, Image)> GetEmbeddingsAndCrop(Image image) - { - int width = image.Width; - int heigth = image.Height; - var faces = _detector.Predict(image); - foreach (var face in faces) - { - Face.FixInScreen(face, width, heigth); - float[] vector; - if (_useFaceAlign) - { - int x = (int)face.X1; - int y = (int)face.Y1; - int w = (int)(face.X2 - face.X1); - int h = (int)(face.Y2 - face.Y1); - var radius = (float)Math.Sqrt(w * w + h * h) / 2f; - var centerFaceX = (face.X2 + face.X1) / 2.0f; - var centerFaceY = (face.Y2 + face.Y1) / 2.0f; - var around_x1 = centerFaceX - radius; - var around_x2 = centerFaceX + radius; - var around_y1 = centerFaceY - radius; - var around_y2 = centerFaceY + radius; - var faceImage = ImagePreprocessor.Crop(image, around_x1, around_y1, around_x2, around_y2); - var matrix = Face.GetTransformMatrix(face); - var builder = new AffineTransformBuilder(); - builder.AppendMatrix(matrix); - faceImage.Mutate(x => x.Transform(builder, KnownResamplers.Bicubic)); - vector = _encoder.Predict(faceImage); - yield return (new FaceEmbedding - { - Face = face, - Vector = vector - }, faceImage); - } - else - { - var faceImage = ImagePreprocessor.Crop(image, face.X1, face.Y1, face.X2, face.Y2); - vector = _encoder.Predict(faceImage); - yield return (new FaceEmbedding - { - Face = face, - Vector = vector - }, faceImage); - } - } - } - } -} diff --git a/ZeroLevel.NN/Architectures/IEncoder.cs b/ZeroLevel.NN/Architectures/IEncoder.cs deleted file mode 100644 index d332035..0000000 --- a/ZeroLevel.NN/Architectures/IEncoder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using SixLabors.ImageSharp; - -namespace ZeroLevel.NN -{ - public interface IEncoder - { - int InputW { get; } - int InputH { get; } - - float[] Predict(Image image); - float[] Predict(Tensor input); - } -} diff --git a/ZeroLevel.NN/Architectures/IFaceDetector.cs b/ZeroLevel.NN/Architectures/IFaceDetector.cs deleted file mode 100644 index d65744e..0000000 --- a/ZeroLevel.NN/Architectures/IFaceDetector.cs +++ /dev/null @@ -1,10 +0,0 @@ -using SixLabors.ImageSharp; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public interface IFaceDetector - { - IList Predict(Image image); - } -} diff --git a/ZeroLevel.NN/Architectures/Yolo5.cs b/ZeroLevel.NN/Architectures/Yolo5.cs deleted file mode 100644 index 0934a03..0000000 --- a/ZeroLevel.NN/Architectures/Yolo5.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ZeroLevel.NN.Architectures -{ - internal class Yolo5 - : SSDNN - { - public Yolo5(string modelPath, int width, int height) - : base(modelPath) - { - this.InputH = height; - this.InputW = width; - } - - public int InputW { private set; get; } - - public int InputH { private set; get; } - } -} diff --git a/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs b/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs deleted file mode 100644 index 6798591..0000000 --- a/ZeroLevel.NN/Architectures/YoloV5/Yolov5Detector.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN.Architectures.YoloV5 -{ - public class Yolov5Detector - : SSDNN - { - private int INPUT_WIDTH = 640; - private int INPUT_HEIGHT = 640; - private int CROP_WIDTH = 1280; - private int CROP_HEIGHT = 1280; - - public Yolov5Detector(string modelPath, int inputWidth = 640, int inputHeight = 640, bool gpu = false) - : base(modelPath, gpu) - { - INPUT_HEIGHT = inputHeight; - INPUT_WIDTH = inputWidth; - } - - public List Predict(Image image, float threshold) - { - var input = MakeInput(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyNormilization() - .ApplyAxeInversion()); - return Predict(input, threshold); - } - - public List PredictMultiply(Image image, bool withFullResizedImage, float threshold) - { - var input = MakeInputBatch(image, - new ImagePreprocessorOptions(INPUT_WIDTH, INPUT_HEIGHT, PredictorChannelType.ChannelFirst) - .ApplyNormilization() - .ApplyAxeInversion() - .UseCrop(CROP_WIDTH, CROP_HEIGHT, withFullResizedImage, true)); - return PredictMultiply(input, threshold); - } - - public List Predict(Tensor input, float threshold) - { - var result = new List(); - Extract(new Dictionary> { { "images", input } }, d => - { - Tensor output; - if (d.ContainsKey("output")) - { - output = d["output"]; - } - else - { - output = d.First().Value; - } - /* - var output350 = d["350"]; - var output498 = d["498"]; - var output646 = d["646"]; - */ - if (output != null && output != null) - { - var relative_koef_x = 1.0f / INPUT_WIDTH; - var relative_koef_y = 1.0f / INPUT_HEIGHT; - for (int box = 0; box < output.Dimensions[1]; box++) - { - var conf = output[0, box, 4]; // уверенность в наличии любого объекта - if (conf > threshold) - { - var class_confidense = output[0, box, 5]; // уверенность в наличии объекта класса person - if (class_confidense > threshold) - { - // Перевод относительно входа модели в относительные координаты - var cx = output[0, box, 0] * relative_koef_x; - var cy = output[0, box, 1] * relative_koef_y; - var h = output[0, box, 2] * relative_koef_y; - var w = output[0, box, 3] * relative_koef_x; - result.Add(new YoloPrediction - { - Cx = cx, - Cy = cy, - W = w, - H = h, - Class = 0, - Label = "0", - Score = conf - }); - } - } - } - } - }); - return result; - } - - public List PredictMultiply(ImagePredictionInput[] inputs, float threshold) - { - var result = new List(); - var relative_koef_x = 1.0f / (float)INPUT_WIDTH; - var relative_koef_y = 1.0f / (float)INPUT_HEIGHT; - foreach (var input in inputs) - { - Extract(new Dictionary> { { "images", input.Tensor } }, d => - { - Tensor output; - if (d.ContainsKey("output")) - { - output = d["output"]; - } - else - { - output = d.First().Value; - } - - /* - var output350 = d["350"]; - var output498 = d["498"]; - var output646 = d["646"]; - */ - if (output != null && output != null) - { - for (int index = 0; index < input.Count; index++) - { - var real_koef_x = (float)input.Offsets[index].Width / (float)INPUT_WIDTH; - var real_koef_y = (float)input.Offsets[index].Height / (float)INPUT_HEIGHT; - for (int box = 0; box < output.Dimensions[1]; box++) - { - var conf = output[index, box, 4]; // уверенность в наличии любого объекта - if (conf > threshold) - { - var class_confidense = output[index, box, 5]; // уверенность в наличии объекта класса person - if (class_confidense > threshold) - { - // Перевод относительно входа модели в относительные координаты - var cx = output[index, box, 0] * real_koef_x; - var cy = output[index, box, 1] * real_koef_y; - var h = output[index, box, 2] * relative_koef_y; - var w = output[index, box, 3] * relative_koef_x; - // Перевод в координаты отнисительно текущего смещения - cx += input.Offsets[index].X; - cy += input.Offsets[index].Y; - result.Add(new YoloPrediction - { - Cx = cx, - Cy = cy, - W = w, - H = h, - Class = 0, - Label = "0", - Score = conf - }); - } - } - } - } - } - }); - } - return result; - } - } -} diff --git a/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs b/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs deleted file mode 100644 index 5a07a99..0000000 --- a/ZeroLevel.NN/Architectures/YoloV8/Yolov8Detector.cs +++ /dev/null @@ -1,249 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using System.Diagnostics; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN.Architectures.YoloV5 -{ - public sealed class TensorPoolItem - { - public int StartX; - public int StartY; - public int Width; - public int Height; - public int TensorIndex; - public Tensor Tensor; - - public TensorPoolItem(Tensor tensor, int tensorIndex, int startX, int startY, int width, int height) - { - Tensor = tensor; - TensorIndex = tensorIndex; - StartX = startX; - StartY = startY; - Width = width; - Height = height; - } - - public void Set(int x, int y, float valueR, float valueG, float valueB) - { - var tx = x - StartX; - if (tx < 0 || tx >= Width) return; - var ty = y - StartY; - - this.Tensor[TensorIndex, 0, tx, ty] = valueR; - this.Tensor[TensorIndex, 1, tx, ty] = valueG; - this.Tensor[TensorIndex, 2, tx, ty] = valueB; - } - } - - public sealed class TensorPool - { - public Tensor[] Tensors; - private const int MAX_BATCH_SIZE = 16; - - public readonly int CropHeight; - public readonly int CropWidth; - public readonly int Width; - public readonly int Height; - - private readonly SortedDictionary _pool = new SortedDictionary(); - private readonly Dictionary> _batchPool = new Dictionary>(); - - public TensorPool(int fullWidth, int fullHeight, int cropWidth, int cropHeight, bool overlap, float overlapProportion) - { - Width = fullWidth; - Height = fullHeight; - CropHeight = cropHeight; - var x_points = SplitRange(fullWidth, cropWidth, overlap ? overlapProportion : 0).ToArray(); - var y_points = SplitRange(fullHeight, cropHeight, overlap ? overlapProportion : 0).ToArray(); - var total = x_points.Length * y_points.Length; - int offset = total % MAX_BATCH_SIZE; - int count_tensor_batches = total / MAX_BATCH_SIZE + (offset == 0 ? 0 : 1); - - Tensors = new Tensor[count_tensor_batches]; - for (int batch_index = 0; batch_index < count_tensor_batches; batch_index++) - { - if (batch_index < count_tensor_batches - 1) - { - Tensors[batch_index] = new DenseTensor(new[] { MAX_BATCH_SIZE, 3, cropWidth, cropHeight }); - } - else - { - Tensors[batch_index] = new DenseTensor(new[] { offset == 0 ? MAX_BATCH_SIZE : offset, 3, cropWidth, cropHeight }); - } - } - var batchIndex = 0; - var tensorIndex = 0; - _batchPool[batchIndex] = new List(MAX_BATCH_SIZE); - foreach (var y in y_points) - { - var columnPool = new TensorPoolItem[x_points.Length]; - int column = 0; - foreach (var x in x_points) - { - columnPool[column] = new TensorPoolItem(Tensors[batchIndex], tensorIndex, x, y, cropWidth, cropHeight); - _batchPool[batchIndex].Add(columnPool[column]); - column++; - tensorIndex++; - if (tensorIndex >= MAX_BATCH_SIZE) - { - tensorIndex = 0; - batchIndex++; - _batchPool[batchIndex] = new List(MAX_BATCH_SIZE); - } - } - _pool[y] = columnPool; - } - VirtualPool = new List(x_points.Length * 2); - } - - public TensorPoolItem GetTensor(int batchIndex, int tensorIndex) - { - foreach (var item in _batchPool[batchIndex]) - { - if (item.TensorIndex == tensorIndex) return item; - } - throw new InvalidProgramException(); - } - - public IEnumerable SplitRange(int size, int cropSize, float overlapProportion) - { - var stride = (int)(cropSize * (1f - overlapProportion)); - var counter = 0; - while (true) - { - var pt = stride * counter; - if ((pt + cropSize) > size) - { - if (cropSize == size || pt == size) - { - break; - } - yield return size - cropSize; - break; - } - else - { - yield return pt; - } - counter++; - } - } - - private readonly List VirtualPool; - public List GetSubpoolForY(int y) - { - VirtualPool.Clear(); - foreach (var pair in _pool) - { - if (y >= pair.Key && y < (pair.Key + CropHeight)) - { - VirtualPool.AddRange(pair.Value); - } - } - return VirtualPool; - } - } - - public class Yolov8Detector - : SSDNN - { - private int INPUT_WIDTH = 1280; - private int INPUT_HEIGHT = 1280; - - public Yolov8Detector(string modelPath, int inputWidth = 1280, int inputHeight = 1280, bool gpu = false) - : base(modelPath, gpu) - { - INPUT_HEIGHT = inputHeight; - INPUT_WIDTH = inputWidth; - } - public List Predict(Image image, float threshold) - { - var sw = new Stopwatch(); - sw.Start(); - var input = ImageToTensors(image); - sw.Stop(); - return Predict(input, threshold); - } - public List Predict(TensorPool inputs, float threshold) - { - var result = new List(); - var relative_koef_x = 1.0f / (float)inputs.Width; - var relative_koef_y = 1.0f / (float)inputs.Height; - int batchIndex = 0; - foreach (var input in inputs.Tensors) - { - Extract(new Dictionary> { { "images", input } }, d => - { - Tensor output; - if (d.ContainsKey("output0")) - { - output = d["output0"]; - } - else - { - output = d.First().Value; - } - if (output != null && output != null) - { - for (int tensorIndex = 0; tensorIndex < output.Dimensions[0]; tensorIndex++) - { - var tensor = inputs.GetTensor(batchIndex, tensorIndex); - for (int box = 0; box < output.Dimensions[2]; box++) - { - var conf = output[tensorIndex, 4, box]; // уверенность в наличии любого объекта - if (conf > threshold) - { - // Перевод относительно входа модели в относительные координаты - var cx = output[tensorIndex, 1, box]; - var cy = output[tensorIndex, 0, box]; - var w = output[tensorIndex, 3, box]; - var h = output[tensorIndex, 2, box]; - // Перевод в координаты отнисительно текущего смещения - cx += tensor.StartX; - cy += tensor.StartY; - result.Add(new YoloPrediction - { - Cx = cx * relative_koef_x, - Cy = cy * relative_koef_y, - W = w * relative_koef_x, - H = h * relative_koef_y, - Class = 0, - Label = "0", - Score = conf - }); - } - } - } - } - }); - batchIndex++; - } - return result; - } - - private TensorPool ImageToTensors(Image image) - { - var pool = new TensorPool(image.Width, image.Height, INPUT_WIDTH, INPUT_HEIGHT, true, 0); - var koef = 1.0f / 255.0f; - ((Image)image).ProcessPixelRows(pixels => - { - for (int y = 0; y < pixels.Height; y++) - { - var subpool = pool.GetSubpoolForY(y); - Span pixelSpan = pixels.GetRowSpan(y); - for (int x = 0; x < pixels.Width; x++) - { - float r = koef * (float)pixelSpan[x].R; - float g = koef * (float)pixelSpan[x].G; - float b = koef * (float)pixelSpan[x].B; - for (int i = 0; i < subpool.Count; i++) - { - subpool[i].Set(x, y, r, g, b); - } - } - } - }); - return pool; - } - } -} diff --git a/ZeroLevel.NN/Examples/tSNE.txt b/ZeroLevel.NN/Examples/tSNE.txt deleted file mode 100644 index fb2f0ff..0000000 --- a/ZeroLevel.NN/Examples/tSNE.txt +++ /dev/null @@ -1,75 +0,0 @@ -public static void DrawTSNE(Dictionary cluster_map, List faces) - { - double[][] t_snefit_vectors = faces.Select(f => f.Vector.Select(e => (double)e).ToArray()).ToArray(); - TSNE tSNE = new TSNE() - { - NumberOfOutputs = 2, - Perplexity = 100 - }; - // Transform to a reduced dimensionality space - var embeddings = tSNE.Transform(t_snefit_vectors); - var xmin = double.MaxValue; - var xmax = double.MinValue; - var ymin = double.MaxValue; - var ymax = double.MinValue; - for (int i = 0; i < embeddings.Length; i++) - { - var lxmin = embeddings[i][0]; - var lxmax = embeddings[i][0]; - if (lxmin < xmin) - xmin = lxmin; - if (lxmax > xmax) - xmax = lxmax; - - var lymin = embeddings[i][1]; - var lymax = embeddings[i][1]; - if (lymin < ymin) - ymin = lymin; - if (lymax > ymax) - ymax = lymax; - } - - var norm_x_scale = 1.0f / (xmax - xmin); - var norm_y_scale = 1.0f / (xmax - xmin); - var xdiff = 0 - xmin; - var ydiff = 0 - ymin; - var centerx = (xmin + xmax) / 2.0f + xdiff; - var centery = (ymin + ymax) / 2.0f + ydiff; - - var width = 2560; - var height = 1440; - - var rnd = new Random((int)Environment.TickCount); - - var clusterIds = cluster_map.Values.Distinct().ToArray(); - var cluster_colors = new Dictionary(); - foreach (var cid in clusterIds) - { - var color = Color.FromRgb((byte)rnd.Next(0, 255), (byte)rnd.Next(0, 255), (byte)rnd.Next(0, 255)); - cluster_colors[cid] = color; - } - - using (var image = new Image(width, height)) - { - for (int i = 0; i < embeddings.Length; i++) - { - var cluster = cluster_map[i]; - var color = cluster_colors[cluster]; - - var x = (int)((embeddings[i][0] + xdiff + centerx) * norm_x_scale * width) - width / 2; - var y = (int)((embeddings[i][1] + ydiff + centery) * norm_y_scale * height) - height / 2; - image.Mutate(im => im.DrawLines( - color, - 4, - new PointF[] { - new PointF(x - 1, y - 1), - new PointF(x + 1, y - 1), - new PointF(x + 1, y + 1), - new PointF(x - 1, y + 1), - new PointF(x - 1, y - 1) - } - )); - } - image.SaveAsJpeg(@"G:\FaceTest\tsne.jpeg"); - } - } \ No newline at end of file diff --git a/ZeroLevel.NN/Models/Cluster.cs b/ZeroLevel.NN/Models/Cluster.cs deleted file mode 100644 index 2024699..0000000 --- a/ZeroLevel.NN/Models/Cluster.cs +++ /dev/null @@ -1,133 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - public class Cluster - { - private int _key; - private readonly List _points = new List(); - - public T this[int index] => _points[index]; - public IReadOnlyList Points => _points; - public int Key { get { return _key; } set { _key = value; } } - public Cluster() - { - } - - public Cluster(T point) - { - _points.Add(point); - } - - public Cluster(IEnumerable points) - { - _points.AddRange(points); - } - - public void Add(T point) - { - _points.Add(point); - } - - public void Remove(T point) - { - _points.Remove(point); - } - /* - public bool IsNeighbor(T feature, - Func embeddingFunction, - Func similarityFunction, - float threshold, - float clusterThreshold) - { - if (_points.Count == 0) return true; - if (_points.Count == 1) - { - var similarity = similarityFunction(embeddingFunction(feature), embeddingFunction(_points[0])); - return similarity >= threshold; - } - var clusterNearestElementsCount = 0; - foreach (var f in _points) - { - var similarity = similarityFunction(embeddingFunction(feature), embeddingFunction(f)); - if (similarity >= threshold) - { - clusterNearestElementsCount++; - } - } - var clusterToFaceScore = (float)clusterNearestElementsCount / (float)_points.Count; - return clusterToFaceScore > clusterThreshold; - } - */ - public bool IsNearest(T feature, - Func distanceFunction, - double maxDistance) - { - if (_points.Count == 0) return true; - if (_points.Count == 1) - { - var distance = distanceFunction(feature, _points[0]); - return distance <= maxDistance; - } - foreach (var f in _points) - { - var distance = distanceFunction(feature, f); - if (distance > maxDistance) - { - return false; - } - } - return true; - } - - public double MinimalDistance(T feature, - Func distanceFunction) - { - if (_points.Count == 0) return int.MaxValue; - var min = distanceFunction(feature, _points[0]); - if (_points.Count == 1) - { - return min; - } - for (int i = 0; i<_points.Count; i++) - { - var distance = distanceFunction(feature, _points[i]); - if (distance < min) - { - min = distance; - } - } - return min; - } - /* - public bool IsNeighborCluster(Cluster cluster, - Func embeddingFunction, - Func similarityFunction, - float threshold, - float clusterThreshold) - { - if (_points.Count == 0) return true; - if (_points.Count == 1 && cluster.IsNeighbor(_points[0], embeddingFunction, similarityFunction, threshold, clusterThreshold)) - { - return true; - } - var clusterNearestElementsCount = 0; - foreach (var f in _points) - { - if (cluster.IsNeighbor(f, embeddingFunction, similarityFunction, threshold, clusterThreshold)) - { - clusterNearestElementsCount++; - } - } - var localCount = _points.Count; - var remoteCount = cluster._points.Count; - var localIntersection = (float)clusterNearestElementsCount / (float)localCount; - var remoteIntersection = (float)clusterNearestElementsCount / (float)remoteCount; - var score = Math.Max(localIntersection, remoteIntersection); - return score > clusterThreshold; - } - */ - public void Merge(Cluster other) - { - this._points.AddRange(other.Points); - } - } -} diff --git a/ZeroLevel.NN/Models/Face.cs b/ZeroLevel.NN/Models/Face.cs deleted file mode 100644 index 3ea6549..0000000 --- a/ZeroLevel.NN/Models/Face.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Numerics; -using Zero.NN.Models; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.NN.Models -{ - public class Face - : IBinarySerializable - { - public float X1; - public float Y1; - public float X2; - public float Y2; - public float Score; - public Landmarks Landmarks = new Landmarks(); - - public float Area => Math.Abs(X2 - X1) * Math.Abs(Y2 - Y1); - - public static float CalculateIoU(Face obj0, Face obj1) - { - var interx0 = Math.Max(obj0.X1, obj1.X1); - var intery0 = Math.Max(obj0.Y1, obj1.Y1); - var interx1 = Math.Min(obj0.X2, obj1.X2); - var intery1 = Math.Min(obj0.Y2, obj1.Y2); - if (interx1 < interx0 || intery1 < intery0) return 0; - var area0 = obj0.Area; - var area1 = obj1.Area; - var areaInter = (interx1 - interx0) * (intery1 - intery0); - var areaSum = area0 + area1 - areaInter; - return (float)(areaInter) / areaSum; - } - public static void FixInScreen(Face bbox, int width, int height) - { - bbox.X1 = Math.Max(0, bbox.X1); - bbox.Y1 = Math.Max(0, bbox.Y1); - bbox.X2 = Math.Min(width, bbox.X2); - bbox.Y2 = Math.Min(height, bbox.Y2); - } - - public static List Nms(List bbox_original_list, float threshold_nms_iou, bool check_class_id) - { - var bbox_nms_list = new List(); - var bbox_list = bbox_original_list.OrderBy(b => b.Score).ToList(); - bool[] is_merged = new bool[bbox_list.Count]; - for (var i = 0; i < bbox_list.Count; i++) - { - is_merged[i] = false; - } - for (var index_high_score = 0; index_high_score < bbox_list.Count; index_high_score++) - { - var candidates = new List(); - if (is_merged[index_high_score]) continue; - - candidates.Add(bbox_list[index_high_score]); - for (var index_low_score = index_high_score + 1; index_low_score < bbox_list.Count; index_low_score++) - { - if (is_merged[index_low_score]) continue; - if (CalculateIoU(bbox_list[index_high_score], bbox_list[index_low_score]) > threshold_nms_iou) - { - candidates.Add(bbox_list[index_low_score]); - is_merged[index_low_score] = true; - } - } - bbox_nms_list.Add(candidates[0]); - } - return bbox_nms_list; - } - - // Normalizes a facial image to a standard size given by outSize. - // Normalization is done based on Dlib's landmark points passed as pointsIn - // After normalization, left corner of the left eye is at (0.3 * w, h/3 ) - // and right corner of the right eye is at ( 0.7 * w, h / 3) where w and h - // are the width and height of outSize. - public static Matrix3x2 GetTransformMatrix(Face face) - { - var w = face.X2 - face.X1; - var h = face.Y2 - face.Y1; - - var leftEyeSrc = new FacePoint((face.Landmarks.LeftEye.X - face.X1) / w, (face.Landmarks.LeftEye.Y - face.Y1) / h); - var rightEyeSrc = new FacePoint((face.Landmarks.RightEye.X - face.X1) / w, (face.Landmarks.RightEye.Y - face.Y1) / h); - - // Corners of the eye in normalized image - var leftEyeDst = new FacePoint(0.3f, 1.0f / 3.0f); - var rightEyeDst = new FacePoint(0.7f, 1.0f / 3.0f); - - return GetTransformMatrix(leftEyeSrc, rightEyeSrc, leftEyeDst, rightEyeDst); - } - - static Matrix3x2 GetTransformMatrix(FacePoint srcLeftEye, FacePoint srcRightEye, - FacePoint dstLeftEye, FacePoint dstRightEye) - { - var s60 = Math.Sin(60.0f * Math.PI / 180.0f); - var c60 = Math.Cos(60.0f * Math.PI / 180.0f); - - // The third point is calculated so that the three points make an equilateral triangle - var xin = c60 * (srcLeftEye.X - srcRightEye.X) - s60 * (srcLeftEye.Y - srcRightEye.Y) + srcRightEye.X; - var yin = s60 * (srcLeftEye.X - srcRightEye.X) + c60 * (srcLeftEye.Y - srcRightEye.Y) + srcRightEye.Y; - - var xout = c60 * (dstLeftEye.X - dstRightEye.X) - s60 * (dstLeftEye.Y - dstRightEye.Y) + dstRightEye.X; - var yout = s60 * (dstLeftEye.X - dstRightEye.X) + c60 * (dstLeftEye.Y - dstRightEye.Y) + dstRightEye.Y; - - System.Drawing.PointF[] source = { - new System.Drawing.PointF(srcLeftEye.X, srcLeftEye.Y), - new System.Drawing.PointF(srcRightEye.X, srcRightEye.Y), - new System.Drawing.PointF((float)xin, (float)yin) - }; - System.Drawing.PointF[] target = { - new System.Drawing.PointF(dstLeftEye.X, dstLeftEye.Y), - new System.Drawing.PointF(dstRightEye.X, dstRightEye.Y), - new System.Drawing.PointF((float)xout, (float)yout) - }; - Aurigma.GraphicsMill.Transforms.Matrix matrix = - Aurigma.GraphicsMill.Transforms.Matrix.CreateFromAffinePoints(source, target); - - return new Matrix3x2( - matrix.Elements[0], matrix.Elements[1], - matrix.Elements[3], matrix.Elements[4], - matrix.Elements[6], matrix.Elements[7]); - } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteFloat(this.X1); - writer.WriteFloat(this.Y1); - writer.WriteFloat(this.X2); - writer.WriteFloat(this.Y2); - writer.WriteFloat(this.Score); - writer.Write(this.Landmarks); - } - - public void Deserialize(IBinaryReader reader) - { - this.X1 = reader.ReadFloat(); - this.Y1 = reader.ReadFloat(); - this.X2 = reader.ReadFloat(); - this.Y2 = reader.ReadFloat(); - this.Score = reader.ReadFloat(); - this.Landmarks = reader.Read(); - } - } -} diff --git a/ZeroLevel.NN/Models/FaceEmbedding.cs b/ZeroLevel.NN/Models/FaceEmbedding.cs deleted file mode 100644 index 3e7077c..0000000 --- a/ZeroLevel.NN/Models/FaceEmbedding.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.NN.Models -{ - public class FaceEmbedding - : IBinarySerializable - { - public Face Face; - public float[] Vector; - public string Tag; - - public void Deserialize(IBinaryReader reader) - { - this.Face = reader.Read(); - this.Vector = reader.ReadFloatArray(); - this.Tag = reader.ReadString(); - } - - public void Serialize(IBinaryWriter writer) - { - writer.Write(this.Face); - writer.WriteArray(this.Vector); - writer.WriteString(this.Tag); - } - } -} diff --git a/ZeroLevel.NN/Models/FacePoint.cs b/ZeroLevel.NN/Models/FacePoint.cs deleted file mode 100644 index 6262d5c..0000000 --- a/ZeroLevel.NN/Models/FacePoint.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.NN.Models -{ - public class FacePoint - : IBinarySerializable - { - public float X { get; set; } - public float Y { get; set; } - - public FacePoint() { } - public FacePoint(float x, float y) { X = x; Y = y; } - - public void Serialize(IBinaryWriter writer) - { - writer.WriteFloat(this.X); - writer.WriteFloat(this.Y); - } - - public void Deserialize(IBinaryReader reader) - { - this.X = reader.ReadFloat(); - this.Y = reader.ReadFloat(); - } - } -} diff --git a/ZeroLevel.NN/Models/Gender.cs b/ZeroLevel.NN/Models/Gender.cs deleted file mode 100644 index 8fc5795..0000000 --- a/ZeroLevel.NN/Models/Gender.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - public enum Gender - { - Unknown = 0, - Male = 1, - Feemale = 2 - } -} diff --git a/ZeroLevel.NN/Models/ImagePredictionInput.cs b/ZeroLevel.NN/Models/ImagePredictionInput.cs deleted file mode 100644 index 3934037..0000000 --- a/ZeroLevel.NN/Models/ImagePredictionInput.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; - -namespace ZeroLevel.NN.Models -{ - public class ImagePredictionInput - { - public Tensor Tensor; - public OffsetBox[] Offsets; - public int Count; - } -} diff --git a/ZeroLevel.NN/Models/ImagePreprocessorCropOptions.cs b/ZeroLevel.NN/Models/ImagePreprocessorCropOptions.cs deleted file mode 100644 index caa8ce4..0000000 --- a/ZeroLevel.NN/Models/ImagePreprocessorCropOptions.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - /// - /// Crop options - /// - public class ImagePreprocessorCropOptions - { - /// - /// Use split original image to crops - /// - public bool Enabled { get; set; } = false; - /// - /// Put resized original image to batch - /// - public bool SaveOriginal { get; set; } - /// - /// Crop width - /// - public int Width { get; set; } - /// - /// Crop height - /// - public int Height { get; set; } - /// - /// Overlap cropped parts - /// - public bool Overlap { get; set; } - /// - /// Overlap width koefficient (0 - 1) - /// - public float OverlapKoefWidth { get; set; } = 0.8f; - /// - /// Overlap height koefficient (0 - 1) - /// - public float OverlapKoefHeight { get; set; } = 0.8f; - } -} diff --git a/ZeroLevel.NN/Models/ImagePreprocessorOptions.cs b/ZeroLevel.NN/Models/ImagePreprocessorOptions.cs deleted file mode 100644 index 91e783f..0000000 --- a/ZeroLevel.NN/Models/ImagePreprocessorOptions.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - public class ImagePreprocessorOptions - { - private const float PIXEL_NORMALIZATION_SCALE = 1.0f / 255.0f; - public ImagePreprocessorOptions(int inputWidth, int inputHeight, PredictorChannelType channelType) - { - this.InputWidth = inputWidth; - this.InputHeight = inputHeight; - this.ChannelType = channelType; - } - - public ImagePreprocessorOptions UseCrop(int width, int height, bool saveOriginal, bool overlap) - { - Crop.Enabled = true; - Crop.Height = height; - Crop.Width = width; - Crop.Overlap = overlap; - Crop.SaveOriginal = saveOriginal; - return this; - } - - public ImagePreprocessorOptions ApplyNormilization(float? multiplier = null) - { - if (multiplier.HasValue) - { - NormalizationMultiplier = multiplier.Value; - } - this.Normalize = true; - return this; - } - - public ImagePreprocessorOptions ApplyAxeInversion() - { - this.InvertXY = true; - return this; - } - - public ImagePreprocessorOptions ApplyCorrection(float[] mean, float[] std) - { - if (this.Correction) - { - throw new InvalidOperationException("Correction setup already"); - } - this.Correction = true; - this.Mean = mean; - this.Std = std; - return this; - } - - public ImagePreprocessorOptions ApplyCorrection(Func correctionFunc) - { - if (this.Correction) - { - throw new InvalidOperationException("Correction setup already"); - } - this.Correction = true; - this.CorrectionFunc = correctionFunc; - return this; - } - - public ImagePreprocessorOptions UseBGR() - { - this.BGR = true; - return this; - } - - - public float NormalizationMultiplier { get; private set; } = PIXEL_NORMALIZATION_SCALE; - /// - /// Channel type, if first tensor dims = [batch_index, channel, x, y], if last, dims = dims = [batch_index, x, y, channel] - /// - public PredictorChannelType ChannelType { get; private set; } - /// - /// Ctop image options - /// - public ImagePreprocessorCropOptions Crop { get; } = new ImagePreprocessorCropOptions(); - /// - /// NN model input height - /// - public int InputHeight { get; private set; } - /// - /// NN model input width - /// - public int InputWidth { get; private set; } - /// - /// Transfrom pixel values to (0-1) range - /// - public bool Normalize { get; private set; } = false; - /// - /// Transform pixel value with mean/std values v=(v-mean)/std - /// - public bool Correction { get; private set; } = false; - /// - /// Mean values if Correction parameter is true - /// - - public Func CorrectionFunc { get; private set; } = null; - - public float[] Mean { get; private set; } - /// - /// Std values if Correction parameter is true - /// - public float[] Std { get; private set; } - /// - /// Put pixel values to tensor in BGR order - /// - public bool BGR { get; set; } = false; - /// - /// Invert width and height in input tensor - /// - public bool InvertXY { get; set; } = false; - /// - /// Channel count (auto calculate) - /// - public int Channels { get; set; } - /// - /// Maximum batch size, decrease if video memory overflow - /// - public int MaxBatchSize { get; set; } = 13; - } -} diff --git a/ZeroLevel.NN/Models/Landmarks.cs b/ZeroLevel.NN/Models/Landmarks.cs deleted file mode 100644 index 5ffdade..0000000 --- a/ZeroLevel.NN/Models/Landmarks.cs +++ /dev/null @@ -1,121 +0,0 @@ -using ZeroLevel.NN.Models; -using ZeroLevel.Services.Serialization; - -namespace Zero.NN.Models -{ - public class Landmarks - : IBinarySerializable - { - public FacePoint RightEye; - public FacePoint LeftEye; - public FacePoint Nose; - public FacePoint RightMouth; - public FacePoint LeftMouth; - - public float Top() - { - var min = RightEye.Y; - if (LeftEye.Y < min) - { - min = LeftEye.Y; - } - if (Nose.Y < min) - { - min = Nose.Y; - } - if (RightMouth.Y < min) - { - min = RightMouth.Y; - } - if (LeftMouth.Y < min) - { - min = LeftMouth.Y; - } - return min; - } - - public float Bottom() - { - var max = RightEye.Y; - if (LeftEye.Y > max) - { - max = LeftEye.Y; - } - if (Nose.Y > max) - { - max = Nose.Y; - } - if (RightMouth.Y > max) - { - max = RightMouth.Y; - } - if (LeftMouth.Y > max) - { - max = LeftMouth.Y; - } - return max; - } - - public float Left() - { - var min = RightEye.X; - if (LeftEye.X < min) - { - min = LeftEye.X; - } - if (Nose.X < min) - { - min = Nose.X; - } - if (RightMouth.X < min) - { - min = RightMouth.X; - } - if (LeftMouth.X < min) - { - min = LeftMouth.X; - } - return min; - } - - public float Right() - { - var max = RightEye.X; - if (LeftEye.X > max) - { - max = LeftEye.X; - } - if (Nose.X > max) - { - max = Nose.X; - } - if (RightMouth.X > max) - { - max = RightMouth.X; - } - if (LeftMouth.X > max) - { - max = LeftMouth.X; - } - return max; - } - - public void Deserialize(IBinaryReader reader) - { - this.RightEye = reader.Read(); - this.LeftEye = reader.Read(); - this.Nose = reader.Read(); - this.RightMouth = reader.Read(); - this.LeftMouth = reader.Read(); - } - - public void Serialize(IBinaryWriter writer) - { - writer.Write(this.RightEye); - writer.Write(this.LeftEye); - writer.Write(this.Nose); - writer.Write(this.RightMouth); - writer.Write(this.LeftMouth); - } - } -} diff --git a/ZeroLevel.NN/Models/OffsetBox.cs b/ZeroLevel.NN/Models/OffsetBox.cs deleted file mode 100644 index bfa25aa..0000000 --- a/ZeroLevel.NN/Models/OffsetBox.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - public class OffsetBox - { - public int X { get; set; } - public int Y { get; set; } - public int Width { get; set; } - public int Height { get; set; } - - public OffsetBox() { } - public OffsetBox(int x, int y, int w, int h) - { - X = x; - Y = y; - Width = w; - Height = h; - } - } -} diff --git a/ZeroLevel.NN/Models/PredictorChannelType.cs b/ZeroLevel.NN/Models/PredictorChannelType.cs deleted file mode 100644 index b4cf276..0000000 --- a/ZeroLevel.NN/Models/PredictorChannelType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ZeroLevel.NN.Models -{ - public enum PredictorChannelType - { - ChannelFirst, - ChannelLast - } -} diff --git a/ZeroLevel.NN/Services/AnchorsGenerator.cs b/ZeroLevel.NN/Services/AnchorsGenerator.cs deleted file mode 100644 index d0a2f24..0000000 --- a/ZeroLevel.NN/Services/AnchorsGenerator.cs +++ /dev/null @@ -1,227 +0,0 @@ -/* - -PORTS FROM https://github.com/hollance/BlazeFace-PyTorch/blob/master/Anchors.ipynb - - */ - -namespace Zero.NN.Services -{ - public class Anchor - { - public float cx; - public float cy; - public float w; - public float h; - } - - // Options to generate anchors for SSD object detection models. - public class AnchorOptions - { - // Number of output feature maps to generate the anchors on. - public int num_layers; - - // Min and max scales for generating anchor boxes on feature maps. - public float min_scale; - public float max_scale; - - // Size of input images. - public int input_size_height; - public int input_size_width; - - // The offset for the center of anchors. The value is in the scale of stride. - // E.g. 0.5 meaning 0.5 * |current_stride| in pixels. - public float anchor_offset_x = 0.5f; - public float anchor_offset_y = 0.5f; - - // Strides of each output feature maps - public int[] strides; - - // List of different aspect ratio to generate anchors - public float[] aspect_ratios; - - // A boolean to indicate whether the fixed 3 boxes per location is used in the lowest layer. - public bool reduce_boxes_in_lowest_layer = false; - - // An additional anchor is added with this aspect ratio and a scale - // interpolated between the scale for a layer and the scale for the next layer - // (1.0 for the last layer). This anchor is not included if this value is 0. - public float interpolated_scale_aspect_ratio = 1.0f; - - // Whether use fixed width and height (e.g. both 1.0f) for each anchor. - // This option can be used when the predicted anchor width and height are in - // pixels. - public bool fixed_anchor_size = false; - - #region PRESETS - public static AnchorOptions FaceDetectionBackMobileGpuOptions => new AnchorOptions - { - num_layers = 4, - min_scale = 0.15625f, - max_scale = 0.75f, - input_size_height = 256, - input_size_width = 256, - anchor_offset_x = 0.5f, - anchor_offset_y = 0.5f, - strides = new[] { 16, 32, 32, 32 }, - aspect_ratios = new[] { 1.0f }, - reduce_boxes_in_lowest_layer = false, - interpolated_scale_aspect_ratio = 1.0f, - fixed_anchor_size = true - }; - - public static AnchorOptions FaceDetectionMobileGpuOptions => new AnchorOptions - { - num_layers = 4, - min_scale = 0.1484375f, - max_scale = 0.75f, - input_size_height = 128, - input_size_width = 128, - anchor_offset_x = 0.5f, - anchor_offset_y = 0.5f, - strides = new[] { 8, 16, 16, 16 }, - aspect_ratios = new[] { 1.0f }, - reduce_boxes_in_lowest_layer = false, - interpolated_scale_aspect_ratio = 1.0f, - fixed_anchor_size = true - }; - - public static AnchorOptions MobileSSDOptions => new AnchorOptions - { - num_layers = 6, - min_scale = 0.2f, - max_scale = 0.95f, - input_size_height = 300, - input_size_width = 300, - anchor_offset_x = 0.5f, - anchor_offset_y = 0.5f, - strides = new[] { 16, 32, 64, 128, 256, 512 }, - aspect_ratios = new[] { 1.0f, 2.0f, 0.5f, 3.0f, 0.3333f }, - reduce_boxes_in_lowest_layer = true, - interpolated_scale_aspect_ratio = 1.0f, - fixed_anchor_size = false - }; - #endregion - } - - internal class AnchorsGenerator - { - private static float calculate_scale(float min_scale, float max_scale, float stride_index, float num_strides) - { - return (float)(min_scale + (max_scale - min_scale) * stride_index / (num_strides - 1.0f)); - } - - private readonly AnchorOptions _options; - private readonly List anchors = new List(); - - public IList Anchors => anchors; - - public AnchorsGenerator(AnchorOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - if (options.strides == null) - { - throw new ArgumentNullException(nameof(options.strides)); - } - _options = options; - Generate(); - } - - private void Generate() - { - var strides_size = _options.strides?.Length ?? 0; - if (_options.num_layers != strides_size) - { - throw new ArgumentException($"Expected {_options.num_layers} strides (as num_layer), got {strides_size} strides"); - } - var layer_id = 0; - while (layer_id < strides_size) - { - var anchor_height = new List(); - var anchor_width = new List(); - var aspect_ratios = new List(); - var scales = new List(); - - // For same strides, we merge the anchors in the same order. - var last_same_stride_layer = layer_id; - while ((last_same_stride_layer < strides_size) && (_options.strides[last_same_stride_layer] == _options.strides[layer_id])) - { - var scale = calculate_scale(_options.min_scale, _options.max_scale, last_same_stride_layer, strides_size); - - if (last_same_stride_layer == 0 && _options.reduce_boxes_in_lowest_layer) - { - // For first layer, it can be specified to use predefined anchors. - aspect_ratios.Add(1.0f); - aspect_ratios.Add(2.0f); - aspect_ratios.Add(0.5f); - scales.Add(0.1f); - scales.Add(scale); - scales.Add(scale); - } - else - { - foreach (var aspect_ratio in _options.aspect_ratios) - { - aspect_ratios.Add(aspect_ratio); - scales.Add(scale); - } - if (_options.interpolated_scale_aspect_ratio > 0.0f) - { - var scale_next = (last_same_stride_layer == (strides_size - 1)) - ? 1.0 - : calculate_scale(_options.min_scale, _options.max_scale, last_same_stride_layer + 1, strides_size); - scales.Add((float)Math.Sqrt(scale * scale_next)); - aspect_ratios.Add(_options.interpolated_scale_aspect_ratio); - } - } - last_same_stride_layer += 1; - } - - for (var i = 0; i < aspect_ratios.Count; i++) - { - var ratio_sqrts = (float)Math.Sqrt(aspect_ratios[i]); - anchor_height.Add(scales[i] / ratio_sqrts); - anchor_width.Add(scales[i] * ratio_sqrts); - } - - var stride = _options.strides[layer_id]; - var feature_map_height = (int)(Math.Ceiling((float)_options.input_size_height / stride)); - var feature_map_width = (int)(Math.Ceiling((float)_options.input_size_width / stride)); - - for (var y = 0; y < feature_map_height; y++) - { - for (var x = 0; x < feature_map_width; x++) - { - for (var anchor_id = 0; anchor_id < anchor_height.Count; anchor_id++) - { - var x_center = (x + _options.anchor_offset_x) / feature_map_width; - var y_center = (y + _options.anchor_offset_y) / feature_map_height; - - var anchor = new Anchor - { - cx = x_center, - cy = y_center, - w = 0f, - h = 0f - }; - if (_options.fixed_anchor_size) - { - anchor.w = 1.0f; - anchor.h = 1.0f; - } - else - { - anchor.w = anchor_width[anchor_id]; - anchor.h = anchor_height[anchor_id]; - } - anchors.Add(anchor); - } - } - } - layer_id = last_same_stride_layer; - } - } - } -} diff --git a/ZeroLevel.NN/Services/Clusterization/FeatureCluster.cs b/ZeroLevel.NN/Services/Clusterization/FeatureCluster.cs deleted file mode 100644 index 55cde57..0000000 --- a/ZeroLevel.NN/Services/Clusterization/FeatureCluster.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace ZeroLevel.NN.Clusterization -{ - public class FeatureCluster - { - private readonly List _features = new List(); - private readonly Func _vectorExtractor; - public FeatureCluster(Func vectorExtractor) - { - _vectorExtractor = vectorExtractor; - } - - public IReadOnlyList Features => _features; - - internal void Append(T face) => _features.Add(face); - public bool IsNeighbor(T feature, Func similarityFunction, float threshold, float clusterThreshold = 0.5f) - { - if (_features.Count == 0) return true; - if (_features.Count == 1) - { - var similarity = similarityFunction(_vectorExtractor(feature), _vectorExtractor(_features[0])); - return similarity >= threshold; - } - var clusterNearestElementsCount = 0; - foreach (var f in _features) - { - var similarity = similarityFunction(_vectorExtractor(feature), _vectorExtractor(f)); - if (similarity >= threshold) - { - clusterNearestElementsCount++; - } - } - var clusterToFaceScore = (float)clusterNearestElementsCount / (float)_features.Count; - return clusterToFaceScore > clusterThreshold; - } - - public bool IsNeighborCluster(FeatureCluster cluster, Func similarityFunction, float threshold, float clusterThreshold = 0.5f) - { - if (_features.Count == 0) return true; - if (_features.Count == 1 && cluster.IsNeighbor(_features[0], similarityFunction, threshold, clusterThreshold)) - { - return true; - } - var clusterNearestElementsCount = 0; - foreach (var f in _features) - { - if (cluster.IsNeighbor(f, similarityFunction, threshold, clusterThreshold)) - { - clusterNearestElementsCount++; - } - } - var localCount = _features.Count; - var remoteCount = cluster.Features.Count; - var localIntersection = (float)clusterNearestElementsCount / (float)localCount; - var remoteIntersection = (float)clusterNearestElementsCount / (float)remoteCount; - var score = Math.Max(localIntersection, remoteIntersection); - return score > clusterThreshold; - } - - public void Merge(FeatureCluster other) - { - this._features.AddRange(other.Features); - } - } -} diff --git a/ZeroLevel.NN/Services/Clusterization/FeatureClusterBulder.cs b/ZeroLevel.NN/Services/Clusterization/FeatureClusterBulder.cs deleted file mode 100644 index 78c642d..0000000 --- a/ZeroLevel.NN/Services/Clusterization/FeatureClusterBulder.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace ZeroLevel.NN.Clusterization -{ - public class FeatureClusterBulder - { - public FeatureClusterCollection Build(IEnumerable items, Func vectorExtractor, Func similarityFunction, float threshold, float clusterThreshold = 0.5f) - { - var collection = new FeatureClusterCollection(); - foreach (var item in items) - { - bool isAdded = false; - foreach (var cluster in collection.Clusters) - { - if (cluster.Value.IsNeighbor(item, similarityFunction, threshold, clusterThreshold)) - { - cluster.Value.Append(item); - isAdded = true; - break; - } - } - if (false == isAdded) - { - var cluster = new FeatureCluster(vectorExtractor); - cluster.Append(item); - collection.Add(cluster); - } - } - MergeClusters(collection, similarityFunction, threshold, clusterThreshold); - return collection; - } - - private void MergeClusters(FeatureClusterCollection collection, Func similarityFunction, float threshold, float clusterThreshold = 0.5f) - { - int lastCount = collection.Clusters.Count; - var removed = new Queue(); - do - { - var ids = collection.Clusters.Keys.ToList(); - for (var i = 0; i < ids.Count - 1; i++) - { - for (var j = i + 1; j < ids.Count; j++) - { - var c1 = collection.Clusters[ids[i]]; - var c2 = collection.Clusters[ids[j]]; - if (c1.IsNeighborCluster(c2, similarityFunction, threshold, clusterThreshold)) - { - c1.Merge(c2); - removed.Enqueue(ids[j]); - ids.RemoveAt(j); - j--; - } - } - } - while (removed.Count > 0) - { - collection.Clusters.Remove(removed.Dequeue()); - } - if (lastCount == collection.Clusters.Count) - { - break; - } - lastCount = collection.Clusters.Count; - } while (true); - } - } -} diff --git a/ZeroLevel.NN/Services/Clusterization/FeatureClusterCollection.cs b/ZeroLevel.NN/Services/Clusterization/FeatureClusterCollection.cs deleted file mode 100644 index 92b052c..0000000 --- a/ZeroLevel.NN/Services/Clusterization/FeatureClusterCollection.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ZeroLevel.NN.Clusterization -{ - public class FeatureClusterCollection - { - private int _clusterKey = 0; - private IDictionary> _clusters = new Dictionary>(); - - public IDictionary> Clusters => _clusters; - - internal void Add(FeatureCluster cluster) - { - _clusters.Add(Interlocked.Increment(ref _clusterKey), cluster); - } - } -} diff --git a/ZeroLevel.NN/Services/CommonHelper.cs b/ZeroLevel.NN/Services/CommonHelper.cs deleted file mode 100644 index e927620..0000000 --- a/ZeroLevel.NN/Services/CommonHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Zero.NN.Services -{ - internal static class CommonHelper - { - public static float Sigmoid(float x) - { - if (x >= 0) - { - return 1.0f / (1.0f + (float)Math.Exp(-x)); - } - else - { - return (float)(Math.Exp(x) / (1.0f + Math.Exp(x))); - } - } - - public static float Logit(float x) - { - if (x == 0) - { - return (float)(int.MinValue); - } - else if (x == 1) - { - return (float)(int.MaxValue); - } - else - { - return (float)Math.Log(x / (1.0f - x)); - } - } - } -} diff --git a/ZeroLevel.NN/Services/ImagePreprocessor.cs b/ZeroLevel.NN/Services/ImagePreprocessor.cs deleted file mode 100644 index 3aafc0e..0000000 --- a/ZeroLevel.NN/Services/ImagePreprocessor.cs +++ /dev/null @@ -1,374 +0,0 @@ -using Microsoft.ML.OnnxRuntime.Tensors; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public static class ImagePreprocessor - { - private static Action, float, int, int, int, int> _precompiledChannelFirstAction = new Action, float, int, int, int, int>((t, v, ind, c, i, j) => { t[ind, c, i, j] = v; }); - private static Action, float, int, int, int, int> _precompiledChannelLastAction = new Action, float, int, int, int, int>((t, v, ind, c, i, j) => { t[ind, i, j, c] = v; }); - private static Func PixelToTensorMethod(ImagePreprocessorOptions options) - { - if (options.Normalize) - { - if (options.Correction) - { - if (options.CorrectionFunc == null) - { - return new Func((b, i) => ((options.NormalizationMultiplier * (float)b) - options.Mean[i]) / options.Std[i]); - } - else - { - return new Func((b, i) => options.CorrectionFunc.Invoke(i, options.NormalizationMultiplier * (float)b)); - } - } - else - { - return new Func((b, i) => options.NormalizationMultiplier * (float)b); - } - } - else if (options.Correction) - { - if (options.CorrectionFunc == null) - { - return new Func((b, i) => (((float)b) - options.Mean[i]) / options.Std[i]); - } - else - { - return new Func((b, i) => options.CorrectionFunc.Invoke(i, (float)b)); - } - } - return new Func((b, _) => (float)b); - } - - //private static int CalculateFragmentsCount(Image image, ImagePreprocessorOptions options) - //{ - // int count = (options.Crop.SaveOriginal ? 1 : 0); - // var Sw = image.Width; // ширина оригинала - // var Sh = image.Height; // высота оригинала - - // var CRw = options.Crop.Width; // ширина кропа - // var CRh = options.Crop.Height; // высота кропа - - // var Dx = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefWidth * CRw) : CRw; // сдвиг по OX к следующему кропу - // var Dy = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefHeight * CRh) : CRh; // сдвиг по OY к следующему кропу - - // for (int x = 0; x < Sw; x += Dx) - // { - // for (int y = 0; y < Sh; y += Dy) - // { - // count++; - // } - // } - // return count; - //} - private static int CalculateFragmentsCount(Image image, ImagePreprocessorOptions options) - { - int count = (options.Crop.SaveOriginal ? 1 : 0); - var Sw = image.Width; // ширина оригинала - var Sh = image.Height; // высота оригинала - - var CRw = options.InputWidth; // ширина кропа (равна ширине входа, т.к. изображение отресайзено подобающим образом) - var CRh = options.InputHeight; // высота кропа (равна высоте входа, т.к. изображение отресайзено подобающим образом) - - var Dx = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefWidth * CRw) : CRw; // сдвиг по OX к следующему кропу - var Dy = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefHeight * CRh) : CRh; // сдвиг по OY к следующему кропу - - for (int x = 0; x < Sw; x += Dx) - { - for (int y = 0; y < Sh; y += Dy) - { - count++; - } - } - return count; - } - private static void FillTensor(Tensor tensor, Image image, int index, ImagePreprocessorOptions options, Func pixToTensor) - { - var append = options.ChannelType == PredictorChannelType.ChannelFirst ? _precompiledChannelFirstAction : _precompiledChannelLastAction; - - ((Image)image).ProcessPixelRows(pixels => - { - if (options.InvertXY) - { - for (int y = 0; y < pixels.Height; y++) - { - Span pixelSpan = pixels.GetRowSpan(y); - for (int x = 0; x < pixels.Width; x++) - { - if (options.BGR) - { - append(tensor, pixToTensor(pixelSpan[x].B, 0), index, 0, y, x); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, y, x); - append(tensor, pixToTensor(pixelSpan[x].R, 2), index, 2, y, x); - } - else - { - append(tensor, pixToTensor(pixelSpan[x].R, 0), index, 0, y, x); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, y, x); - append(tensor, pixToTensor(pixelSpan[x].B, 2), index, 2, y, x); - } - } - } - } - else - { - for (int y = 0; y < pixels.Height; y++) - { - Span pixelSpan = pixels.GetRowSpan(y); - for (int x = 0; x < pixels.Width; x++) - { - if (options.BGR) - { - append(tensor, pixToTensor(pixelSpan[x].B, 0), index, 0, x, y); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, x, y); - append(tensor, pixToTensor(pixelSpan[x].R, 2), index, 2, x, y); - } - else - { - append(tensor, pixToTensor(pixelSpan[x].R, 0), index, 0, x, y); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, x, y); - append(tensor, pixToTensor(pixelSpan[x].B, 2), index, 2, x, y); - } - } - } - } - }); - } - - private static void FillTensor(Tensor tensor, Image image, int startX, int startY, int w, int h, int index, ImagePreprocessorOptions options, Func pixToTensor) - { - var append = options.ChannelType == PredictorChannelType.ChannelFirst ? _precompiledChannelFirstAction : _precompiledChannelLastAction; - - if (image.PixelType.BitsPerPixel != 24) - { - var i = image; - image = i.CloneAs(); - i.Dispose(); - } - - ((Image)image).ProcessPixelRows(pixels => - { - if (options.InvertXY) - { - for (int y = startY; y < h; y++) - { - Span pixelSpan = pixels.GetRowSpan(y); - for (int x = startX; x < w; x++) - { - if (options.BGR) - { - append(tensor, pixToTensor(pixelSpan[x].B, 0), index, 0, y, x); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, y, x); - append(tensor, pixToTensor(pixelSpan[x].R, 2), index, 2, y, x); - } - else - { - append(tensor, pixToTensor(pixelSpan[x].R, 0), index, 0, y, x); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, y, x); - append(tensor, pixToTensor(pixelSpan[x].B, 2), index, 2, y, x); - } - } - } - } - else - { - for (int y = startY; y < h; y++) - { - Span pixelSpan = pixels.GetRowSpan(y); - for (int x = startX; x < w; x++) - { - if (options.BGR) - { - append(tensor, pixToTensor(pixelSpan[x].B, 0), index, 0, x, y); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, x, y); - append(tensor, pixToTensor(pixelSpan[x].R, 2), index, 2, x, y); - } - else - { - append(tensor, pixToTensor(pixelSpan[x].R, 0), index, 0, x, y); - append(tensor, pixToTensor(pixelSpan[x].G, 1), index, 1, x, y); - append(tensor, pixToTensor(pixelSpan[x].B, 2), index, 2, x, y); - } - } - } - } - }); - } - - private static Tensor InitInputTensor(ImagePreprocessorOptions options, int batchSize = 1) - { - switch (options.ChannelType) - { - case PredictorChannelType.ChannelFirst: - return options.InvertXY - ? new DenseTensor(new[] { batchSize, options.Channels, options.InputHeight, options.InputWidth }) - : new DenseTensor(new[] { batchSize, options.Channels, options.InputWidth, options.InputHeight }); - default: - return options.InvertXY - ? new DenseTensor(new[] { batchSize, options.InputHeight, options.InputWidth, options.Channels }) - : new DenseTensor(new[] { batchSize, options.InputWidth, options.InputHeight, options.Channels }); - } - } - - - public static ImagePredictionInput[] ToTensors(this Image image, ImagePreprocessorOptions options) - { - ImagePredictionInput[] result = null; - var pixToTensor = PixelToTensorMethod(options); - options.Channels = image.PixelType.BitsPerPixel >> 3; - if (options.Crop.Enabled) - { - // Размеры оригинального изображения - var Sw = image.Width; - var Sh = image.Height; - - // Создание ресайза для целочисленного прохода кропами шириной CRw и высотой CRh - var resizedForCropWidthKoef = options.InputWidth / (double)options.Crop.Width; - var resizedForCropHeightKoef = options.InputHeight / (double)options.Crop.Height; - - // Размеры для ресайза изображения к размеру по которому удобно идти кропами - var resizedForCropWidth = (int)Math.Round(Sw * resizedForCropWidthKoef, MidpointRounding.ToEven); - var resizedForCropHeight = (int)Math.Round(Sh * resizedForCropHeightKoef, MidpointRounding.ToEven); - - // Размеры кропа, равны входу сети, а не (options.Crop.Width, options.Crop.Height), т.к. для оптимизации изображение будет предварительно отресайзено - var CRw = options.InputWidth; - var CRh = options.InputHeight; - - // Расчет сдвигов между кропами - var Dx = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefWidth * CRw) : CRw; - var Dy = options.Crop.Overlap ? (int)(options.Crop.OverlapKoefHeight * CRh) : CRh; - - using (var source = image.Clone(img => img.Resize(resizedForCropWidth, resizedForCropHeight, KnownResamplers.Bicubic))) - { - // Количество тензоров всего, во всех батчах суммарно - var count = CalculateFragmentsCount(source, options); - - // Проверка, укладывается ли количество тензоров поровну в батчи - int offset = count % options.MaxBatchSize; - - // Количество батчей - int count_tensor_batches = count / options.MaxBatchSize + (offset == 0 ? 0 : 1); - - // Батчи - var tensors = new ImagePredictionInput[count_tensor_batches]; - - // Инициализация батчей - Parallel.For(0, count_tensor_batches, batch_index => - { - if (batch_index < count_tensor_batches - 1) - { - tensors[batch_index] = new ImagePredictionInput - { - Tensor = InitInputTensor(options, options.MaxBatchSize), - Offsets = new OffsetBox[options.MaxBatchSize], - Count = options.MaxBatchSize - }; - } - else - { - tensors[batch_index] = new ImagePredictionInput - { - Tensor = InitInputTensor(options, offset == 0 ? options.MaxBatchSize : offset), - Offsets = new OffsetBox[offset == 0 ? options.MaxBatchSize : offset], - Count = offset == 0 ? options.MaxBatchSize : offset - }; - } - }); - - // Заполнение батчей - int tensor_index = 0; - - // Если используется ресайз оригинала кроме кропов, пишется в первый батч в первый тензор - if (options.Crop.SaveOriginal) - { - using (var copy = source.Clone(img => img.Resize(options.InputWidth, options.InputHeight, KnownResamplers.Bicubic))) - { - FillTensor(tensors[0].Tensor, copy, 0, options, pixToTensor); - tensors[tensor_index].Offsets[0] = new OffsetBox(0, 0, image.Width, image.Height); - } - tensor_index++; - } - tensor_index--; - Parallel.ForEach(SteppedIterator(0, source.Width, Dx), x => - { - // Можно запараллелить и тут, но выигрыш дает малоощутимый - for (int y = 0; y < source.Height; y += Dy) - { - var current_index = Interlocked.Increment(ref tensor_index); - // Индекс тензора внутри батча - var b_index = current_index % options.MaxBatchSize; - // Индекс батча - var p_index = (int)Math.Round((double)current_index / (double)options.MaxBatchSize, MidpointRounding.ToNegativeInfinity); - int w = CRw; - if ((x + CRw) > source.Width) - { - w = source.Width - x; - } - int h = CRh; - if ((y + CRh) > source.Height) - { - h = source.Height - y; - } - // Заполнение b_index тензора в p_index батче - FillTensor(tensors[p_index].Tensor, source, x, y, w, h, b_index, options, pixToTensor); - // Указание смещений для данного тензора - tensors[p_index].Offsets[b_index] = new OffsetBox(x, y, options.Crop.Width, options.Crop.Height); - } - }); - return tensors; - } - } - // if resize only - result = new ImagePredictionInput[1]; - using (var copy = image.Clone(img => img.Resize(options.InputWidth, options.InputHeight, KnownResamplers.Bicubic))) - { - Tensor tensor = InitInputTensor(options); - FillTensor(tensor, copy, 0, options, pixToTensor); - result[0] = new ImagePredictionInput - { - Count = 1, - Offsets = null, - Tensor = tensor - }; - } - return result; - } - - - private static IEnumerable SteppedIterator(int startIndex, int endIndex, int stepSize) - { - for (int i = startIndex; i < endIndex; i += stepSize) - { - yield return i; - } - } - - public static Image Crop(Image source, float x1, float y1, float x2, float y2) - { - int left = 0; - int right = 0; - int top = 0; - int bottom = 0; - - int width = (int)(x2 - x1); - int height = (int)(y2 - y1); - - if (x1 < 0) { left = (int)-x1; x1 = 0; } - if (x2 > source.Width) { right = (int)(x2 - source.Width); x2 = source.Width - 1; } - if (y1 < 0) { top = (int)-y1; y1 = 0; } - if (y2 > source.Height) { bottom = (int)(y2 - source.Height); y2 = source.Height - 1; } - - if (left + right + top + bottom > 0) - { - var backgroundImage = new Image(SixLabors.ImageSharp.Configuration.Default, width, height, new Rgb24(0, 0, 0)); - using (var crop = source.Clone(img => img.Crop(new Rectangle((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1))))) - { - backgroundImage.Mutate(bg => bg.DrawImage(crop, new Point(left, top), 1f)); - } - return backgroundImage; - } - return source.Clone(img => img.Crop(new Rectangle((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1)))); - } - } -} diff --git a/ZeroLevel.NN/Services/KNN/Point.cs b/ZeroLevel.NN/Services/KNN/Point.cs deleted file mode 100644 index fb37d3c..0000000 --- a/ZeroLevel.NN/Services/KNN/Point.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace ZeroLevel.NN.Services.KNN -{ - public class FPoint - { - public float[] Values { get; set; } - } - - public interface IMetric - { - float Calculate(FPoint a, FPoint b); - } -} diff --git a/ZeroLevel.NN/Services/NMS.cs b/ZeroLevel.NN/Services/NMS.cs deleted file mode 100644 index c9ccee5..0000000 --- a/ZeroLevel.NN/Services/NMS.cs +++ /dev/null @@ -1,118 +0,0 @@ -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN.Services -{ - public static class NMS - { - private const float IOU_THRESHOLD = .2f; - private const float IOU_MERGE_THRESHOLD = .5f; - - public static void Apply(List boxes) - { - for (int i = 0; i < boxes.Count - 1; i++) - { - var left = boxes[i]; - for (int j = i + 1; j < boxes.Count; j++) - { - var right = boxes[j]; - if (left.Class == right.Class) - { - // удаление вложенных боксов - var ni = NestingIndex(left, right); - if (ni == 1) - { - boxes.RemoveAt(i); - i--; - break; - } - else if (ni == 2) - { - boxes.RemoveAt(j); - j--; - } - // ----------------------------- - else - { - var iou = IOU(left, right); - if (iou > IOU_THRESHOLD) - { - if (left.Score > right.Score) - { - boxes.RemoveAt(j); - j--; - } - else - { - boxes.RemoveAt(i); - i--; - break; - } - } - /*else if (threshold > 0.01f && iou > 0.2f) - { - // UNION - var x1 = Math.Min(left.X, right.X); - var y1 = Math.Min(left.Y, right.Y); - var x2 = Math.Max(left.X + left.W, right.X + right.W); - var y2 = Math.Max(left.Y + left.H, right.Y + right.H); - var w = x2 - x1; - var h = y2 - y1; - boxes.RemoveAt(j); - boxes.RemoveAt(i); - boxes.Add(new Prediction - { - Class = left.Class, - Label = left.Label, - Score = Math.Max(left.Score, right.Score), - Cx = x1 + w / 2.0f, - Cy = y1 + h / 2.0f, - W = w, - H = h - }); - i--; - break; - }*/ - } - } - } - } - } - - /// - /// проверка на вложенность боксов - /// - static int NestingIndex(YoloPrediction box1, YoloPrediction box2) - { - if (box1.X > box2.X && - box1.Y > box2.Y && - (box1.X + box1.W) < (box2.X + box2.W) && - (box1.Y + box1.H) < (box2.Y + box2.H)) - { - return 1; - } - if (box2.X > box1.X && - box2.Y > box1.Y && - (box2.X + box2.W) < (box1.X + box1.W) && - (box2.Y + box2.H) < (box1.Y + box1.H)) - { - return 2; - } - return 0; - } - - static float IOU(YoloPrediction box1, YoloPrediction box2) - { - var left = (float)Math.Max(box1[0], box2[0]); - var right = (float)Math.Min(box1[2], box2[2]); - - var top = (float)Math.Max(box1[1], box2[1]); - var bottom = (float)Math.Min(box1[3], box2[3]); - - var width = right - left; - var height = bottom - top; - var intersectionArea = width * height; - - return intersectionArea / (float)(box1.Area + box2.Area - intersectionArea); - } - } -} diff --git a/ZeroLevel.NN/Services/SSDNN.cs b/ZeroLevel.NN/Services/SSDNN.cs deleted file mode 100644 index 8ed3cac..0000000 --- a/ZeroLevel.NN/Services/SSDNN.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Microsoft.ML.OnnxRuntime; -using Microsoft.ML.OnnxRuntime.Tensors; -using ZeroLevel.NN.Models; - -namespace ZeroLevel.NN -{ - public abstract class SSDNN - : IDisposable - { - private readonly InferenceSession _session; - - public SSDNN(string modelPath, bool gpu = false) - { - if (gpu) - { - try - { - var so = SessionOptions.MakeSessionOptionWithCudaProvider(0); - so.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_FATAL; - so.GraphOptimizationLevel = GraphOptimizationLevel.ORT_DISABLE_ALL; - _session = new InferenceSession(modelPath, so); - } - catch (Exception ex) - { - Log.Error(ex, "Fault create InferenceSession with CUDA"); - _session = new InferenceSession(modelPath); - } - } - else - { - _session = new InferenceSession(modelPath); - } - } - - protected void Extract(IDictionary> input, Action>> inputHandler) - { - var container = new List(); - foreach (var pair in input) - { - container.Add(NamedOnnxValue.CreateFromTensor(pair.Key, pair.Value)); - } - using (var output = _session.Run(container)) - { - var result = new Dictionary>(); - foreach (var o in output) - { - result.Add(o.Name, o.AsTensor()); - } - inputHandler.Invoke(result); - } - } - - /// - /// Scale input vectors individually to unit norm (vector length). - /// - protected void Norm(float[] vector) - { - var totalSum = vector.Sum(v => v * v); - var length = (float)Math.Sqrt(totalSum); - var inverseLength = 1.0f / length; - for (int i = 0; i < vector.Length; i++) - { - vector[i] *= inverseLength; - } - } - protected ImagePredictionInput[] MakeInputBatch(Image image, ImagePreprocessorOptions options) - { - return ImagePreprocessor.ToTensors(image, options); - } - - protected Tensor MakeInput(Image image, ImagePreprocessorOptions options) - { - var input = ImagePreprocessor.ToTensors(image, options); - return input[0].Tensor; - } - - protected int Argmax(float[] embedding) - { - if (embedding.Length == 0) return -1; - var im = 0; - var max = embedding[0]; - for (var i = 1; i < embedding.Length; i++) - { - if (embedding[i] > max) - { - im = i; - max = embedding[i]; - } - } - return im; - } - - public void Dispose() - { - _session?.Dispose(); - } - } -} diff --git a/ZeroLevel.NN/ZeroLevel.NN.csproj b/ZeroLevel.NN/ZeroLevel.NN.csproj deleted file mode 100644 index c0c8c9a..0000000 --- a/ZeroLevel.NN/ZeroLevel.NN.csproj +++ /dev/null @@ -1,48 +0,0 @@ - - - - net6.0 - enable - disable - True - embedded - none - 1.0.0.3 - Ogoun - Ogoun - Copyright Ogoun 2022 - zero.png - https://github.com/ogoun/Zero/wiki - https://github.com/ogoun/Zero - git - - AnyCPU;x64 - - - - False - - - - False - - - - - True - \ - - - - - - - - - - - - - - - diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/collections.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/collections.proto deleted file mode 100644 index 794f3ee..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/collections.proto +++ /dev/null @@ -1,444 +0,0 @@ -syntax = "proto3"; -package qdrant; - -message VectorParams { - uint64 size = 1; // Size of the vectors - Distance distance = 2; // Distance function used for comparing vectors - optional HnswConfigDiff hnsw_config = 3; // Configuration of vector HNSW graph. If omitted - the collection configuration will be used - optional QuantizationConfig quantization_config = 4; // Configuration of vector quantization config. If omitted - the collection configuration will be used - optional bool on_disk = 5; // If true - serve vectors from disk. If set to false, the vectors will be loaded in RAM. -} - -message VectorParamsDiff { - optional HnswConfigDiff hnsw_config = 1; // Update params for HNSW index. If empty object - it will be unset - optional QuantizationConfigDiff quantization_config = 2; // Update quantization params. If none - it is left unchanged. - optional bool on_disk = 3; // If true - serve vectors from disk. If set to false, the vectors will be loaded in RAM. -} - -message VectorParamsMap { - map map = 1; -} - -message VectorParamsDiffMap { - map map = 1; -} - -message VectorsConfig { - oneof config { - VectorParams params = 1; - VectorParamsMap params_map = 2; - } -} - -message VectorsConfigDiff { - oneof config { - VectorParamsDiff params = 1; - VectorParamsDiffMap params_map = 2; - } -} - -message GetCollectionInfoRequest { - string collection_name = 1; // Name of the collection -} - -message ListCollectionsRequest { -} - -message CollectionDescription { - string name = 1; // Name of the collection -} - -message GetCollectionInfoResponse { - CollectionInfo result = 1; - double time = 2; // Time spent to process -} - -message ListCollectionsResponse { - repeated CollectionDescription collections = 1; - double time = 2; // Time spent to process -} - -enum Distance { - UnknownDistance = 0; - Cosine = 1; - Euclid = 2; - Dot = 3; -} - -enum CollectionStatus { - UnknownCollectionStatus = 0; - Green = 1; // All segments are ready - Yellow = 2; // Optimization in process - Red = 3; // Something went wrong -} - -enum PayloadSchemaType { - UnknownType = 0; - Keyword = 1; - Integer = 2; - Float = 3; - Geo = 4; - Text = 5; - Bool = 6; -} - -enum QuantizationType { - UnknownQuantization = 0; - Int8 = 1; -} - -enum CompressionRatio { - x4 = 0; - x8 = 1; - x16 = 2; - x32 = 3; - x64 = 4; -} - -message OptimizerStatus { - bool ok = 1; - string error = 2; -} - -message HnswConfigDiff { - /* - Number of edges per node in the index graph. Larger the value - more accurate the search, more space required. - */ - optional uint64 m = 1; - /* - Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build the index. - */ - optional uint64 ef_construct = 2; - /* - Minimal size (in KiloBytes) of vectors for additional payload-based indexing. - If the payload chunk is smaller than `full_scan_threshold` additional indexing won't be used - - in this case full-scan search should be preferred by query planner and additional indexing is not required. - Note: 1 Kb = 1 vector of size 256 - */ - optional uint64 full_scan_threshold = 3; - /* - Number of parallel threads used for background index building. If 0 - auto selection. - */ - optional uint64 max_indexing_threads = 4; - /* - Store HNSW index on disk. If set to false, the index will be stored in RAM. - */ - optional bool on_disk = 5; - /* - Number of additional payload-aware links per node in the index graph. If not set - regular M parameter will be used. - */ - optional uint64 payload_m = 6; -} - -message WalConfigDiff { - optional uint64 wal_capacity_mb = 1; // Size of a single WAL block file - optional uint64 wal_segments_ahead = 2; // Number of segments to create in advance -} - -message OptimizersConfigDiff { - /* - The minimal fraction of deleted vectors in a segment, required to perform segment optimization - */ - optional double deleted_threshold = 1; - /* - The minimal number of vectors in a segment, required to perform segment optimization - */ - optional uint64 vacuum_min_vector_number = 2; - /* - Target amount of segments the optimizer will try to keep. - Real amount of segments may vary depending on multiple parameters: - - - Amount of stored points. - - Current write RPS. - - It is recommended to select the default number of segments as a factor of the number of search threads, - so that each segment would be handled evenly by one of the threads. - */ - optional uint64 default_segment_number = 3; - /* - Do not create segments larger this size (in kilobytes). - Large segments might require disproportionately long indexation times, - therefore it makes sense to limit the size of segments. - - If indexing speed is more important - make this parameter lower. - If search speed is more important - make this parameter higher. - Note: 1Kb = 1 vector of size 256 - If not set, will be automatically selected considering the number of available CPUs. - */ - optional uint64 max_segment_size = 4; - /* - Maximum size (in kilobytes) of vectors to store in-memory per segment. - Segments larger than this threshold will be stored as read-only memmaped file. - - Memmap storage is disabled by default, to enable it, set this threshold to a reasonable value. - - To disable memmap storage, set this to `0`. - - Note: 1Kb = 1 vector of size 256 - */ - optional uint64 memmap_threshold = 5; - /* - Maximum size (in kilobytes) of vectors allowed for plain index, exceeding this threshold will enable vector indexing - - Default value is 20,000, based on . - - To disable vector indexing, set to `0`. - - Note: 1kB = 1 vector of size 256. - */ - optional uint64 indexing_threshold = 6; - /* - Interval between forced flushes. - */ - optional uint64 flush_interval_sec = 7; - /* - Max number of threads, which can be used for optimization. If 0 - `NUM_CPU - 1` will be used - */ - optional uint64 max_optimization_threads = 8; -} - -message ScalarQuantization { - QuantizationType type = 1; // Type of quantization - optional float quantile = 2; // Number of bits to use for quantization - optional bool always_ram = 3; // If true - quantized vectors always will be stored in RAM, ignoring the config of main storage -} - -message ProductQuantization { - CompressionRatio compression = 1; // Compression ratio - optional bool always_ram = 2; // If true - quantized vectors always will be stored in RAM, ignoring the config of main storage -} - -message BinaryQuantization { - optional bool always_ram = 1; // If true - quantized vectors always will be stored in RAM, ignoring the config of main storage -} - -message QuantizationConfig { - oneof quantization { - ScalarQuantization scalar = 1; - ProductQuantization product = 2; - BinaryQuantization binary = 3; - } -} - -message Disabled { - -} - -message QuantizationConfigDiff { - oneof quantization { - ScalarQuantization scalar = 1; - ProductQuantization product = 2; - Disabled disabled = 3; - BinaryQuantization binary = 4; - } -} - -message CreateCollection { - string collection_name = 1; // Name of the collection - reserved 2; // Deprecated - reserved 3; // Deprecated - optional HnswConfigDiff hnsw_config = 4; // Configuration of vector index - optional WalConfigDiff wal_config = 5; // Configuration of the Write-Ahead-Log - optional OptimizersConfigDiff optimizers_config = 6; // Configuration of the optimizers - optional uint32 shard_number = 7; // Number of shards in the collection, default is 1 for standalone, otherwise equal to the number of nodes. Minimum is 1 - optional bool on_disk_payload = 8; // If true - point's payload will not be stored in memory - optional uint64 timeout = 9; // Wait timeout for operation commit in seconds, if not specified - default value will be supplied - optional VectorsConfig vectors_config = 10; // Configuration for vectors - optional uint32 replication_factor = 11; // Number of replicas of each shard that network tries to maintain, default = 1 - optional uint32 write_consistency_factor = 12; // How many replicas should apply the operation for us to consider it successful, default = 1 - optional string init_from_collection = 13; // Specify name of the other collection to copy data from - optional QuantizationConfig quantization_config = 14; // Quantization configuration of vector -} - -message UpdateCollection { - string collection_name = 1; // Name of the collection - optional OptimizersConfigDiff optimizers_config = 2; // New configuration parameters for the collection. This operation is blocking, it will only proceed once all current optimizations are complete - optional uint64 timeout = 3; // Wait timeout for operation commit in seconds if blocking, if not specified - default value will be supplied - optional CollectionParamsDiff params = 4; // New configuration parameters for the collection - optional HnswConfigDiff hnsw_config = 5; // New HNSW parameters for the collection index - optional VectorsConfigDiff vectors_config = 6; // New vector parameters - optional QuantizationConfigDiff quantization_config = 7; // Quantization configuration of vector -} - -message DeleteCollection { - string collection_name = 1; // Name of the collection - optional uint64 timeout = 2; // Wait timeout for operation commit in seconds, if not specified - default value will be supplied -} - -message CollectionOperationResponse { - bool result = 1; // if operation made changes - double time = 2; // Time spent to process -} - -message CollectionParams { - reserved 1; // Deprecated - reserved 2; // Deprecated - uint32 shard_number = 3; // Number of shards in collection - bool on_disk_payload = 4; // If true - point's payload will not be stored in memory - optional VectorsConfig vectors_config = 5; // Configuration for vectors - optional uint32 replication_factor = 6; // Number of replicas of each shard that network tries to maintain - optional uint32 write_consistency_factor = 7; // How many replicas should apply the operation for us to consider it successful -} - -message CollectionParamsDiff { - optional uint32 replication_factor = 1; // Number of replicas of each shard that network tries to maintain - optional uint32 write_consistency_factor = 2; // How many replicas should apply the operation for us to consider it successful - optional bool on_disk_payload = 3; // If true - point's payload will not be stored in memory -} - -message CollectionConfig { - CollectionParams params = 1; // Collection parameters - HnswConfigDiff hnsw_config = 2; // Configuration of vector index - OptimizersConfigDiff optimizer_config = 3; // Configuration of the optimizers - WalConfigDiff wal_config = 4; // Configuration of the Write-Ahead-Log - optional QuantizationConfig quantization_config = 5; // Configuration of the vector quantization -} - -enum TokenizerType { - Unknown = 0; - Prefix = 1; - Whitespace = 2; - Word = 3; - Multilingual = 4; -} - -message TextIndexParams { - TokenizerType tokenizer = 1; // Tokenizer type - optional bool lowercase = 2; // If true - all tokens will be lowercase - optional uint64 min_token_len = 3; // Minimal token length - optional uint64 max_token_len = 4; // Maximal token length -} - -message PayloadIndexParams { - oneof index_params { - TextIndexParams text_index_params = 1; // Parameters for text index - } -} - -message PayloadSchemaInfo { - PayloadSchemaType data_type = 1; // Field data type - optional PayloadIndexParams params = 2; // Field index parameters - optional uint64 points = 3; // Number of points indexed within this field indexed -} - -message CollectionInfo { - CollectionStatus status = 1; // operating condition of the collection - OptimizerStatus optimizer_status = 2; // status of collection optimizers - uint64 vectors_count = 3; // number of vectors in the collection - uint64 segments_count = 4; // Number of independent segments - reserved 5; // Deprecated - reserved 6; // Deprecated - CollectionConfig config = 7; // Configuration - map payload_schema = 8; // Collection data types - uint64 points_count = 9; // number of points in the collection - optional uint64 indexed_vectors_count = 10; // number of indexed vectors in the collection. -} - -message ChangeAliases { - repeated AliasOperations actions = 1; // List of actions - optional uint64 timeout = 2; // Wait timeout for operation commit in seconds, if not specified - default value will be supplied -} - -message AliasOperations { - oneof action { - CreateAlias create_alias = 1; - RenameAlias rename_alias = 2; - DeleteAlias delete_alias = 3; - } -} - -message CreateAlias { - string collection_name = 1; // Name of the collection - string alias_name = 2; // New name of the alias -} - -message RenameAlias { - string old_alias_name = 1; // Name of the alias to rename - string new_alias_name = 2; // Name of the alias -} - -message DeleteAlias { - string alias_name = 1; // Name of the alias -} - -message ListAliasesRequest { -} - -message ListCollectionAliasesRequest { - string collection_name = 1; // Name of the collection -} - -message AliasDescription { - string alias_name = 1; // Name of the alias - string collection_name = 2; // Name of the collection -} - -message ListAliasesResponse { - repeated AliasDescription aliases = 1; - double time = 2; // Time spent to process -} - -message CollectionClusterInfoRequest { - string collection_name = 1; // Name of the collection -} - -enum ReplicaState { - Active = 0; // Active and sound - Dead = 1; // Failed for some reason - Partial = 2; // The shard is partially loaded and is currently receiving data from other shards - Initializing = 3; // Collection is being created - Listener = 4; // A shard which receives data, but is not used for search; Useful for backup shards -} - -message LocalShardInfo { - uint32 shard_id = 1; // Local shard id - uint64 points_count = 2; // Number of points in the shard - ReplicaState state = 3; // Is replica active -} - -message RemoteShardInfo { - uint32 shard_id = 1; // Local shard id - uint64 peer_id = 2; // Remote peer id - ReplicaState state = 3; // Is replica active -} - -message ShardTransferInfo { - uint32 shard_id = 1; // Local shard id - uint64 from = 2; - uint64 to = 3; - bool sync = 4; // If `true` transfer is a synchronization of a replicas; If `false` transfer is a moving of a shard from one peer to another -} - -message CollectionClusterInfoResponse { - uint64 peer_id = 1; // ID of this peer - uint64 shard_count = 2; // Total number of shards - repeated LocalShardInfo local_shards = 3; // Local shards - repeated RemoteShardInfo remote_shards = 4; // Remote shards - repeated ShardTransferInfo shard_transfers = 5; // Shard transfers -} - -message MoveShard { - uint32 shard_id = 1; // Local shard id - uint64 from_peer_id = 2; - uint64 to_peer_id = 3; -} - -message Replica { - uint32 shard_id = 1; - uint64 peer_id = 2; -} - -message UpdateCollectionClusterSetupRequest { - string collection_name = 1; // Name of the collection - oneof operation { - MoveShard move_shard = 2; - MoveShard replicate_shard = 3; - MoveShard abort_transfer = 4; - Replica drop_replica = 5; - } - optional uint64 timeout = 6; // Wait timeout for operation commit in seconds, if not specified - default value will be supplied -} - -message UpdateCollectionClusterSetupResponse { - bool result = 1; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/collections_internal_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/collections_internal_service.proto deleted file mode 100644 index 465a511..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/collections_internal_service.proto +++ /dev/null @@ -1,26 +0,0 @@ -syntax = "proto3"; - -import "Protos/collections.proto"; - -package qdrant; - -service CollectionsInternal { - /* - Get collection info - */ - rpc Get (GetCollectionInfoRequestInternal) returns (GetCollectionInfoResponse) {} - /* - Initiate shard transfer - */ - rpc Initiate (InitiateShardTransferRequest) returns (CollectionOperationResponse) {} -} - -message GetCollectionInfoRequestInternal { - GetCollectionInfoRequest get_collectionInfoRequest = 1; - uint32 shard_id = 2; -} - -message InitiateShardTransferRequest { - string collection_name = 1; // Name of the collection - uint32 shard_id = 2; // Id of the temporary shard -} \ No newline at end of file diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/collections_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/collections_service.proto deleted file mode 100644 index 2e33674..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/collections_service.proto +++ /dev/null @@ -1,48 +0,0 @@ -syntax = "proto3"; - -import "Protos/collections.proto"; - -package qdrant; - -service Collections { - /* - Get detailed information about specified existing collection - */ - rpc Get (GetCollectionInfoRequest) returns (GetCollectionInfoResponse) {} - /* - Get list name of all existing collections - */ - rpc List (ListCollectionsRequest) returns (ListCollectionsResponse) {} - /* - Create new collection with given parameters - */ - rpc Create (CreateCollection) returns (CollectionOperationResponse) {} - /* - Update parameters of the existing collection - */ - rpc Update (UpdateCollection) returns (CollectionOperationResponse) {} - /* - Drop collection and all associated data - */ - rpc Delete (DeleteCollection) returns (CollectionOperationResponse) {} - /* - Update Aliases of the existing collection - */ - rpc UpdateAliases (ChangeAliases) returns (CollectionOperationResponse) {} - /* - Get list of all aliases for a collection - */ - rpc ListCollectionAliases (ListCollectionAliasesRequest) returns (ListAliasesResponse) {} - /* - Get list of all aliases for all existing collections - */ - rpc ListAliases (ListAliasesRequest) returns (ListAliasesResponse) {} - /* - Get cluster information for a collection - */ - rpc CollectionClusterInfo (CollectionClusterInfoRequest) returns (CollectionClusterInfoResponse) {} - /* - Update cluster setup for a collection - */ - rpc UpdateCollectionClusterSetup (UpdateCollectionClusterSetupRequest) returns (UpdateCollectionClusterSetupResponse) {} -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/json_with_int.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/json_with_int.proto deleted file mode 100644 index 3fc496e..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/json_with_int.proto +++ /dev/null @@ -1,61 +0,0 @@ -// Fork of the google.protobuf.Value with explicit support for integer values - -syntax = "proto3"; - -package qdrant; - -// `Struct` represents a structured data value, consisting of fields -// which map to dynamically typed values. In some languages, `Struct` -// might be supported by a native representation. For example, in -// scripting languages like JS a struct is represented as an -// object. The details of that representation are described together -// with the proto support for the language. -// -// The JSON representation for `Struct` is a JSON object. -message Struct { - // Unordered map of dynamically typed values. - map fields = 1; -} - -// `Value` represents a dynamically typed value which can be either -// null, a number, a string, a boolean, a recursive struct value, or a -// list of values. A producer of value is expected to set one of those -// variants, absence of any variant indicates an error. -// -// The JSON representation for `Value` is a JSON value. -message Value { - // The kind of value. - oneof kind { - // Represents a null value. - NullValue null_value = 1; - // Represents a double value. - double double_value = 2; - // Represents an integer value - int64 integer_value = 3; - // Represents a string value. - string string_value = 4; - // Represents a boolean value. - bool bool_value = 5; - // Represents a structured value. - Struct struct_value = 6; - // Represents a repeated `Value`. - ListValue list_value = 7; - } -} - -// `NullValue` is a singleton enumeration to represent the null value for the -// `Value` type union. -// -// The JSON representation for `NullValue` is JSON `null`. -enum NullValue { - // Null value. - NULL_VALUE = 0; -} - -// `ListValue` is a wrapper around a repeated field of values. -// -// The JSON representation for `ListValue` is a JSON array. -message ListValue { - // Repeated field of dynamically typed values. - repeated Value values = 1; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/points.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/points.proto deleted file mode 100644 index 9d4c985..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/points.proto +++ /dev/null @@ -1,622 +0,0 @@ -syntax = "proto3"; - -package qdrant; - -import "Protos/json_with_int.proto"; -import "Protos/collections.proto"; - - -enum WriteOrderingType { - Weak = 0; // Write operations may be reordered, works faster, default - Medium = 1; // Write operations go through dynamically selected leader, may be inconsistent for a short period of time in case of leader change - Strong = 2; // Write operations go through the permanent leader, consistent, but may be unavailable if leader is down -} - -message WriteOrdering { - WriteOrderingType type = 1; // Write ordering guarantees -} - -enum ReadConsistencyType { - All = 0; // Send request to all nodes and return points which are present on all of them - Majority = 1; // Send requests to all nodes and return points which are present on majority of them - Quorum = 2; // Send requests to half + 1 nodes, return points which are present on all of them -} - -message ReadConsistency { - oneof value { - ReadConsistencyType type = 1; // Common read consistency configurations - uint64 factor = 2; // Send request to a specified number of nodes, and return points which are present on all of them - } -} - -// --------------------------------------------- -// ------------- Point Id Requests ------------- -// --------------------------------------------- - -message PointId { - oneof point_id_options { - uint64 num = 1; // Numerical ID of the point - string uuid = 2; // UUID - } -} - -message Vector { - repeated float data = 1; -} - -// --------------------------------------------- -// ---------------- RPC Requests --------------- -// --------------------------------------------- - -message UpsertPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - repeated PointStruct points = 3; - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -message DeletePoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - PointsSelector points = 3; // Affected points - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -message GetPoints { - string collection_name = 1; // name of the collection - repeated PointId ids = 2; // List of points to retrieve - reserved 3; // deprecated "with_vector" field - WithPayloadSelector with_payload = 4; // Options for specifying which payload to include or not - optional WithVectorsSelector with_vectors = 5; // Options for specifying which vectors to include into response - optional ReadConsistency read_consistency = 6; // Options for specifying read consistency guarantees -} - -message UpdatePointVectors { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - repeated PointVectors points = 3; // List of points and vectors to update - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -message PointVectors { - PointId id = 1; // ID to update vectors for - Vectors vectors = 2; // Named vectors to update, leave others intact -} - -message DeletePointVectors { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - PointsSelector points_selector = 3; // Affected points - VectorsSelector vectors = 4; // List of vector names to delete - optional WriteOrdering ordering = 5; // Write ordering guarantees -} - -message SetPayloadPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - map payload = 3; // New payload values - reserved 4; // List of point to modify, deprecated - optional PointsSelector points_selector = 5; // Affected points - optional WriteOrdering ordering = 6; // Write ordering guarantees -} - -message DeletePayloadPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - repeated string keys = 3; // List of keys to delete - reserved 4; // Affected points, deprecated - optional PointsSelector points_selector = 5; // Affected points - optional WriteOrdering ordering = 6; // Write ordering guarantees -} - -message ClearPayloadPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - PointsSelector points = 3; // Affected points - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -enum FieldType { - FieldTypeKeyword = 0; - FieldTypeInteger = 1; - FieldTypeFloat = 2; - FieldTypeGeo = 3; - FieldTypeText = 4; - FieldTypeBool = 5; -} - -message CreateFieldIndexCollection { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - string field_name = 3; // Field name to index - optional FieldType field_type = 4; // Field type. - optional PayloadIndexParams field_index_params = 5; // Payload index params. - optional WriteOrdering ordering = 6; // Write ordering guarantees -} - -message DeleteFieldIndexCollection { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - string field_name = 3; // Field name to delete - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -message PayloadIncludeSelector { - repeated string fields = 1; // List of payload keys to include into result -} - -message PayloadExcludeSelector { - repeated string fields = 1; // List of payload keys to exclude from the result -} - -message WithPayloadSelector { - oneof selector_options { - bool enable = 1; // If `true` - return all payload, if `false` - none - PayloadIncludeSelector include = 2; - PayloadExcludeSelector exclude = 3; - } -} - -message NamedVectors { - map vectors = 1; -} - -message Vectors { - oneof vectors_options { - Vector vector = 1; - NamedVectors vectors = 2; - } -} - -message VectorsSelector { - repeated string names = 1; // List of vectors to include into result -} - -message WithVectorsSelector { - oneof selector_options { - bool enable = 1; // If `true` - return all vectors, if `false` - none - VectorsSelector include = 2; // List of payload keys to include into result - } -} - -message QuantizationSearchParams { - /* - If set to true, search will ignore quantized vector data - */ - optional bool ignore = 1; - - /* - If true, use original vectors to re-score top-k results. Default is true. - */ - optional bool rescore = 2; - - /* - Oversampling factor for quantization. - - Defines how many extra vectors should be pre-selected using quantized index, - and then re-scored using original vectors. - - For example, if `oversampling` is 2.4 and `limit` is 100, then 240 vectors will be pre-selected using quantized index, - and then top-100 will be returned after re-scoring. - */ - optional double oversampling = 3; -} - -message SearchParams { - /* - Params relevant to HNSW index. Size of the beam in a beam-search. - Larger the value - more accurate the result, more time required for search. - */ - optional uint64 hnsw_ef = 1; - - /* - Search without approximation. If set to true, search may run long but with exact results. - */ - optional bool exact = 2; - - /* - If set to true, search will ignore quantized vector data - */ - optional QuantizationSearchParams quantization = 3; - /* - If enabled, the engine will only perform search among indexed or small segments. - Using this option prevents slow searches in case of delayed index, but does not - guarantee that all uploaded vectors will be included in search results - */ - optional bool indexed_only = 4; -} - -message SearchPoints { - string collection_name = 1; // name of the collection - repeated float vector = 2; // vector - Filter filter = 3; // Filter conditions - return only those points that satisfy the specified conditions - uint64 limit = 4; // Max number of result - reserved 5; // deprecated "with_vector" field - WithPayloadSelector with_payload = 6; // Options for specifying which payload to include or not - SearchParams params = 7; // Search config - optional float score_threshold = 8; // If provided - cut off results with worse scores - optional uint64 offset = 9; // Offset of the result - optional string vector_name = 10; // Which vector to use for search, if not specified - use default vector - optional WithVectorsSelector with_vectors = 11; // Options for specifying which vectors to include into response - optional ReadConsistency read_consistency = 12; // Options for specifying read consistency guarantees -} - -message SearchBatchPoints { - string collection_name = 1; // Name of the collection - repeated SearchPoints search_points = 2; - optional ReadConsistency read_consistency = 3; // Options for specifying read consistency guarantees -} - -message WithLookup { - string collection = 1; // Name of the collection to use for points lookup - optional WithPayloadSelector with_payload = 2; // Options for specifying which payload to include (or not) - optional WithVectorsSelector with_vectors = 3; // Options for specifying which vectors to include (or not) -} - - -message SearchPointGroups { - string collection_name = 1; // Name of the collection - repeated float vector = 2; // Vector to compare against - Filter filter = 3; // Filter conditions - return only those points that satisfy the specified conditions - uint32 limit = 4; // Max number of result - WithPayloadSelector with_payload = 5; // Options for specifying which payload to include or not - SearchParams params = 6; // Search config - optional float score_threshold = 7; // If provided - cut off results with worse scores - optional string vector_name = 8; // Which vector to use for search, if not specified - use default vector - optional WithVectorsSelector with_vectors = 9; // Options for specifying which vectors to include into response - string group_by = 10; // Payload field to group by, must be a string or number field. If there are multiple values for the field, all of them will be used. One point can be in multiple groups. - uint32 group_size = 11; // Maximum amount of points to return per group - optional ReadConsistency read_consistency = 12; // Options for specifying read consistency guarantees - optional WithLookup with_lookup = 13; // Options for specifying how to use the group id to lookup points in another collection -} - -message ScrollPoints { - string collection_name = 1; - Filter filter = 2; // Filter conditions - return only those points that satisfy the specified conditions - optional PointId offset = 3; // Start with this ID - optional uint32 limit = 4; // Max number of result - reserved 5; // deprecated "with_vector" field - WithPayloadSelector with_payload = 6; // Options for specifying which payload to include or not - optional WithVectorsSelector with_vectors = 7; // Options for specifying which vectors to include into response - optional ReadConsistency read_consistency = 8; // Options for specifying read consistency guarantees -} - -message LookupLocation { - string collection_name = 1; - optional string vector_name = 2; // Which vector to use for search, if not specified - use default vector -} - -message RecommendPoints { - string collection_name = 1; // name of the collection - repeated PointId positive = 2; // Look for vectors closest to those - repeated PointId negative = 3; // Try to avoid vectors like this - Filter filter = 4; // Filter conditions - return only those points that satisfy the specified conditions - uint64 limit = 5; // Max number of result - reserved 6; // deprecated "with_vector" field - WithPayloadSelector with_payload = 7; // Options for specifying which payload to include or not - SearchParams params = 8; // Search config - optional float score_threshold = 9; // If provided - cut off results with worse scores - optional uint64 offset = 10; // Offset of the result - optional string using = 11; // Define which vector to use for recommendation, if not specified - default vector - optional WithVectorsSelector with_vectors = 12; // Options for specifying which vectors to include into response - optional LookupLocation lookup_from = 13; // Name of the collection to use for points lookup, if not specified - use current collection - optional ReadConsistency read_consistency = 14; // Options for specifying read consistency guarantees -} - -message RecommendBatchPoints { - string collection_name = 1; // Name of the collection - repeated RecommendPoints recommend_points = 2; - optional ReadConsistency read_consistency = 3; // Options for specifying read consistency guarantees -} - -message RecommendPointGroups { - string collection_name = 1; // Name of the collection - repeated PointId positive = 2; // Look for vectors closest to those - repeated PointId negative = 3; // Try to avoid vectors like this - Filter filter = 4; // Filter conditions - return only those points that satisfy the specified conditions - uint32 limit = 5; // Max number of groups in result - WithPayloadSelector with_payload = 6; // Options for specifying which payload to include or not - SearchParams params = 7; // Search config - optional float score_threshold = 8; // If provided - cut off results with worse scores - optional string using = 9; // Define which vector to use for recommendation, if not specified - default vector - optional WithVectorsSelector with_vectors = 10; // Options for specifying which vectors to include into response - optional LookupLocation lookup_from = 11; // Name of the collection to use for points lookup, if not specified - use current collection - string group_by = 12; // Payload field to group by, must be a string or number field. If there are multiple values for the field, all of them will be used. One point can be in multiple groups. - uint32 group_size = 13; // Maximum amount of points to return per group - optional ReadConsistency read_consistency = 14; // Options for specifying read consistency guarantees - optional WithLookup with_lookup = 15; // Options for specifying how to use the group id to lookup points in another collection -} - -message CountPoints { - string collection_name = 1; // name of the collection - Filter filter = 2; // Filter conditions - return only those points that satisfy the specified conditions - optional bool exact = 3; // If `true` - return exact count, if `false` - return approximate count -} - -message PointsUpdateOperation { - message PointStructList { - repeated PointStruct points = 1; - } - message SetPayload { - map payload = 1; - optional PointsSelector points_selector = 2; // Affected points - } - message DeletePayload { - repeated string keys = 1; - optional PointsSelector points_selector = 2; // Affected points - } - message UpdateVectors { - repeated PointVectors points = 1; // List of points and vectors to update - } - message DeleteVectors { - PointsSelector points_selector = 1; // Affected points - VectorsSelector vectors = 2; // List of vector names to delete - } - - oneof operation { - PointStructList upsert = 1; - PointsSelector delete = 2; - SetPayload set_payload = 3; - SetPayload overwrite_payload = 4; - DeletePayload delete_payload = 5; - PointsSelector clear_payload = 6; - UpdateVectors update_vectors = 7; - DeleteVectors delete_vectors = 8; - } -} - -message UpdateBatchPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - repeated PointsUpdateOperation operations = 3; - optional WriteOrdering ordering = 4; // Write ordering guarantees -} - -// --------------------------------------------- -// ---------------- RPC Response --------------- -// --------------------------------------------- - -message PointsOperationResponse { - UpdateResult result = 1; - double time = 2; // Time spent to process -} - -message UpdateResult { - uint64 operation_id = 1; // Number of operation - UpdateStatus status = 2; // Operation status -} - -enum UpdateStatus { - UnknownUpdateStatus = 0; - Acknowledged = 1; // Update is received, but not processed yet - Completed = 2; // Update is applied and ready for search -} - -message ScoredPoint { - PointId id = 1; // Point id - map payload = 2; // Payload - float score = 3; // Similarity score - reserved 4; // deprecated "vector" field - uint64 version = 5; // Last update operation applied to this point - optional Vectors vectors = 6; // Vectors to search -} - -message GroupId { - oneof kind { - // Represents a double value. - uint64 unsigned_value = 1; - // Represents an integer value - int64 integer_value = 2; - // Represents a string value. - string string_value = 3; - } -} - -message PointGroup { - GroupId id = 1; // Group id - repeated ScoredPoint hits = 2; // Points in the group - RetrievedPoint lookup = 3; // Point(s) from the lookup collection that matches the group id -} - -message GroupsResult { - repeated PointGroup groups = 1; // Groups -} - -message SearchResponse { - repeated ScoredPoint result = 1; - double time = 2; // Time spent to process -} - -message BatchResult { - repeated ScoredPoint result = 1; -} - -message SearchBatchResponse { - repeated BatchResult result = 1; - double time = 2; // Time spent to process -} - -message SearchGroupsResponse { - GroupsResult result = 1; - double time = 2; // Time spent to process -} - -message CountResponse { - CountResult result = 1; - double time = 2; // Time spent to process -} - -message ScrollResponse { - optional PointId next_page_offset = 1; // Use this offset for the next query - repeated RetrievedPoint result = 2; - double time = 3; // Time spent to process -} - -message CountResult { - uint64 count = 1; -} - -message RetrievedPoint { - PointId id = 1; - map payload = 2; - reserved 3; // deprecated "vector" field - optional Vectors vectors = 4; -} - -message GetResponse { - repeated RetrievedPoint result = 1; - double time = 2; // Time spent to process -} - -message RecommendResponse { - repeated ScoredPoint result = 1; - double time = 2; // Time spent to process -} - -message RecommendBatchResponse { - repeated BatchResult result = 1; - double time = 2; // Time spent to process -} - -message RecommendGroupsResponse { - GroupsResult result = 1; - double time = 2; // Time spent to process -} - -message UpdateBatchResponse { - repeated UpdateResult result = 1; - double time = 2; // Time spent to process -} - -// --------------------------------------------- -// ------------- Filter Conditions ------------- -// --------------------------------------------- - -message Filter { - repeated Condition should = 1; // At least one of those conditions should match - repeated Condition must = 2; // All conditions must match - repeated Condition must_not = 3; // All conditions must NOT match -} - -message Condition { - oneof condition_one_of { - FieldCondition field = 1; - IsEmptyCondition is_empty = 2; - HasIdCondition has_id = 3; - Filter filter = 4; - IsNullCondition is_null = 5; - NestedCondition nested = 6; - } -} - -message IsEmptyCondition { - string key = 1; -} - -message IsNullCondition { - string key = 1; -} - -message HasIdCondition { - repeated PointId has_id = 1; -} - -message NestedCondition { - string key = 1; // Path to nested object - Filter filter = 2; // Filter condition -} - -message FieldCondition { - string key = 1; - Match match = 2; // Check if point has field with a given value - Range range = 3; // Check if points value lies in a given range - GeoBoundingBox geo_bounding_box = 4; // Check if points geolocation lies in a given area - GeoRadius geo_radius = 5; // Check if geo point is within a given radius - ValuesCount values_count = 6; // Check number of values for a specific field - // GeoPolygon geo_polygon = 7; // Check if geo point is within a given polygon -} - -message Match { - oneof match_value { - string keyword = 1; // Match string keyword - int64 integer = 2; // Match integer - bool boolean = 3; // Match boolean - string text = 4; // Match text - RepeatedStrings keywords = 5; // Match multiple keywords - RepeatedIntegers integers = 6; // Match multiple integers - RepeatedIntegers except_integers = 7; // Match any other value except those integers - RepeatedStrings except_keywords = 8; // Match any other value except those keywords - } -} - -message RepeatedStrings { - repeated string strings = 1; -} - -message RepeatedIntegers { - repeated int64 integers = 1; -} - -message Range { - optional double lt = 1; - optional double gt = 2; - optional double gte = 3; - optional double lte = 4; -} - -message GeoBoundingBox { - GeoPoint top_left = 1; // north-west corner - GeoPoint bottom_right = 2; // south-east corner -} - -message GeoRadius { - GeoPoint center = 1; // Center of the circle - float radius = 2; // In meters -} - -message GeoPolygon { - // Ordered list of coordinates representing the vertices of a polygon. - // The minimum size is 4, and the first coordinate and the last coordinate - // should be the same to form a closed polygon. - repeated GeoPoint points = 1; -} - -message ValuesCount { - optional uint64 lt = 1; - optional uint64 gt = 2; - optional uint64 gte = 3; - optional uint64 lte = 4; -} - -// --------------------------------------------- -// -------------- Points Selector -------------- -// --------------------------------------------- - -message PointsSelector { - oneof points_selector_one_of { - PointsIdsList points = 1; - Filter filter = 2; - } -} - -message PointsIdsList { - repeated PointId ids = 1; -} - -// --------------------------------------------- -// ------------------- Point ------------------- -// --------------------------------------------- - - -message PointStruct { - PointId id = 1; - reserved 2; // deprecated "vector" field - map payload = 3; - optional Vectors vectors = 4; -} - - -message GeoPoint { - double lon = 1; - double lat = 2; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/points_internal_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/points_internal_service.proto deleted file mode 100644 index 5720e74..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/points_internal_service.proto +++ /dev/null @@ -1,118 +0,0 @@ -syntax = "proto3"; - -import "Protos/points.proto"; - -package qdrant; - -import "google/protobuf/struct.proto"; - -service PointsInternal { - rpc Upsert (UpsertPointsInternal) returns (PointsOperationResponse) {} - rpc Sync (SyncPointsInternal) returns (PointsOperationResponse) {} - rpc Delete (DeletePointsInternal) returns (PointsOperationResponse) {} - rpc UpdateVectors (UpdateVectorsInternal) returns (PointsOperationResponse) {} - rpc DeleteVectors (DeleteVectorsInternal) returns (PointsOperationResponse) {} - rpc SetPayload (SetPayloadPointsInternal) returns (PointsOperationResponse) {} - rpc OverwritePayload (SetPayloadPointsInternal) returns (PointsOperationResponse) {} - rpc DeletePayload (DeletePayloadPointsInternal) returns (PointsOperationResponse) {} - rpc ClearPayload (ClearPayloadPointsInternal) returns (PointsOperationResponse) {} - rpc CreateFieldIndex (CreateFieldIndexCollectionInternal) returns (PointsOperationResponse) {} - rpc DeleteFieldIndex (DeleteFieldIndexCollectionInternal) returns (PointsOperationResponse) {} - rpc Search (SearchPointsInternal) returns (SearchResponse) {} - rpc SearchBatch (SearchBatchPointsInternal) returns (SearchBatchResponse) {} - rpc Scroll (ScrollPointsInternal) returns (ScrollResponse) {} - rpc Count (CountPointsInternal) returns (CountResponse) {} - rpc Recommend (RecommendPointsInternal) returns (RecommendResponse) {} - rpc Get (GetPointsInternal) returns (GetResponse) {} -} - - -message SyncPoints { - string collection_name = 1; // name of the collection - optional bool wait = 2; // Wait until the changes have been applied? - repeated PointStruct points = 3; - optional PointId from_id = 4; // Start of the sync range - optional PointId to_id = 5; // End of the sync range - optional WriteOrdering ordering = 6; -} - -message SyncPointsInternal { - SyncPoints sync_points = 1; - optional uint32 shard_id = 2; -} - -message UpsertPointsInternal { - UpsertPoints upsert_points = 1; - optional uint32 shard_id = 2; -} - -message DeletePointsInternal { - DeletePoints delete_points = 1; - optional uint32 shard_id = 2; -} - -message UpdateVectorsInternal { - UpdatePointVectors update_vectors = 1; - optional uint32 shard_id = 2; -} - -message DeleteVectorsInternal { - DeletePointVectors delete_vectors = 1; - optional uint32 shard_id = 2; -} - -message SetPayloadPointsInternal { - SetPayloadPoints set_payload_points = 1; - optional uint32 shard_id = 2; -} - -message DeletePayloadPointsInternal { - DeletePayloadPoints delete_payload_points = 1; - optional uint32 shard_id = 2; -} - -message ClearPayloadPointsInternal { - ClearPayloadPoints clear_payload_points = 1; - optional uint32 shard_id = 2; -} - -message CreateFieldIndexCollectionInternal { - CreateFieldIndexCollection create_field_index_collection = 1; - optional uint32 shard_id = 2; -} - -message DeleteFieldIndexCollectionInternal { - DeleteFieldIndexCollection delete_field_index_collection = 1; - optional uint32 shard_id = 2; -} - -message SearchPointsInternal { - SearchPoints search_points = 1; - optional uint32 shard_id = 2; -} - -message SearchBatchPointsInternal { - string collection_name = 1; - repeated SearchPoints search_points = 2; - optional uint32 shard_id = 3; -} - -message ScrollPointsInternal { - ScrollPoints scroll_points = 1; - optional uint32 shard_id = 2; -} - -message RecommendPointsInternal { - RecommendPoints recommend_points = 1; - optional uint32 shard_id = 2; -} - -message GetPointsInternal { - GetPoints get_points = 1; - optional uint32 shard_id = 2; -} - -message CountPointsInternal { - CountPoints count_points = 1; - optional uint32 shard_id = 2; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/points_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/points_service.proto deleted file mode 100644 index 14ac19f..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/points_service.proto +++ /dev/null @@ -1,91 +0,0 @@ -syntax = "proto3"; - -import "Protos/points.proto"; - -package qdrant; - -import "google/protobuf/struct.proto"; - -service Points { - /* - Perform insert + updates on points. If a point with a given ID already exists - it will be overwritten. - */ - rpc Upsert (UpsertPoints) returns (PointsOperationResponse) {} - /* - Delete points - */ - rpc Delete (DeletePoints) returns (PointsOperationResponse) {} - /* - Retrieve points - */ - rpc Get (GetPoints) returns (GetResponse) {} - /* - Update named vectors for point - */ - rpc UpdateVectors (UpdatePointVectors) returns (PointsOperationResponse) {} - /* - Delete named vectors for points - */ - rpc DeleteVectors (DeletePointVectors) returns (PointsOperationResponse) {} - /* - Set payload for points - */ - rpc SetPayload (SetPayloadPoints) returns (PointsOperationResponse) {} - /* - Overwrite payload for points - */ - rpc OverwritePayload (SetPayloadPoints) returns (PointsOperationResponse) {} - /* - Delete specified key payload for points - */ - rpc DeletePayload (DeletePayloadPoints) returns (PointsOperationResponse) {} - /* - Remove all payload for specified points - */ - rpc ClearPayload (ClearPayloadPoints) returns (PointsOperationResponse) {} - /* - Create index for field in collection - */ - rpc CreateFieldIndex (CreateFieldIndexCollection) returns (PointsOperationResponse) {} - /* - Delete field index for collection - */ - rpc DeleteFieldIndex (DeleteFieldIndexCollection) returns (PointsOperationResponse) {} - /* - Retrieve closest points based on vector similarity and given filtering conditions - */ - rpc Search (SearchPoints) returns (SearchResponse) {} - /* - Retrieve closest points based on vector similarity and given filtering conditions - */ - rpc SearchBatch (SearchBatchPoints) returns (SearchBatchResponse) {} - /* - Retrieve closest points based on vector similarity and given filtering conditions, grouped by a given field - */ - rpc SearchGroups (SearchPointGroups) returns (SearchGroupsResponse) {} - /* - Iterate over all or filtered points points - */ - rpc Scroll (ScrollPoints) returns (ScrollResponse) {} - /* - Look for the points which are closer to stored positive examples and at the same time further to negative examples. - */ - rpc Recommend (RecommendPoints) returns (RecommendResponse) {} - /* - Look for the points which are closer to stored positive examples and at the same time further to negative examples. - */ - rpc RecommendBatch (RecommendBatchPoints) returns (RecommendBatchResponse) {} - /* - Look for the points which are closer to stored positive examples and at the same time further to negative examples, grouped by a given field - */ - rpc RecommendGroups (RecommendPointGroups) returns (RecommendGroupsResponse) {} - /* - Count points in collection with given filtering conditions - */ - rpc Count (CountPoints) returns (CountResponse) {} - - /* - Perform multiple update operations in one request - */ - rpc UpdateBatch (UpdateBatchPoints) returns (UpdateBatchResponse) {} -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant.proto deleted file mode 100644 index 8e5ffd0..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant.proto +++ /dev/null @@ -1,22 +0,0 @@ -syntax = "proto3"; - -import "Protos/collections_service.proto"; -import "Protos/collections_internal_service.proto"; -import "Protos/points_service.proto"; -import "Protos/points_internal_service.proto"; -import "Protos/qdrant_internal_service.proto"; -import "Protos/raft_service.proto"; -import "Protos/snapshots_service.proto"; - -package qdrant; - -service Qdrant { - rpc HealthCheck (HealthCheckRequest) returns (HealthCheckReply) {} -} - -message HealthCheckRequest {} - -message HealthCheckReply { - string title = 1; - string version = 2; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant_internal_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant_internal_service.proto deleted file mode 100644 index e8c1ab7..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/qdrant_internal_service.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package qdrant; - -service QdrantInternal { - /* - Get HTTP port for remote host. - */ - rpc GetHttpPort (HttpPortRequest) returns (HttpPortResponse) {} -} - -message HttpPortRequest {} - -message HttpPortResponse { - int32 port = 1; -} diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/raft_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/raft_service.proto deleted file mode 100644 index b1d6caf..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/raft_service.proto +++ /dev/null @@ -1,51 +0,0 @@ -syntax = "proto3"; - -package qdrant; - -import "google/protobuf/empty.proto"; - -service Raft { - // Send Raft message to another peer - rpc Send (RaftMessage) returns (google.protobuf.Empty); - // Send to bootstrap peer - // Returns uri by id if bootstrap knows this peer - rpc WhoIs (PeerId) returns (Uri); - // Send to bootstrap peer - // Adds peer to the network - // Returns all peers - rpc AddPeerToKnown (AddPeerToKnownMessage) returns (AllPeers); - // DEPRECATED - // Its functionality is now included in `AddPeerToKnown` - // - // Send to bootstrap peer - // Proposes to add this peer as participant of consensus - rpc AddPeerAsParticipant (PeerId) returns (google.protobuf.Empty); -} - -message RaftMessage { - bytes message = 1; -} - -message AllPeers { - repeated Peer all_peers = 1; - uint64 first_peer_id = 2; -} - -message Peer { - string uri = 1; - uint64 id = 2; -} - -message AddPeerToKnownMessage { - optional string uri = 1; - optional uint32 port = 2; - uint64 id = 3; -} - -message PeerId { - uint64 id = 1; -} - -message Uri { - string uri = 1; -} \ No newline at end of file diff --git a/ZeroLevel.Qdrant.GrpcClient/Protos/snapshots_service.proto b/ZeroLevel.Qdrant.GrpcClient/Protos/snapshots_service.proto deleted file mode 100644 index 5770268..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/Protos/snapshots_service.proto +++ /dev/null @@ -1,75 +0,0 @@ -syntax = "proto3"; - -package qdrant; - -import "google/protobuf/struct.proto"; -import "google/protobuf/timestamp.proto"; - -service Snapshots { - /* - Create collection snapshot - */ - rpc Create (CreateSnapshotRequest) returns (CreateSnapshotResponse) {} - /* - List collection snapshots - */ - rpc List (ListSnapshotsRequest) returns (ListSnapshotsResponse) {} - /* - Delete collection snapshots - */ - rpc Delete (DeleteSnapshotRequest) returns (DeleteSnapshotResponse) {} - /* - Create full storage snapshot - */ - rpc CreateFull (CreateFullSnapshotRequest) returns (CreateSnapshotResponse) {} - /* - List full storage snapshots - */ - rpc ListFull (ListFullSnapshotsRequest) returns (ListSnapshotsResponse) {} - /* - List full storage snapshots - */ - rpc DeleteFull (DeleteFullSnapshotRequest) returns (DeleteSnapshotResponse) {} - -} - -message CreateFullSnapshotRequest {} - -message ListFullSnapshotsRequest {} - -message DeleteFullSnapshotRequest { - string snapshot_name = 1; // Name of the full snapshot -} - -message CreateSnapshotRequest { - string collection_name = 1; // Name of the collection -} - -message ListSnapshotsRequest { - string collection_name = 1; // Name of the collection -} - -message DeleteSnapshotRequest { - string collection_name = 1; // Name of the collection - string snapshot_name = 2; // Name of the collection snapshot -} - -message SnapshotDescription { - string name = 1; // Name of the snapshot - google.protobuf.Timestamp creation_time = 2; // Creation time of the snapshot - int64 size = 3; // Size of the snapshot in bytes -} - -message CreateSnapshotResponse { - SnapshotDescription snapshot_description = 1; - double time = 2; // Time spent to process -} - -message ListSnapshotsResponse { - repeated SnapshotDescription snapshot_descriptions = 1; - double time = 2; // Time spent to process -} - -message DeleteSnapshotResponse { - double time = 1; // Time spent to process -} diff --git a/ZeroLevel.Qdrant.GrpcClient/ZeroLevel.Qdrant.GrpcClient.csproj b/ZeroLevel.Qdrant.GrpcClient/ZeroLevel.Qdrant.GrpcClient.csproj deleted file mode 100644 index c1c3b5e..0000000 --- a/ZeroLevel.Qdrant.GrpcClient/ZeroLevel.Qdrant.GrpcClient.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net6.0 - enable - enable - False - 1.15.1 - 1.15.1 - 1.15.1 - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - diff --git a/ZeroLevel.SQL/ZeroLevel.SQL.csproj b/ZeroLevel.SQL/ZeroLevel.SQL.csproj deleted file mode 100644 index c7e2952..0000000 --- a/ZeroLevel.SQL/ZeroLevel.SQL.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net6.0 - AnyCPU;x64;x86 - Light wrapper over ado.net - ogoun - Copyright Ogoun 2020 - https://github.com/ogoun/Zero/tree/master/ZeroLevel.SQL - https://github.com/ogoun/Zero - - GitHub - zero.png - 1.0.2.1 - 1.0.2 - 1.0.2.1 - Move to new ZeroLevelVersion - - - - - - True - - - - - - - - - - - - - - diff --git a/ZeroLevel.SqLite/AuthRepository.cs b/ZeroLevel.SqLite/AuthRepository.cs deleted file mode 100644 index 69534b5..0000000 --- a/ZeroLevel.SqLite/AuthRepository.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using ZeroLevel.Models; - -namespace ZeroLevel.SqLite -{ - public class AuthRepository - { - private static byte[] DEFAULT_ADMIN_PWD_HASH = null; - - private readonly SqLiteUserRepository _userRepository = new SqLiteUserRepository(); - - public UserInfo GetUserInfo(string username, string password) - { - if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) - { - return UserInfo.GetAnonimus(); - } - // Check built-in admin - if (DEFAULT_ADMIN_PWD_HASH != null && DEFAULT_ADMIN_PWD_HASH.Length > 0 && (username.Equals("root", System.StringComparison.Ordinal) || username.Equals("admin", System.StringComparison.Ordinal)) - && DEFAULT_ADMIN_PWD_HASH.SequenceEqual(ComputeHash(password))) - { - return new UserInfo - { - Role = UserRole.SysAdmin, - UserId = -1, - UserName = "sysadmin", - DisplayName = "System Administrator", - Created = DateTime.Now - }; - } - else - { - var user = _userRepository.Get(username, ComputeHash(password)); - if (user != null) - { - return new UserInfo - { - Created = new DateTime(user.Timestamp, DateTimeKind.Utc), - DisplayName = user.DisplayName, - Role = user.Role, - UserId = user.Id, - UserName = user.UserName - }; - } - } - return null; - } - - public InvokeResult CreateUser(string username, string pwd, string displayName, UserRole role, long currentUserId) - { - return _userRepository.SaveUser(new User - { - Creator = currentUserId, - DisplayName = displayName, - PasswordHash = ComputeHash(pwd), - Role = role, - Timestamp = DateTime.UtcNow.Ticks, - UserName = username - }); - } - - public InvokeResult> GetUsers() - { - try - { - return InvokeResult>.Succeeding(_userRepository.GetAll()); - } - catch (Exception ex) - { - return InvokeResult>.Fault>(ex.Message); - } - } - - public InvokeResult RemoveUser(string login) - { - return _userRepository.RemoveUser(login); - } - - public void SetAdminPassword(string rootPwd) => DEFAULT_ADMIN_PWD_HASH = ComputeHash(rootPwd); - - private byte[] ComputeHash(string pwd) - { - using (SHA256 shaM = new SHA256Managed()) - { - return shaM.ComputeHash(Encoding.UTF8.GetBytes(pwd)); - } - } - } -} diff --git a/ZeroLevel.SqLite/BaseSqLiteDB.cs b/ZeroLevel.SqLite/BaseSqLiteDB.cs deleted file mode 100644 index 09fbc4a..0000000 --- a/ZeroLevel.SqLite/BaseSqLiteDB.cs +++ /dev/null @@ -1,118 +0,0 @@ -using SQLite; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq.Expressions; -using ZeroLevel.Services.FileSystem; - -namespace ZeroLevel.SqLite -{ - public abstract class BaseSqLiteDB - : IDisposable - where T : class, new() - { - protected SQLiteConnection _db; - public BaseSqLiteDB(string name) - { - _db = new SQLiteConnection(PrepareDb(name)); - } - - public int Append(T record) - { - return _db.Insert(record); - } - - public CreateTableResult CreateTable() - { - return _db.CreateTable(); - } - - public int DropTable() - { - return _db.DropTable(); - } - - public IEnumerable SelectAll() - { - return _db.Table(); - } - - public IEnumerable SelectBy(Expression> predicate) - { - return _db.Table().Where(predicate); - } - - public T Single(Expression> predicate) - { - return _db.Table().FirstOrDefault(predicate); - } - - public T Single(Expression> predicate, Expression> orderBy, bool desc = false) - { - if (desc) - { - return _db.Table().Where(predicate).OrderByDescending(orderBy).FirstOrDefault(); - } - return _db.Table().Where(predicate).OrderBy(orderBy).FirstOrDefault(); - } - - public T Single(Expression> orderBy, bool desc = false) - { - if (desc) - { - return _db.Table().OrderByDescending(orderBy).FirstOrDefault(); - } - return _db.Table().OrderBy(orderBy).FirstOrDefault(); - } - - public IEnumerable SelectBy(int N, Expression> predicate) - { - return _db.Table().Where(predicate).Take(N); - } - - public long Count() - { - return _db.Table().Count(); - } - - public long Count(Expression> predicate) - { - return _db.Table().Count(predicate); - } - - public int Delete(Expression> predicate) - { - return _db.Table().Delete(predicate); - } - - public int Update(T record) - { - return _db.Update(record); - } - - protected static string PrepareDb(string path) - { - if (Path.IsPathRooted(path) == false) - { - path = Path.Combine(FSUtils.GetAppLocalDbDirectory(), path); - } - return Path.GetFullPath(path); - } - - protected abstract void DisposeStorageData(); - - public void Dispose() - { - DisposeStorageData(); - try - { - _db?.Close(); - _db?.Dispose(); - } - catch (Exception ex) - { - Log.Error(ex, "[BaseSqLiteDB] Fault close db connection"); - } - } - } -} diff --git a/ZeroLevel.SqLite/Model/User.cs b/ZeroLevel.SqLite/Model/User.cs deleted file mode 100644 index fd75e96..0000000 --- a/ZeroLevel.SqLite/Model/User.cs +++ /dev/null @@ -1,18 +0,0 @@ -using SQLite; - -namespace ZeroLevel.SqLite -{ - public class User - { - [PrimaryKey, AutoIncrement] - public long Id { get; set; } - [Indexed] - public string UserName { get; set; } - public string DisplayName { get; set; } - [Indexed] - public byte[] PasswordHash { get; set; } - public long Timestamp { get; set; } - public long Creator { get; set; } - public UserRole Role { get; set; } - } -} diff --git a/ZeroLevel.SqLite/Model/UserInfo.cs b/ZeroLevel.SqLite/Model/UserInfo.cs deleted file mode 100644 index 9299445..0000000 --- a/ZeroLevel.SqLite/Model/UserInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace ZeroLevel.SqLite -{ - public class UserInfo - { - private readonly static UserInfo _anon = new UserInfo - { - Created = DateTime.MinValue, - DisplayName = "Anonimus", - Role = UserRole.Anonimus, - UserId = -2, - UserName = "anonimus" - }; - - public static UserInfo GetAnonimus() => _anon; - - public long UserId { get; set; } - public string UserName { get; set; } - public string DisplayName { get; set; } - public DateTime Created { get; set; } - public UserRole Role { get; set; } - } -} diff --git a/ZeroLevel.SqLite/Model/UserRole.cs b/ZeroLevel.SqLite/Model/UserRole.cs deleted file mode 100644 index a4de25f..0000000 --- a/ZeroLevel.SqLite/Model/UserRole.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ZeroLevel.SqLite -{ - public enum UserRole - : Int32 - { - Anonimus = 0, - Operator = 1, - Editor = 512, - Administrator = 1024, - SysAdmin = 4096 - } -} diff --git a/ZeroLevel.SqLite/Properties/PublishProfiles/FolderProfile.pubxml b/ZeroLevel.SqLite/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 3051601..0000000 --- a/ZeroLevel.SqLite/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Release - x64 - bin\Release\net6.0\publish\ - FileSystem - net6.0 - false - - \ No newline at end of file diff --git a/ZeroLevel.SqLite/SqLiteDupStorage.cs b/ZeroLevel.SqLite/SqLiteDupStorage.cs deleted file mode 100644 index 117754b..0000000 --- a/ZeroLevel.SqLite/SqLiteDupStorage.cs +++ /dev/null @@ -1,155 +0,0 @@ -using SQLite; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading; - -namespace ZeroLevel.SqLite -{ - public sealed class DuplicateRecord - { - [PrimaryKey, AutoIncrement] - public long Id { get; set; } - [Indexed] - public string Hash { get; set; } - public long Timestamp { get; set; } - public byte[] Data { get; set; } - } - - /// - /// Хранит данные указанное число дней, и позволяет выполнить проверку наличия данных, для отбрасывания дубликатов - /// - public sealed class SqLiteDupStorage - : BaseSqLiteDB - { - #region Fields - - private readonly long _removeOldRecordsTaskKey; - private readonly int _countDays; - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - #endregion Fields - - #region Private members - private void RemoveOldRecordsTask(long key) - { - _rwLock.EnterWriteLock(); - try - { - Delete(r => r.Timestamp < DateTime.Now.AddDays(-_countDays).Ticks); - } - catch (Exception ex) - { - Log.Error(ex, "[SQLiteDupStorage] Fault remove old records from db"); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - #endregion Private members - - #region Ctor - - public SqLiteDupStorage(string database_file_path, int period) - : base(database_file_path) - { - CreateTable(); - _countDays = period > 0 ? period : 1; - _removeOldRecordsTaskKey = Sheduller.RemindEvery(TimeSpan.FromMinutes(1), RemoveOldRecordsTask); - } - - #endregion Ctor - - #region API - - /// - /// true в случае обнаружения дубликата - /// - public bool TestAndInsert(byte[] body) - { - var hash = GenerateSHA256String(body); - var timestamp = DateTime.Now.Ticks; - _rwLock.EnterReadLock(); - var exists = new List(); - try - { - foreach (var record in SelectBy(r => r.Hash == hash)) - { - exists.Add(record.Data); - } - } - catch (Exception ex) - { - Log.Error(ex, $"[SQLiteDupStorage] Fault search existing records by hash ({hash})"); - // no return! - } - finally - { - _rwLock.ExitReadLock(); - } - if (exists.Any()) - { - foreach (var candidate in exists) - { - if (ArrayExtensions.UnsafeEquals(candidate, body)) - return true; - } - } - _rwLock.EnterWriteLock(); - try - { - Append(new DuplicateRecord - { - Data = body, - Hash = hash, - Timestamp = timestamp - }); - } - catch (Exception ex) - { - Log.Error(ex, $"[SQLiteDupStorage] Fault insert record in duplications storage. Hash '{hash}'. Timestamp '{timestamp}'."); - } - finally - { - _rwLock.ExitWriteLock(); - } - return false; - } - - #endregion API - - #region IDisposable - - protected override void DisposeStorageData() - { - Sheduller.Remove(_removeOldRecordsTaskKey); - } - - #endregion IDisposable - - #region Helpers - - private static string GenerateSHA256String(byte[] bytes) - { - using (SHA256 sha256 = SHA256Managed.Create()) - { - byte[] hash = sha256.ComputeHash(bytes); - return ByteArrayToString(hash); - } - } - - private static string ByteArrayToString(byte[] ba) - { - StringBuilder hex = new StringBuilder(ba.Length * 2); - foreach (byte b in ba) - hex.AppendFormat("{0:x2}", b); - return hex.ToString(); - } - - #endregion Helpers - } -} diff --git a/ZeroLevel.SqLite/SqLitePacketBuffer.cs b/ZeroLevel.SqLite/SqLitePacketBuffer.cs deleted file mode 100644 index 0158fdc..0000000 --- a/ZeroLevel.SqLite/SqLitePacketBuffer.cs +++ /dev/null @@ -1,120 +0,0 @@ -using SQLite; -using System; -using System.Threading; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.SqLite -{ - - public sealed class PacketRecord - { - [PrimaryKey, AutoIncrement] - public long Id { get; set; } - [Indexed] - public long Timestamp { get; set; } - public byte[] Data { get; set; } - } - - /// - /// Промежуточное/временное хранилище пакетов данных, для случаев сбоя доставок через шину данных - /// - public sealed class SqLitePacketBuffer - : BaseSqLiteDB - where T : IBinarySerializable - { - private sealed class PacketBufferRecord - { - public int Id { get; set; } - public byte[] Body { get; set; } - } - - #region Fields - - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - #endregion Fields - - public SqLitePacketBuffer(string database_file_path) - : base(database_file_path) - { - CreateTable(); - } - - public void Push(T frame) - { - _rwLock.EnterWriteLock(); - var creationTime = DateTime.Now.Ticks; - try - { - Append(new PacketRecord - { - Data = MessageSerializer.Serialize(frame), - Timestamp = creationTime - }); - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLitePacketBuffer] Fault insert record in buffer storage."); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - public bool Pop(Func pop_callback) - { - bool success = false; - long id = -1; - _rwLock.EnterReadLock(); - try - { - var record = Single(r => r.Timestamp); - id = record.Id; - var body = record.Data; - try - { - success = pop_callback(MessageSerializer.Deserialize(body)); - } - catch (Exception ex) - { - Log.Error(ex, "Fault handle buffered data"); - } - } - catch (Exception ex) - { - Log.Error(ex, "[SqLitePacketBuffer] Fault preload datafrom db"); - } - finally - { - _rwLock.ExitReadLock(); - } - if (success) - { - RemoveRecordById(id); - } - return success; - } - - private void RemoveRecordById(long id) - { - _rwLock.EnterWriteLock(); - try - { - Delete(r => r.Id == id); - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLitePacketBuffer] Fault remove record by id '{id}'"); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - protected override void DisposeStorageData() - { - } - } -} diff --git a/ZeroLevel.SqLite/SqLiteUserRepository.cs b/ZeroLevel.SqLite/SqLiteUserRepository.cs deleted file mode 100644 index d39a706..0000000 --- a/ZeroLevel.SqLite/SqLiteUserRepository.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using ZeroLevel.Models; - -namespace ZeroLevel.SqLite -{ - public class SqLiteUserRepository - : BaseSqLiteDB - { - #region Fields - - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - #endregion Fields - - #region Ctor - - public SqLiteUserRepository() - : base("users.db") - { - } - - #endregion Ctor - - public IEnumerable GetAll() - { - var list = new List(); - _rwLock.EnterReadLock(); - try - { - foreach (var r in SelectAll()) - { - list.Add(r); - } - } - catch (Exception ex) - { - Log.Error(ex, "[SqLiteUserRepository] Fault get all users"); - } - finally - { - _rwLock.ExitReadLock(); - } - return list; - } - - public User Get(long id) - { - User user = null; - _rwLock.EnterReadLock(); - try - { - user = Single(r => r.Id == id); - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLiteUserRepository] Fault get user by id '{id}'"); - } - finally - { - _rwLock.ExitReadLock(); - } - return user; - } - - public User Get(string username, byte[] hash) - { - User user = null; - _rwLock.EnterReadLock(); - try - { - user = Single(r => r.UserName == username && r.PasswordHash == hash); - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLiteUserRepository] Fault get user by username '{username}' and pwdhash"); - } - finally - { - _rwLock.ExitReadLock(); - } - return user; - } - - public InvokeResult SaveUser(User user) - { - long id = -1; - _rwLock.EnterWriteLock(); - var creationTime = DateTime.UtcNow.Ticks; - try - { - var count_obj = Count(r => r.UserName == user.UserName); - if (count_obj > 0) - { - return InvokeResult.Fault("Пользователь с таким именем уже существует"); - } - id = Append(user).Id; - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLiteUserRepository] Fault insert user in storage."); - InvokeResult.Fault(ex.Message); - } - finally - { - _rwLock.ExitWriteLock(); - } - return InvokeResult.Succeeding(id); - } - - public InvokeResult RemoveUser(string login) - { - _rwLock.EnterWriteLock(); - try - { - Delete(r => r.UserName == login); - return InvokeResult.Succeeding(); - } - catch (Exception ex) - { - Log.Error(ex, $"[SqLiteUserRepository] Fault remove user '{login}'"); - return InvokeResult.Fault(ex.Message); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - protected override void DisposeStorageData() - { - } - } -} diff --git a/ZeroLevel.SqLite/UserCacheRepository.cs b/ZeroLevel.SqLite/UserCacheRepository.cs deleted file mode 100644 index 569bc90..0000000 --- a/ZeroLevel.SqLite/UserCacheRepository.cs +++ /dev/null @@ -1,170 +0,0 @@ -using SQLite; -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using ZeroLevel.Services.Serialization; - -namespace ZeroLevel.SqLite -{ - public sealed class DataRecord - { - [PrimaryKey, AutoIncrement] - public long Id { get; set; } - [Indexed] - public string Key { get; set; } - public byte[] Data { get; set; } - } - public sealed class UserCacheRepository - : BaseSqLiteDB - where T : IBinarySerializable - { - #region Fields - - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - #endregion Fields - - #region Ctor - - public UserCacheRepository() - : base(typeof(T).Name) - { - CreateTable(); - } - - #endregion Ctor - - #region API - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private string KEY(long userId, string name) => $"{userId}.{name}"; - - public bool AddOrUpdate(long userid, string name, T data) - { - var key = KEY(userid, name); - bool update = false; - _rwLock.EnterReadLock(); - try - { - var count_obj = Count(r => r.Key == key); - if (count_obj > 0) - { - update = true; - } - } - catch (Exception ex) - { - Log.Error(ex, $"[UserCacheRepository] Fault search existing records by name ({name})"); - // no return! - } - finally - { - _rwLock.ExitReadLock(); - } - _rwLock.EnterWriteLock(); - try - { - var body = MessageSerializer.Serialize(data); - if (update) - { - var r = Single(r => r.Key == key); - r.Data = body; - Update(r); - } - else - { - Append(new DataRecord - { - Data = body, - Key = key - }); - } - return true; - } - catch (Exception ex) - { - Log.Error(ex, $"[UserCacheRepository] Fault insert record in storage. UserId: {userid}. Name '{name}'. Data: {typeof(T).Name}."); - } - finally - { - _rwLock.ExitWriteLock(); - } - return false; - } - - public void Remove(long userid, string name) - { - var key = KEY(userid, name); - _rwLock.EnterWriteLock(); - try - { - Delete(r => r.Key == key); - } - catch (Exception ex) - { - Log.Error(ex, $"[UserCacheRepository] Fault remove record from db by name '{name}'. UserId: {userid}. Data: {typeof(T).Name}."); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - - public long Count(long userid, string name) - { - var key = KEY(userid, name); - _rwLock.EnterWriteLock(); - try - { - return Count(r => r.Key == key); - } - catch (Exception ex) - { - Log.Error(ex, $"[UserCacheRepository] Fault get count record from db by name '{name}'. UserId: {userid}. Data: {typeof(T).Name}."); - } - finally - { - _rwLock.ExitWriteLock(); - } - return 0; - } - - public IEnumerable GetAll(long userid, string name) - { - var key = KEY(userid, name); - var result = new List(); - _rwLock.EnterReadLock(); - try - { - foreach (var r in SelectBy(r=>r.Key == key)) - { - var data = r.Data; - if (data != null) - { - result.Add(MessageSerializer.Deserialize(data)); - } - } - } - catch (Exception ex) - { - Log.Error(ex, $"[UserCacheRepository] Fault read all records by name: {name}. UserId: {userid}. Data: {typeof(T).Name}."); - } - finally - { - _rwLock.ExitReadLock(); - } - return result; - } - - #endregion API - - #region IDisposable - - protected override void DisposeStorageData() - { - } - - #endregion IDisposable - } -} diff --git a/ZeroLevel.SqLite/ZeroLevel.SqLite.csproj b/ZeroLevel.SqLite/ZeroLevel.SqLite.csproj deleted file mode 100644 index 3f601f3..0000000 --- a/ZeroLevel.SqLite/ZeroLevel.SqLite.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net6.0 - Ogoun - Helpers for sqlite databases. -Based on System.Data.SQLite.Core - Copyright Ogoun 2020 - https://github.com/ogoun/Zero/tree/master/ZeroLevel.SqLite - GitHub - https://github.com/ogoun/Zero - zero.png - - AnyCPU;x64;x86 - Move to new ZeroLevel version - 1.0.4.0 - 1.0.4.0 - 1.0.4.0 - Library - - - - - - True - - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/ZeroLevel.SqLite/sqlite3.dll b/ZeroLevel.SqLite/sqlite3.dll deleted file mode 100644 index 70a8f7f..0000000 Binary files a/ZeroLevel.SqLite/sqlite3.dll and /dev/null differ diff --git a/ZeroLevel.sln b/ZeroLevel.sln index 012b133..96652e9 100644 --- a/ZeroLevel.sln +++ b/ZeroLevel.sln @@ -3,16 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel", "ZeroLevel\ZeroLevel.csproj", "{06C9E60E-D449-41A7-9BF0-A829AAF5D214}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.Discovery", "ZeroLevel.Discovery\ZeroLevel.Discovery.csproj", "{5CE51CC9-7884-4E21-9D68-2321CA14312E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FileTransferTest", "FileTransferTest", "{FC074553-5D9F-4DF1-9130-7092E37DE768}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestPipeLine", "TestPipeLine", "{03ACF314-93FC-46FE-9FB8-3F46A01A5A15}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.SQL", "ZeroLevel.SQL\ZeroLevel.SQL.csproj", "{D25EC1F0-3BD2-409C-8A01-8C8339D5835C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.Logger", "ZeroLevel.Logger\ZeroLevel.Logger.csproj", "{D1C061DB-3565-43C3-B8F3-628DE4908750}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPFExamples", "WPFExamples", "{7CCA0125-7A96-48FA-9F0D-BCF7EAD8FF9D}" @@ -31,12 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WPFExamples", "WPFExamples" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConnectionTest", "ConnectionTest", "{D5207A5A-2F27-4992-9BA5-0BDCFC59F133}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.HNSW", "ZeroLevel.HNSW\ZeroLevel.HNSW.csproj", "{1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroNetworkMonitor", "ZeroNetworkMonitor\ZeroNetworkMonitor.csproj", "{B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.NN", "ZeroLevel.NN\ZeroLevel.NN.csproj", "{C67E5F2E-B62E-441D-99F5-8ECA6CECE804}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{380C828E-55D0-488B-A95F-4EFD20597BF5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Consumer", "Tests\TestPipeLine\Consumer\Consumer.csproj", "{6C87E9EF-423F-47FC-BCFC-1A8E51336F02}" @@ -51,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTransferClient", "Tests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTransferServer", "Tests\FileTransferTest\FileTransferServer\FileTransferServer.csproj", "{EA61441D-3E95-40AD-88B8-B87FC09368BA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HNSWDemo", "Tests\HNSWDemo\HNSWDemo.csproj", "{EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Tests\ConnectionTest\Client\Client.csproj", "{78668262-996C-4181-A0B7-C4C9C6E228CA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Tests\ConnectionTest\Server\Server.csproj", "{D8BA6763-B7DD-4D38-903C-73A985F13974}" @@ -63,9 +53,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "Tests\TestApp\Te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.UnitTests", "Tests\ZeroLevel.UnitTests\ZeroLevel.UnitTests.csproj", "{F73E2055-376A-4DC4-996E-E99009EC62D6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qdrant.Test", "Tests\Qdrant.Test\Qdrant.Test.csproj", "{F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel", "ZeroLevel\ZeroLevel.csproj", "{301D5818-59C5-4B46-9F80-A1054CC6CAA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.MsSql", "ZeroLevel.MsSql\ZeroLevel.MsSql.csproj", "{3F9E6132-926C-46DE-B202-2355C126FD67}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.Qdrant.GrpcClient", "ZeroLevel.Qdrant.GrpcClient\ZeroLevel.Qdrant.GrpcClient.csproj", "{C5AB3481-2421-4503-9B7A-F5028B0D16F8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.ML", "ZeroLevel.ML\ZeroLevel.ML.csproj", "{9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -77,18 +69,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|Any CPU.Build.0 = Debug|Any CPU - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|x64.ActiveCfg = Debug|x64 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|x64.Build.0 = Debug|x64 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|x86.ActiveCfg = Debug|x86 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Debug|x86.Build.0 = Debug|x86 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|Any CPU.ActiveCfg = Release|Any CPU - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|Any CPU.Build.0 = Release|Any CPU - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|x64.ActiveCfg = Release|x64 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|x64.Build.0 = Release|x64 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|x86.ActiveCfg = Release|x86 - {06C9E60E-D449-41A7-9BF0-A829AAF5D214}.Release|x86.Build.0 = Release|x86 {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Debug|Any CPU.Build.0 = Debug|Any CPU {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Debug|x64.ActiveCfg = Debug|x64 @@ -101,18 +81,6 @@ Global {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Release|x64.Build.0 = Release|x64 {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Release|x86.ActiveCfg = Release|x86 {5CE51CC9-7884-4E21-9D68-2321CA14312E}.Release|x86.Build.0 = Release|x86 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|x64.ActiveCfg = Debug|x64 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|x64.Build.0 = Debug|x64 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|x86.ActiveCfg = Debug|x86 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Debug|x86.Build.0 = Debug|x86 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|Any CPU.Build.0 = Release|Any CPU - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x64.ActiveCfg = Release|x64 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x64.Build.0 = Release|x64 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x86.ActiveCfg = Release|x86 - {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x86.Build.0 = Release|x86 {D1C061DB-3565-43C3-B8F3-628DE4908750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1C061DB-3565-43C3-B8F3-628DE4908750}.Debug|Any CPU.Build.0 = Debug|Any CPU {D1C061DB-3565-43C3-B8F3-628DE4908750}.Debug|x64.ActiveCfg = Debug|x64 @@ -125,18 +93,6 @@ Global {D1C061DB-3565-43C3-B8F3-628DE4908750}.Release|x64.Build.0 = Release|x64 {D1C061DB-3565-43C3-B8F3-628DE4908750}.Release|x86.ActiveCfg = Release|x86 {D1C061DB-3565-43C3-B8F3-628DE4908750}.Release|x86.Build.0 = Release|x86 - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|x64.ActiveCfg = Debug|x64 - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|x64.Build.0 = Debug|x64 - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|x86.ActiveCfg = Debug|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Debug|x86.Build.0 = Debug|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|Any CPU.Build.0 = Release|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|x64.ActiveCfg = Release|x64 - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|x64.Build.0 = Release|x64 - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|x86.ActiveCfg = Release|Any CPU - {1EAC0A2C-B00F-4353-94D3-3BB4DC5C92AE}.Release|x86.Build.0 = Release|Any CPU {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Debug|x64.ActiveCfg = Debug|x64 @@ -149,18 +105,6 @@ Global {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Release|x64.Build.0 = Release|x64 {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Release|x86.ActiveCfg = Release|Any CPU {B89249F8-BD37-4AF7-9BB5-65855FA3B3FA}.Release|x86.Build.0 = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|x64.ActiveCfg = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|x64.Build.0 = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|x86.ActiveCfg = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Debug|x86.Build.0 = Debug|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|Any CPU.Build.0 = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|x64.ActiveCfg = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|x64.Build.0 = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|x86.ActiveCfg = Release|Any CPU - {C67E5F2E-B62E-441D-99F5-8ECA6CECE804}.Release|x86.Build.0 = Release|Any CPU {6C87E9EF-423F-47FC-BCFC-1A8E51336F02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C87E9EF-423F-47FC-BCFC-1A8E51336F02}.Debug|Any CPU.Build.0 = Debug|Any CPU {6C87E9EF-423F-47FC-BCFC-1A8E51336F02}.Debug|x64.ActiveCfg = Debug|x64 @@ -233,18 +177,6 @@ Global {EA61441D-3E95-40AD-88B8-B87FC09368BA}.Release|x64.Build.0 = Release|x64 {EA61441D-3E95-40AD-88B8-B87FC09368BA}.Release|x86.ActiveCfg = Release|Any CPU {EA61441D-3E95-40AD-88B8-B87FC09368BA}.Release|x86.Build.0 = Release|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|x64.ActiveCfg = Debug|x64 - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|x64.Build.0 = Debug|x64 - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|x86.ActiveCfg = Debug|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Debug|x86.Build.0 = Debug|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|Any CPU.Build.0 = Release|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|x64.ActiveCfg = Release|x64 - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|x64.Build.0 = Release|x64 - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|x86.ActiveCfg = Release|Any CPU - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196}.Release|x86.Build.0 = Release|Any CPU {78668262-996C-4181-A0B7-C4C9C6E228CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {78668262-996C-4181-A0B7-C4C9C6E228CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {78668262-996C-4181-A0B7-C4C9C6E228CA}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -305,30 +237,42 @@ Global {F73E2055-376A-4DC4-996E-E99009EC62D6}.Release|x64.Build.0 = Release|x64 {F73E2055-376A-4DC4-996E-E99009EC62D6}.Release|x86.ActiveCfg = Release|x86 {F73E2055-376A-4DC4-996E-E99009EC62D6}.Release|x86.Build.0 = Release|x86 - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|x64.ActiveCfg = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|x64.Build.0 = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|x86.ActiveCfg = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Debug|x86.Build.0 = Debug|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|Any CPU.Build.0 = Release|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|x64.ActiveCfg = Release|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|x64.Build.0 = Release|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|x86.ActiveCfg = Release|Any CPU - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30}.Release|x86.Build.0 = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|x64.ActiveCfg = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|x64.Build.0 = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|x86.ActiveCfg = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Debug|x86.Build.0 = Debug|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|Any CPU.Build.0 = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|x64.ActiveCfg = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|x64.Build.0 = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|x86.ActiveCfg = Release|Any CPU - {C5AB3481-2421-4503-9B7A-F5028B0D16F8}.Release|x86.Build.0 = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|x64.ActiveCfg = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|x64.Build.0 = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|x86.ActiveCfg = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Debug|x86.Build.0 = Debug|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|Any CPU.Build.0 = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|x64.ActiveCfg = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|x64.Build.0 = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|x86.ActiveCfg = Release|Any CPU + {301D5818-59C5-4B46-9F80-A1054CC6CAA3}.Release|x86.Build.0 = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|x64.Build.0 = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Debug|x86.Build.0 = Debug|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|Any CPU.Build.0 = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|x64.ActiveCfg = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|x64.Build.0 = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|x86.ActiveCfg = Release|Any CPU + {3F9E6132-926C-46DE-B202-2355C126FD67}.Release|x86.Build.0 = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|x64.Build.0 = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|x86.ActiveCfg = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Debug|x86.Build.0 = Debug|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|Any CPU.Build.0 = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|x64.ActiveCfg = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|x64.Build.0 = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|x86.ActiveCfg = Release|Any CPU + {9F6690E5-D35D-4FA7-BDF0-FE320C4878E0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -343,13 +287,11 @@ Global {4C2A50C3-1CBD-488E-9B7B-FB380086D8FD} = {03ACF314-93FC-46FE-9FB8-3F46A01A5A15} {B2ADCA70-7A83-4035-9C75-19A9FD452E34} = {FC074553-5D9F-4DF1-9130-7092E37DE768} {EA61441D-3E95-40AD-88B8-B87FC09368BA} = {FC074553-5D9F-4DF1-9130-7092E37DE768} - {EE3C3DFE-4D4C-4E94-9D12-2FE752ACC196} = {380C828E-55D0-488B-A95F-4EFD20597BF5} {78668262-996C-4181-A0B7-C4C9C6E228CA} = {D5207A5A-2F27-4992-9BA5-0BDCFC59F133} {D8BA6763-B7DD-4D38-903C-73A985F13974} = {D5207A5A-2F27-4992-9BA5-0BDCFC59F133} {9525DFC1-91F4-4B38-BDD0-7C46507C636F} = {380C828E-55D0-488B-A95F-4EFD20597BF5} {3C6C5D02-93B7-4842-97C0-870B4B6AF5F5} = {380C828E-55D0-488B-A95F-4EFD20597BF5} {F73E2055-376A-4DC4-996E-E99009EC62D6} = {380C828E-55D0-488B-A95F-4EFD20597BF5} - {F7F16D4D-2B0F-4332-B2D1-872B4B6D9B30} = {380C828E-55D0-488B-A95F-4EFD20597BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A65DB16F-877D-4586-A9F3-8BBBFBAF5CEB} diff --git a/ZeroLevel/IsExternalInitFix.cs b/ZeroLevel/IsExternalInitFix.cs new file mode 100644 index 0000000..96e5972 --- /dev/null +++ b/ZeroLevel/IsExternalInitFix.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// This dummy class is required to compile records when targeting .NET Standard + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class IsExternalInit + { + } +} \ No newline at end of file diff --git a/ZeroLevel/Properties/PublishProfiles/FolderProfile.pubxml b/ZeroLevel/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 495395e..0000000 --- a/ZeroLevel/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Release - x64 - bin\Release\net6.0\publish\win-x64\ - FileSystem - net6.0 - false - - \ No newline at end of file diff --git a/ZeroLevel/Services/Extensions/LinqExtension.cs b/ZeroLevel/Services/Extensions/LinqExtension.cs index 704e08c..d32445f 100644 --- a/ZeroLevel/Services/Extensions/LinqExtension.cs +++ b/ZeroLevel/Services/Extensions/LinqExtension.cs @@ -47,8 +47,8 @@ namespace System.Linq } } - /*public static IEnumerable DistinctBy - (this IEnumerable source, Func keySelector) + public static IEnumerable DistinctBy + (this IEnumerable source, Func keySelector) { if (source != null) { @@ -61,7 +61,7 @@ namespace System.Linq } } } - }*/ + } public static IList Materialize(this IEnumerable source) { diff --git a/ZeroLevel/Services/Extensions/TypeExtensions.cs b/ZeroLevel/Services/Extensions/TypeExtensions.cs index c51da65..0e5050b 100644 --- a/ZeroLevel/Services/Extensions/TypeExtensions.cs +++ b/ZeroLevel/Services/Extensions/TypeExtensions.cs @@ -55,5 +55,11 @@ namespace ZeroLevel return IsAssignableToGenericType(baseType, genericType); } + + public static bool IsAssignableTo(this Type type, Type assignableType) + => assignableType.IsAssignableFrom(type); + + public static bool IsAssignableTo(this Type type) + => typeof(T).IsAssignableFrom(type); } } \ No newline at end of file diff --git a/ZeroLevel/Services/Network/Utils/Networks/Network.cs b/ZeroLevel/Services/Network/Utils/Networks/Network.cs index 7b19ba7..56a95e2 100644 --- a/ZeroLevel/Services/Network/Utils/Networks/Network.cs +++ b/ZeroLevel/Services/Network/Utils/Networks/Network.cs @@ -28,7 +28,7 @@ namespace ZeroLevel.Services.Network.Utils using (var client = new HttpClient(clientHandler)) { var message = new HttpRequestMessage(HttpMethod.Get, "http://ipv4.icanhazip.com"); - using (var response = client.Send(message)) + using (var response = client.SendAsync(message).Result) { return response.Content.ReadAsStringAsync().Result; } diff --git a/ZeroLevel/Services/PartitionStorage/Indexes/IndexBuilder.cs b/ZeroLevel/Services/PartitionStorage/Indexes/IndexBuilder.cs index 141dce2..b2cac16 100644 --- a/ZeroLevel/Services/PartitionStorage/Indexes/IndexBuilder.cs +++ b/ZeroLevel/Services/PartitionStorage/Indexes/IndexBuilder.cs @@ -104,7 +104,7 @@ namespace ZeroLevel.Services.PartitionStorage } if (dict.Count > _stepValue) { - var step = (int)Math.Round(dict.Count / (float)_stepValue, MidpointRounding.ToZero); + var step = (int)Math.Truncate(dict.Count / (float)_stepValue); var index_file = Path.Combine(_indexCatalog, Path.GetFileName(file)); _phisicalFileAccessorCachee.LockFile(index_file); diff --git a/ZeroLevel/Services/PartitionStorage/Interfaces/IStorePartitionBuilder.cs b/ZeroLevel/Services/PartitionStorage/Interfaces/IStorePartitionBuilder.cs index 2362f3e..709f4e3 100644 --- a/ZeroLevel/Services/PartitionStorage/Interfaces/IStorePartitionBuilder.cs +++ b/ZeroLevel/Services/PartitionStorage/Interfaces/IStorePartitionBuilder.cs @@ -28,7 +28,7 @@ namespace ZeroLevel.Services.PartitionStorage /// /// Performs compression/grouping of recorded data in a partition /// - Task Compress(); + void Compress(); /// /// Rebuilds indexes for data in a partition /// diff --git a/ZeroLevel/Services/PartitionStorage/KV.cs b/ZeroLevel/Services/PartitionStorage/KV.cs index 8a0d619..6c5cef9 100644 --- a/ZeroLevel/Services/PartitionStorage/KV.cs +++ b/ZeroLevel/Services/PartitionStorage/KV.cs @@ -1,6 +1,5 @@ namespace ZeroLevel.Services.PartitionStorage { public record KV(TKey Key, TValue Value); - public record KVM(TKey Key, TValue Value, TMeta Meta); } diff --git a/ZeroLevel/Services/PartitionStorage/Partition/CompactKeyStorePartitionBuilder.cs b/ZeroLevel/Services/PartitionStorage/Partition/CompactKeyStorePartitionBuilder.cs index 19410d3..c8166b0 100644 --- a/ZeroLevel/Services/PartitionStorage/Partition/CompactKeyStorePartitionBuilder.cs +++ b/ZeroLevel/Services/PartitionStorage/Partition/CompactKeyStorePartitionBuilder.cs @@ -50,12 +50,12 @@ namespace ZeroLevel.Services.PartitionStorage.Partition CloseWriteStreams(); } - public async Task Compress() + public void Compress() { var files = Directory.GetFiles(_catalog); if (files != null && files.Length > 0) { - await Parallel.ForEachAsync(files, async (file, ct) => await CompressFile(file)); + Parallel.ForEach(files, async (file, ct) => await CompressFile(file)); } } public async IAsyncEnumerable> Iterate() @@ -151,7 +151,7 @@ namespace ZeroLevel.Services.PartitionStorage.Partition } } File.Delete(file); - File.Move(tempFile, file, true); + File.Move(tempFile, file); } finally { diff --git a/ZeroLevel/Services/PartitionStorage/Partition/StoreMergePartitionAccessor.cs b/ZeroLevel/Services/PartitionStorage/Partition/StoreMergePartitionAccessor.cs index a39769b..7ddd452 100644 --- a/ZeroLevel/Services/PartitionStorage/Partition/StoreMergePartitionAccessor.cs +++ b/ZeroLevel/Services/PartitionStorage/Partition/StoreMergePartitionAccessor.cs @@ -124,7 +124,7 @@ namespace ZeroLevel.Services.PartitionStorage { File.Delete(updateFilePath); } - File.Move(file, updateFilePath, true); + File.Move(file, updateFilePath); } finally { diff --git a/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionAccessor.cs b/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionAccessor.cs index 0b3a251..e222224 100644 --- a/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionAccessor.cs +++ b/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionAccessor.cs @@ -469,7 +469,7 @@ namespace ZeroLevel.Services.PartitionStorage // 3. Replace from temporary to original PhisicalFileAccessorCachee.DropDataReader(filePath); File.Delete(filePath); - File.Move(tempFile, filePath, true); + File.Move(tempFile, filePath); // Rebuild index if needs if (_options.Index.Enabled && autoReindex) diff --git a/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionBuilder.cs b/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionBuilder.cs index 32c8f49..d64bbd1 100644 --- a/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionBuilder.cs +++ b/ZeroLevel/Services/PartitionStorage/Partition/StorePartitionBuilder.cs @@ -53,12 +53,28 @@ namespace ZeroLevel.Services.PartitionStorage CloseWriteStreams(); } - public async Task Compress() + public void Compress() { var files = Directory.GetFiles(_catalog); if (files != null && files.Length > 0) { - await Parallel.ForEachAsync(files, async(file, _) => await CompressFile(file)); + var tasks = new Task[files.Length]; + for (int i = 0; i < files.Length; i++) + { + tasks[i] = Task.Factory.StartNew(f => + { + var file = (string)f; + try + { + CompressFile(file).Wait(); + } + catch (Exception ex) + { + Log.Error(ex, $"[StorePartitionBuilder.Compress] '{file}'"); + } + }, files[i]); + } + Task.WaitAll(tasks); } } public async IAsyncEnumerable> Iterate() @@ -108,7 +124,7 @@ namespace ZeroLevel.Services.PartitionStorage { Log.SystemError(ex, $"[StoreDirect] Fault use writeStream for key '{groupKey}'"); return false; - } + } } private async Task StoreDirectSafe(TKey key, TInput value) { @@ -122,7 +138,7 @@ namespace ZeroLevel.Services.PartitionStorage }); return true; } - catch(Exception ex) + catch (Exception ex) { Log.SystemError(ex, $"[StoreDirectSafe] Fault use writeStream for key '{groupKey}'"); return false; @@ -184,8 +200,12 @@ namespace ZeroLevel.Services.PartitionStorage await Serializer.ValueSerializer.Invoke(writer, v); } } - File.Delete(file); - File.Move(tempFile, file, true); + if (File.Exists(file)) + { + File.Delete(file); + } + File.Copy(tempFile, file, true); + File.Delete(tempFile); } finally { diff --git a/ZeroLevel/Services/Serialization/IBinaryReader.cs b/ZeroLevel/Services/Serialization/IBinaryReader.cs index 68f7e44..fe5d4ff 100644 --- a/ZeroLevel/Services/Serialization/IBinaryReader.cs +++ b/ZeroLevel/Services/Serialization/IBinaryReader.cs @@ -38,10 +38,6 @@ namespace ZeroLevel.Services.Serialization DateTime? ReadDateTime(); - TimeOnly? ReadTime(); - - DateOnly? ReadDate(); - decimal ReadDecimal(); TimeSpan ReadTimeSpan(); @@ -154,8 +150,6 @@ namespace ZeroLevel.Services.Serialization Task ReadStringAsync(); Task ReadGuidAsync(); Task ReadDateTimeAsync(); - Task ReadTimeAsync(); - Task ReadDateAsync(); Task ReadDecimalAsync(); Task ReadTimeSpanAsync(); Task ReadIPAsync(); diff --git a/ZeroLevel/Services/Serialization/IBinaryWriter.cs b/ZeroLevel/Services/Serialization/IBinaryWriter.cs index 44a5ace..4f6ae9a 100644 --- a/ZeroLevel/Services/Serialization/IBinaryWriter.cs +++ b/ZeroLevel/Services/Serialization/IBinaryWriter.cs @@ -40,10 +40,6 @@ namespace ZeroLevel.Services.Serialization void WriteDateTime(DateTime? datetime); - void WriteTime(TimeOnly? time); - - void WriteDate(DateOnly? date); - void WriteDecimal(Decimal number); void WriteTimeSpan(TimeSpan period); @@ -145,10 +141,6 @@ namespace ZeroLevel.Services.Serialization Task WriteDateTimeAsync(DateTime? datetime); - Task WriteTimeAsync(TimeOnly? time); - - Task WriteDateAsync(DateOnly? date); - Task WriteDecimalAsync(Decimal number); Task WriteTimeSpanAsync(TimeSpan period); diff --git a/ZeroLevel/Services/Serialization/MemoryStreamReader.cs b/ZeroLevel/Services/Serialization/MemoryStreamReader.cs index cf2b5d4..d5c90ea 100644 --- a/ZeroLevel/Services/Serialization/MemoryStreamReader.cs +++ b/ZeroLevel/Services/Serialization/MemoryStreamReader.cs @@ -249,21 +249,7 @@ namespace ZeroLevel.Services.Serialization long deserialized = BitConverter.ToInt64(buffer, 0); return DateTime.FromBinary(deserialized); } - public TimeOnly? ReadTime() - { - var is_null = ReadByte(); - if (is_null == 0) return null; - var ts = ReadTimeSpan(); - return TimeOnly.FromTimeSpan(ts); - } - - public DateOnly? ReadDate() - { - var is_null = ReadByte(); - if (is_null == 0) return null; - var days = ReadInt32(); - return DateOnly.FromDayNumber(days); - } + public IPAddress ReadIP() { var exists = ReadByte(); @@ -777,21 +763,6 @@ namespace ZeroLevel.Services.Serialization long deserialized = BitConverter.ToInt64(buffer, 0); return DateTime.FromBinary(deserialized); } - public async Task ReadTimeAsync() - { - var is_null = await ReadByteAsync(); - if (is_null == 0) return null; - var ts = await ReadTimeSpanAsync(); - return TimeOnly.FromTimeSpan(ts); - } - - public async Task ReadDateAsync() - { - var is_null = await ReadByteAsync(); - if (is_null == 0) return null; - var days = await ReadInt32Async(); - return DateOnly.FromDayNumber(days); - } public async Task ReadIPAsync() { var exists = await ReadByteAsync(); diff --git a/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs b/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs index 4e93967..3b776d0 100644 --- a/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs +++ b/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs @@ -144,34 +144,6 @@ namespace ZeroLevel.Services.Serialization } } - public void WriteTime(TimeOnly? time) - { - if (time == null) - { - WriteByte(0); - } - else - { - WriteByte(1); - var ts = time.Value.ToTimeSpan(); - WriteTimeSpan(ts); - } - } - - public void WriteDate(DateOnly? date) - { - if (date == null) - { - WriteByte(0); - } - else - { - WriteByte(1); - var days = date.Value.DayNumber; - WriteInt32(days); - } - } - public void WriteIP(IPAddress ip) { if (ip == null) @@ -562,34 +534,6 @@ namespace ZeroLevel.Services.Serialization } } - public async Task WriteTimeAsync(TimeOnly? time) - { - if (time == null) - { - WriteByte(0); - } - else - { - WriteByte(1); - var ts = time.Value.ToTimeSpan(); - await WriteTimeSpanAsync(ts); - } - } - - public async Task WriteDateAsync(DateOnly? date) - { - if (date == null) - { - WriteByte(0); - } - else - { - WriteByte(1); - var days = date.Value.DayNumber; - await WriteInt32Async(days); - } - } - public async Task WriteIPAsync(IPAddress ip) { if (ip == null) diff --git a/ZeroLevel/Services/Utils/RRandom.cs b/ZeroLevel/Services/Utils/RRandom.cs index 8f0c97a..506519d 100644 --- a/ZeroLevel/Services/Utils/RRandom.cs +++ b/ZeroLevel/Services/Utils/RRandom.cs @@ -10,7 +10,8 @@ namespace ZeroLevel.Services.Utils uint scale = uint.MaxValue; while (scale == uint.MaxValue) { - byte[] four_bytes = RandomNumberGenerator.GetBytes(4); + byte[] four_bytes = new byte[4]; + RandomNumberGenerator.Fill(four_bytes); scale = BitConverter.ToUInt32(four_bytes, 0); } return (int)(min + (max - min) * (scale / (double)uint.MaxValue)); diff --git a/ZeroLevel/ZeroLevel.csproj b/ZeroLevel/ZeroLevel.csproj index b6f7924..4313e66 100644 --- a/ZeroLevel/ZeroLevel.csproj +++ b/ZeroLevel/ZeroLevel.csproj @@ -1,80 +1,19 @@  - - net8.0 - Multi-tool - - ogoun - ogoun - 3.4.0.8 - - https://github.com/ogoun/Zero/wiki - Copyright Ogoun 2023 - - - https://github.com/ogoun/Zero - git - 3.4.0.8 - 3.4.0.8 - AnyCPU;x64;x86 - zero.png - full - none - zero.ico - AnyCPU - ZeroLevel - MIT - - - - true - False - portable - - - - true - False - - - - true - False - - - - true - prompt - none - false - x64 - False - - - - true - prompt - none - false - x86 - False - - - - true - False - none - - - - - - - - - True - - - + + latest + netstandard2.1 + enable + lz.ico + True + + + + + + + + + diff --git a/ZeroLevel/lz.ico b/ZeroLevel/lz.ico new file mode 100644 index 0000000..5bed896 Binary files /dev/null and b/ZeroLevel/lz.ico differ diff --git a/ZeroLevel/zero.ico b/ZeroLevel/zero.ico deleted file mode 100644 index ac52a8c..0000000 Binary files a/ZeroLevel/zero.ico and /dev/null differ diff --git a/ZeroLevel/zero.png b/ZeroLevel/zero.png deleted file mode 100644 index d904627..0000000 Binary files a/ZeroLevel/zero.png and /dev/null differ diff --git a/ZeroNetworkMonitor/ZeroNetworkMonitor.csproj b/ZeroNetworkMonitor/ZeroNetworkMonitor.csproj index a8324e0..0331aec 100644 --- a/ZeroNetworkMonitor/ZeroNetworkMonitor.csproj +++ b/ZeroNetworkMonitor/ZeroNetworkMonitor.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net8.0-windows7.0 enable true AnyCPU;x64 diff --git a/zero.png b/zero.png deleted file mode 100644 index d904627..0000000 Binary files a/zero.png and /dev/null differ