diff --git a/.dockerignore b/.dockerignore index e69de29..cd967fc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/Domain/BigFrameLayerRenderResult.cs b/Domain/BigFrameLayerRenderResult.cs index bf06a08..49817a1 100644 --- a/Domain/BigFrameLayerRenderResult.cs +++ b/Domain/BigFrameLayerRenderResult.cs @@ -1,8 +1,8 @@ -using Domain.Abstractions; +using SkiaSharp; namespace Domain; -public class FramePartRenderResult +public class BigFrameLayerRenderResult { public uint Row { get; set; } public uint Column { get; set; } @@ -10,5 +10,6 @@ public class FramePartRenderResult public uint FrameSizeY { get; set; } // или пиксели? - public string Frame { get; set; } + public SKBitmap Frame { get; set; } //типа автомаска + public List FrameInfos { get; set; } = []; } \ No newline at end of file diff --git a/Domain/Domain.csproj b/Domain/Domain.csproj index cfadb03..17118bb 100644 --- a/Domain/Domain.csproj +++ b/Domain/Domain.csproj @@ -1,9 +1,14 @@ - net7.0 + net9.0 enable enable + 13 + + + + diff --git a/Domain/ExpIniFileGenerator.cs b/Domain/ExpIniFileGenerator.cs index 96bd9e1..0eec35a 100644 --- a/Domain/ExpIniFileGenerator.cs +++ b/Domain/ExpIniFileGenerator.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Domain.Something; +namespace Domain; public class ExpIniFileGenerator(uint rowCount, uint oddColumnCount, uint evenColumnCount) { diff --git a/Domain/Interfaces/IFrameRender.cs b/Domain/Interfaces/IFrameRender.cs index e69de29..cffb4d5 100644 --- a/Domain/Interfaces/IFrameRender.cs +++ b/Domain/Interfaces/IFrameRender.cs @@ -0,0 +1,12 @@ +using Domain; + +namespace Domain.Interfaces; + +public interface IFrameRender +{ + public Guid RenderId { get; } + + public Task> Execute(CancellationToken cancellationToken); + + public long EstimateMemoryUsage(); +} \ No newline at end of file diff --git a/Domain/Interfaces/ILayer.cs b/Domain/Interfaces/ILayer.cs index a089c17..aa4b217 100644 --- a/Domain/Interfaces/ILayer.cs +++ b/Domain/Interfaces/ILayer.cs @@ -1,5 +1,5 @@ using SkiaSharp; -namespace Domain; +namespace Domain.Interfaces; public interface ILayer { @@ -9,5 +9,5 @@ public interface ILayer public SKBitmap? Mask { get; } public bool InvertMask { get; } public Task Prerender(); - public Task Render(uint u, uint u1, uint frameWidth, uint frameHeight, SKBitmap automask); + public Task Render(uint x, uint y, uint frameWidth, uint frameHeight, SKBitmap automask); } diff --git a/Domain/Interfaces/IRenderManager.cs b/Domain/Interfaces/IRenderManager.cs index c207fd0..8a9c26c 100644 --- a/Domain/Interfaces/IRenderManager.cs +++ b/Domain/Interfaces/IRenderManager.cs @@ -1,4 +1,4 @@ -namespace Domain; +namespace Domain.Interfaces; public interface IRenderManager { diff --git a/Domain/Project.cs b/Domain/Project.cs index e69de29..5d86834 100644 --- a/Domain/Project.cs +++ b/Domain/Project.cs @@ -0,0 +1,10 @@ +using Domain.Interfaces; + +namespace Domain; +public class Project +{ + public Guid Id { get; set; } + public IEnumerable Layers { get; set; } = []; + public uint Width { get; set; } + public uint Height { get; set; } +} \ No newline at end of file diff --git a/Domain/Render.cs b/Domain/Render.cs index e69de29..8e608fc 100644 --- a/Domain/Render.cs +++ b/Domain/Render.cs @@ -0,0 +1,69 @@ +using System.Threading.Channels; +using Domain.Interfaces; +using Domain.RenderPart; + +namespace Domain; + +public class Render +{ + public Guid Id { get; } + public Guid ProjectId { get; } + + private readonly IEnumerable _layers; + private readonly uint _width; + private readonly uint _height; + + private readonly bool _useBigFrames = true; + private readonly CancellationTokenSource _cancellationTokenSource = new(); + + public Render(Project project) + { + Id = Guid.NewGuid(); + ProjectId = project.Id; + _width = project.Width; + _height = project.Height; + _layers = project.Layers; + } + + public async Task Run(Channel channel) + { + foreach (var layer in _layers) + { + await layer.Prerender(); + } + + if (_useBigFrames) + { + await MakeBigFrames(channel); + } + else + { + throw new NotImplementedException(); + // MakeFrames(channel); + } + + //maybe returb render result + } + + private async Task MakeBigFrames(Channel channel) + { + uint bigFrameWidth = 8, bigFrameHeight = 8; + + for (uint y = 0; y < _height; y += bigFrameHeight) + { + uint currentBigFrameHeight = Math.Min(bigFrameHeight, _height - y); + + for (uint x = 0; x < _width; x += bigFrameWidth) + { + uint currentBigFrameWidth = Math.Min(bigFrameWidth, _width - x); + var bigFrameRender = new BigFrameRender(_layers, x, y, currentBigFrameWidth, currentBigFrameHeight, Id); + await channel.Writer.WriteAsync(bigFrameRender); + } + } + } + + public async Task Stop() + { + await Task.Delay(100); + } +} \ No newline at end of file diff --git a/Domain/RenderManager.cs b/Domain/RenderManager.cs index e69de29..896e6e0 100644 --- a/Domain/RenderManager.cs +++ b/Domain/RenderManager.cs @@ -0,0 +1,89 @@ +using System.Collections.Concurrent; +using System.Threading.Channels; +using Domain.Interfaces; + +namespace Domain; + +public class RenderManager : IRenderManager +{ + private readonly Channel _taskChannel; + private readonly ConcurrentDictionary _renders = []; + + public RenderManager(int channelCapacity = 5) + { + var options = new BoundedChannelOptions(channelCapacity) + { + FullMode = BoundedChannelFullMode.Wait // Ожидание при переполнении канала + }; + _taskChannel = Channel.CreateBounded(options); + } + + public Task StartRender(Project project) + { + var render = new Render(project); + if (_renders.TryAdd(render.Id, render)) + { + _ =render.Run(_taskChannel); + } + _ = ProcessQueue(); + return Task.FromResult(render.Id); + } + + private async Task ProcessQueue(CancellationToken cancellationToken = default) + { + while (await _taskChannel.Reader.WaitToReadAsync(cancellationToken)) + { + while (_taskChannel.Reader.TryRead(out var task)) + { + try + { + await task.Execute(cancellationToken); + } + catch (OperationCanceledException) + { + // Console.WriteLine($"Task {task.RenderId} was cancelled."); + } + } + } + } + + public async Task StopRender(Guid renderId) + { + if (_renders.TryRemove(renderId, out var render)) + { + await render.Stop(); + UpdateProjectHistoryToHistory(render.ProjectId, render.Id); + } + } + + private async Task ClearTasksForRender(Guid renderId) + { + var remainingTasks = new List(); + while (await _taskChannel.Reader.WaitToReadAsync()) + { + if (_taskChannel.Reader.TryRead(out var task)) + { + // if (task.RenderId != renderId) + // { + // remainingTasks.Add(task); + // } + } + } + + foreach (var task in remainingTasks) + { + await _taskChannel.Writer.WriteAsync(task); + } + } + + private void UpdateProjectHistoryToHistory(Guid projectId, Guid renderId) + { + Console.WriteLine($"UpdateProjectHistoryToHistory: {projectId}, {renderId}"); + } + + public IEnumerable GetActiveRenders() + { + return new List(); + } + +} \ No newline at end of file diff --git a/Domain/RenderPart/BigFrameRender.cs b/Domain/RenderPart/BigFrameRender.cs index b308958..4a6525f 100644 --- a/Domain/RenderPart/BigFrameRender.cs +++ b/Domain/RenderPart/BigFrameRender.cs @@ -1,9 +1,7 @@ -using Domain.Abstractions; using Domain.Interfaces; -using Domain.Something; using SkiaSharp; -namespace Domain; +namespace Domain.RenderPart; public class BigFrameRender : RenderPartBase { diff --git a/Domain/RenderPart/FrameRender.cs b/Domain/RenderPart/FrameRender.cs index ea666dd..a263ca2 100644 --- a/Domain/RenderPart/FrameRender.cs +++ b/Domain/RenderPart/FrameRender.cs @@ -1,6 +1,6 @@ namespace Domain.RenderPart; -// public class FrameRenderPart : RenderPartBase +// public class FrameRender : RenderPartBase // { // // } \ No newline at end of file diff --git a/Domain/RenderPart/RenderPartBase.cs b/Domain/RenderPart/RenderPartBase.cs index 99fded6..883ec32 100644 --- a/Domain/RenderPart/RenderPartBase.cs +++ b/Domain/RenderPart/RenderPartBase.cs @@ -1,9 +1,6 @@ -using System.Collections; -using Domain.Abstractions; using Domain.Interfaces; -using Domain.Something; -namespace Domain; +namespace Domain.RenderPart; public abstract class RenderPartBase : IFrameRender { diff --git a/Domain/RenderResult.cs b/Domain/RenderResult.cs index 8e13d18..442bbfa 100644 --- a/Domain/RenderResult.cs +++ b/Domain/RenderResult.cs @@ -1,6 +1,29 @@ -namespace Domain.Abstractions; +namespace Domain; public class RenderResult { - public List<> + public List Frames { get; set; } = []; + public ExpIniConfig ExpIniConfig { get; set; } = null!; +} + +public class ExpIniConfig +{ + public uint RowCount { get; set; } + public uint OddColumnCount { get; set; } + public uint EvenColumnCount { get; set; } + public IEnumerable FrameInfos { get; set; } = []; +} + +public class FrameInfo +{ + public uint Number { get; set; } + public uint Row { get; set; } + public uint Column { get; set; } + public ExpState State { get; set; } +} + +public enum ExpState +{ + E, //поправить + Z, //поправить } \ No newline at end of file diff --git a/Make3.RenderServer/Grpc/Protos/Layers/test-layer.proto b/Make3.RenderServer/Grpc/Protos/Layers/test-layer.proto index e69de29..91b4b03 100644 --- a/Make3.RenderServer/Grpc/Protos/Layers/test-layer.proto +++ b/Make3.RenderServer/Grpc/Protos/Layers/test-layer.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +import "google/protobuf/wrappers.proto"; +option csharp_namespace = "LayerClients"; + +service TestLayer { + rpc Prerender (PrerenderRequest) returns (PrerenderResponse); + rpc Render (RenderRequest) returns (RenderResponse); +} + +message PrerenderRequest { + string optical_schema_data = 1; + bytes mask = 2; + bytes image = 3; + bool invert_mask = 4; +} + +message PrerenderResponse { + string status = 1; + repeated MatrixRow test_matrix = 2; // Представляем матрицу через строки +} + +message MatrixRow { + repeated double values = 1; // Значения одной строки матрицы +} + +message RenderRequest { + uint32 x = 1; + uint32 y = 2; + uint32 frame_width = 3; + uint32 frame_height = 4; + bytes image = 5; + google.protobuf.BoolValue invert_mask = 6; + google.protobuf.BytesValue mask = 7; + google.protobuf.BytesValue automask = 8; +} + +message RenderResponse { + bytes rendered_image = 1; +} \ No newline at end of file diff --git a/Make3.RenderServer/Layers/TestLayer.cs b/Make3.RenderServer/Layers/TestLayer.cs index e69de29..94f7139 100644 --- a/Make3.RenderServer/Layers/TestLayer.cs +++ b/Make3.RenderServer/Layers/TestLayer.cs @@ -0,0 +1,62 @@ +using Domain; +using Domain.Interfaces; +using Google.Protobuf; +using LayerClients; +using SkiaSharp; + +namespace Make3.RenderServer.Layers; + +public class TestLayer(LayerClients.TestLayer.TestLayerClient grpcClient) : ILayer +{ + public uint Order { get; init; } + public OpticalSchema OpticalSchema { get; init; } + public SKBitmap? Mask { get; init; } + public required SKBitmap Image { get; init; } + public bool InvertMask { get; init; } + + private readonly LayerClients.TestLayer.TestLayerClient _grpcClient = grpcClient; + + public async Task Prerender() + { + using var imageData = Image.Encode(SKEncodedImageFormat.Png, 100); + using var maskData = Mask?.Encode(SKEncodedImageFormat.Png, 100); + var request = new PrerenderRequest + { + OpticalSchemaData = OpticalSchema.ToString(), + Image = ByteString.CopyFrom(imageData.Span), + }; + if (maskData != null) + { + request.Mask = ByteString.CopyFrom(maskData.Span); + request.InvertMask = InvertMask; + } + + var response = await _grpcClient.PrerenderAsync(request); + } + + public async Task Render(uint x, uint y, uint frameWidth, uint frameHeight, SKBitmap? automask) + { + //здесь надо кусок изображения взять + using var imageData = Image.Encode(SKEncodedImageFormat.Png, 100); + using var maskData = Mask?.Encode(SKEncodedImageFormat.Png, 100); + using var automaskData = automask?.Encode(SKEncodedImageFormat.Png, 100); + var request = new RenderRequest + { + X = x, + Y = y, + FrameWidth = frameWidth, + FrameHeight = frameHeight, + Image = ByteString.CopyFrom(imageData.Span), + // Automask = ByteString.CopyFrom(imageData.Span), + }; + if (maskData != null) + { + request.Mask = ByteString.CopyFrom(maskData.Span); + request.InvertMask = InvertMask; + } + request.Automask = automaskData == null ? null : ByteString.CopyFrom(automaskData.Span); + var response = await _grpcClient.RenderAsync(request); + var byteArray = response.RenderedImage.ToByteArray(); + return SKBitmap.Decode(byteArray); + } +} \ No newline at end of file diff --git a/Make3.RenderServer/Make3.RenderServer.csproj b/Make3.RenderServer/Make3.RenderServer.csproj index 672bca4..df58ac7 100644 --- a/Make3.RenderServer/Make3.RenderServer.csproj +++ b/Make3.RenderServer/Make3.RenderServer.csproj @@ -8,6 +8,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -17,4 +22,12 @@ + + + + + + + + diff --git a/Make3.RenderServer/Program.cs b/Make3.RenderServer/Program.cs index d5e0ef3..c40f162 100644 --- a/Make3.RenderServer/Program.cs +++ b/Make3.RenderServer/Program.cs @@ -1,9 +1,23 @@ +using Domain; +using Domain.Interfaces; +using Make3.RenderServer; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); +builder.Services.AddScoped(); + +// builder.Services.AddGrpc(); +builder.Services.AddGrpcClient(options => +{ + options.Address = new Uri("http://localhost:5062"); +}); + +builder.Services.AddHostedService(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/Make3.RenderServer/TestProjectBackgroundService.cs b/Make3.RenderServer/TestProjectBackgroundService.cs index e69de29..46958c0 100644 --- a/Make3.RenderServer/TestProjectBackgroundService.cs +++ b/Make3.RenderServer/TestProjectBackgroundService.cs @@ -0,0 +1,61 @@ +using Domain; +using Domain.Interfaces; +using Make3.RenderServer.Layers; +using SkiaSharp; + +namespace Make3.RenderServer; + +public class TestProjectBackgroundService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly LayerClients.TestLayer.TestLayerClient _testLaterGrpcClient; + + public TestProjectBackgroundService(IServiceProvider serviceProvider, LayerClients.TestLayer.TestLayerClient testLaterGrpcClient) + { + _serviceProvider = serviceProvider; + _testLaterGrpcClient = testLaterGrpcClient; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var dir = @"C:\Users\Ko12A\OneDrive\Изображения"; + var image1 = OpenImage(Path.Combine(dir, "01_gettyimages_488111026_resized.jpg")); + var image2 = OpenImage(Path.Combine(dir, "kisspng-bmp-file-for.bmp")); + var image3 = OpenImage(Path.Combine(dir, "Zrzut ekranu 2021-07-20 164131.png")); + + var project = new Project + { + Id = Guid.NewGuid(), + Height = Convert.ToUInt32(image1.Width), + Width = Convert.ToUInt32(image2.Width), + Layers = [ + new TestLayer(_testLaterGrpcClient) + { + Order = 0, + Image = image1, + OpticalSchema = OpticalSchema.Sp11, + }, + new TestLayer(_testLaterGrpcClient) + { + Order = 1, + Image = image2, + Mask = image3, + InvertMask = false, + OpticalSchema = OpticalSchema.Sp11, + }, + ], + }; + + using var scope = _serviceProvider.CreateScope(); + var renderManager = scope.ServiceProvider.GetRequiredService(); + var renderId = await renderManager.StartRender(project); + } + + private SKBitmap OpenImage(string filename) + { + using SKBitmap bitmap = SKBitmap.Decode(filename); + return bitmap.Copy(); + } + + +} \ No newline at end of file diff --git a/Make3.Renderer.sln b/Make3.Renderer.sln index 2e2f61e..eadb512 100644 --- a/Make3.Renderer.sln +++ b/Make3.Renderer.sln @@ -7,6 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Make3.Renderer", "Make3.Ren EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{5D88EEB2-A075-4531-890E-DC38E2700325}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FA217554-E89C-467E-AE89-FC167D77F55F}" + ProjectSection(SolutionItems) = preProject + compose.yaml = compose.yaml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Make3.RenderServer", "Make3.RenderServer\Make3.RenderServer.csproj", "{3262E7F2-74E7-4314-A87B-E8CC357CCE76}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +28,10 @@ Global {5D88EEB2-A075-4531-890E-DC38E2700325}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D88EEB2-A075-4531-890E-DC38E2700325}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D88EEB2-A075-4531-890E-DC38E2700325}.Release|Any CPU.Build.0 = Release|Any CPU + {3262E7F2-74E7-4314-A87B-E8CC357CCE76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3262E7F2-74E7-4314-A87B-E8CC357CCE76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3262E7F2-74E7-4314-A87B-E8CC357CCE76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3262E7F2-74E7-4314-A87B-E8CC357CCE76}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Make3.Renderer/Layer1.cs b/Make3.Renderer/Layer1.cs index 0a8897c..1a32f06 100644 --- a/Make3.Renderer/Layer1.cs +++ b/Make3.Renderer/Layer1.cs @@ -1,17 +1,29 @@ -using Domain.Abstractions; +using Domain; +using Domain.Interfaces; +using SkiaSharp; -namespace Domain.ConcreteLayers; +namespace Make3.Renderer; public class Layer1 : ILayer { public uint Order { get; set; } public OpticalSchema OpticalSchema { get; set; } - public char[,] Image { get; set; } - public char[,]? Mask { get; set; } + public SKBitmap Image { get; set; } + public SKBitmap? Mask { get; set; } public bool InvertMask { get; set; } public Task Execute() { throw new NotImplementedException(); } + + public async Task Prerender() + { + throw new NotImplementedException(); + } + + public async Task Render(uint x, uint y, uint frameWidth, uint frameHeight, SKBitmap automask) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/Make3.Renderer/Make3.Renderer.csproj b/Make3.Renderer/Make3.Renderer.csproj index aa56d6d..6990e9c 100644 --- a/Make3.Renderer/Make3.Renderer.csproj +++ b/Make3.Renderer/Make3.Renderer.csproj @@ -2,18 +2,26 @@ Exe - net7.0 + net9.0 enable enable + 13 - - - - - + + + Always + + + Always + + + Always + + + diff --git a/Make3.Renderer/Program.cs b/Make3.Renderer/Program.cs index d624d08..c3cf410 100644 --- a/Make3.Renderer/Program.cs +++ b/Make3.Renderer/Program.cs @@ -1,46 +1,64 @@ // See https://aka.ms/new-console-template for more information -using Domain.Abstractions; -using Make3.Renderer; -using Make3.Renderer.Layers.TrekoLayers; -using OpenCvSharp; -Console.WriteLine("Hello, World!"); +using Domain; +using SkiaSharp; -var filePath = @"F:\GS_TEST.png"; -filePath = @"F:\GS_TEST_1.png"; - -var outputDirectory = "F:\\"; - -using (var s = new FileStream(filePath, FileMode.Open, FileAccess.Read)) -{ - var testClass = new TestClass(s, outputDirectory); - testClass.Execute(); -} Console.ReadLine(); -//входные данные -var projectLayers = new List -{ - new Treko(0), - new Treko(1), - new Treko(2), -}; -//программа -projectLayers.Sort((a, b) => a.Order.CompareTo(b.Order)); +// SKBitmap OpenImage(string filename) +// { +// var text = File.ReadAllText(filename); +// var rows = text.Split(Environment.NewLine); +// var width = rows.Max(q => q.Length); +// var result = new char[width, rows.Length]; +// for (var row = 0; row < rows.Length; row++) +// { +// for (var col = 0; col < width; col++) +// { +// result[col, row] = rows[row][col]; +// } +// } +// return result; +// } -for (int i = 0; i < projectLayers.Count; i++) -{ - await projectLayers[i].Execute(); -} -//List ImageColors(Mat image) -//{ - -//} -class ShapeInfo -{ - public Point[][] Points { get; set; } - public byte Color { get; set; } -} \ No newline at end of file +// var filePath = @"F:\GS_TEST.png"; +// filePath = @"F:\GS_TEST_1.png"; +// +// var outputDirectory = "F:\\"; +// +// using (var s = new FileStream(filePath, FileMode.Open, FileAccess.Read)) +// { +// var testClass = new TestClass(s, outputDirectory); +// testClass.Execute(); +// } +// Console.ReadLine(); +// //входные данные +// var projectLayers = new List +// { +// new Treko(0), +// new Treko(1), +// new Treko(2), +// }; +// +// +// //программа +// projectLayers.Sort((a, b) => a.Order.CompareTo(b.Order)); +// +// for (int i = 0; i < projectLayers.Count; i++) +// { +// await projectLayers[i].Execute(); +// } +// +// //List ImageColors(Mat image) +// //{ +// +// //} +// +// class ShapeInfo +// { +// public Point[][] Points { get; set; } +// public byte Color { get; set; } +// } \ No newline at end of file diff --git a/Make3.Renderer/TestClass.cs b/Make3.Renderer/TestClass.cs index 1a4dea5..2e2b602 100644 --- a/Make3.Renderer/TestClass.cs +++ b/Make3.Renderer/TestClass.cs @@ -1,425 +1,425 @@ -using OpenCvSharp; - -namespace Make3.Renderer -{ - internal class TestClass - { - private readonly Mat _inputMat; - private readonly string _outputDirectory; - - public TestClass(Stream stream, string outputDirectory) - { - _inputMat = Mat.FromStream(stream, ImreadModes.Grayscale); ; - _outputDirectory = outputDirectory; - } - - public TestClass(Mat inputMat, string outputDirectory) - { - _inputMat = inputMat; - _outputDirectory = outputDirectory; - } - - internal void Execute() - { - var colors = GetColors(); - - var c = _inputMat.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); - var closing = c.EmptyClone(); - Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); - - //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); - Point[][] contours; - HierarchyIndex[] hierarchy; - //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - closing.FindContours(out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - - var contNum = 0; - - var allContursColored = GetContoursColored(contours, hierarchy, contNum); - allContursColored.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST_{contNum}_conturs_colored.png")); - - var allConturs = GetContoursBW(contours, hierarchy, contNum); - allConturs.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contNum}_conturs_BW.png")); - - //colorSeparatedMats.Add(c); - - DistanceTransform(allConturs, contNum); - } - - private List GetColors() - { - var nonZero = _inputMat.FindNonZero(); - var colors = new List(); - var total = nonZero.Total(); - for (int i = 0; i < total; i++) - { - var p = nonZero.At(i); - var color = _inputMat.At(p.X, p.Y); - if (!colors.Contains(color) && color != 0) colors.Add(color); - } - return colors; - } - - private Mat GetContoursColored(Point[][] contours, HierarchyIndex[] hierarchy, int contur) - { - var result = Mat.Zeros(_inputMat.Size(), MatType.CV_8UC3).ToMat(); - Cv2.DrawContours(result, contours, contur, new Scalar(hierarchy[contur].Parent < 0 ? 255 : 0, 128, 20 * contur), -1, - hierarchy: hierarchy, - maxLevel: int.MaxValue); - return result; - } - - /// - /// sdfsd - /// - /// - /// - /// - /// - private Mat GetContoursBW(Point[][] contours, HierarchyIndex[] hierarchy, int contur) - { - var result = Mat.Zeros(_inputMat.Size(), MatType.CV_8UC1).ToMat(); - Cv2.DrawContours(result, contours, contur, new Scalar(hierarchy[contur].Parent < 255 ? 255 : 20 * contur), -1, - hierarchy: hierarchy, - maxLevel: int.MaxValue); - return result; - } - - /// - /// - /// Ссылка на обсуждение нахождения расстояний - /// вот и на форум из комментариев - /// ещё вот. Там на C++ - /// - /// - /// Вот ещё что-то с DataTransform. - /// - /// - /// И ещё, но что-то не читал. - /// - /// - /// Это, к сожалению, на Питоне, - /// и там используется местная библиотека numpy, так что не подходит. - /// - /// - /// Вот тут - /// примеры функций типа bwdist и прочих. До конца не смотрел, возможно, что-то и подходит.К сожалению, тоже на Питоне, и также используется numpy. - /// - /// - /// Это - /// просто документация, описание DistanceTransformWithLabels. К сожалению, написано очень скудно, и примеров нет. - /// - /// - /// Вот тут - /// примеры функций, но похоже, просто скопировано с докементации, примеров, к сожалению, нет (ну они есть, но не те). - /// - /// - /// - /// - private void DistanceTransform(Mat input, int contur) - { - var distanceTransform = input.EmptyClone(); - //Cv2.DistanceTransform(dst1, dst2, DistanceTypes.L2, DistanceTransformMasks.Precise); - var labels = input.EmptyClone(); - Cv2.DistanceTransformWithLabels(input, distanceTransform, labels, DistanceTypes.L2, DistanceTransformMasks.Precise, - //Cv2.DistanceTransformWithLabels(dst1, dst2, labelIndexes, DistanceTypes.L2, DistanceTransformMasks.Mask3, - DistanceTransformLabelTypes.Pixel); - distanceTransform.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contur}_distance_transform.png")); - - //labels.PushBack(-1); - var k = 1; - for (int i = 0; i < distanceTransform.Height; i++) - { - for (int j = 0; j < distanceTransform.Width; j++) - { - var px = distanceTransform.At(i, j); - if (px == 0) - { - //labels.PushBack(new Vec2i(i, j)); - } - else { } - var p = labels.At(i, j); - - var p_x = p % labels.Width; - var p_y = p / labels.Width; - - var p1 = labels.At(i, j); - var p2 = labels.At(i, j); - var p3 = labels.At(i, j); - var p4 = labels.At(i, j); - - } - } - - labels.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contur}_distance_transform_labels.png")); - } - - //private void B() - //{ - // using (var s = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - // { - - // //var m = new Mat(); - // //var m = Mat.FromStream(s, ImreadModes.Color); - // var m = Mat.FromStream(s, ImreadModes.Grayscale); - // var result = new List(); - - // var nonZero = m.FindNonZero(); - // var colors = new List(); - // var total = nonZero.Total(); - // for (int i = 0; i < total; i++) - // { - // var p = nonZero.At(i); - // var color = m.At(p.X, p.Y); - // if (!colors.Contains(color) && color != 0) colors.Add(color); - // } - - - // //___ - - // //var c = m.InRange(new Scalar(color), new Scalar(color)); - - // var c = m.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); - - // var closing = c.EmptyClone(); - - // //var contours0 = closing.FindContoursAsMat(RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - // //Cv2.MorphologyEx(c, closing, MorphTypes.Close, contours0[0]); - // Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); - - // //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); - // Point[][] contours1; - // HierarchyIndex[] hierarchy; - // //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - // closing.FindContours(out contours1, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - - // var contNum = 0; - - // var aaaaa = contours1.Count(); - - // var aaa = hierarchy.Where(q => q.Parent < 0).ToList(); - - // //var boundingRect = Cv2.BoundingRect(contours1[0]); - - // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); - // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // //Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, 128, 20 * contNum), -1, - // // LineTypes.Link8, maxLevel: -1); - // //Cv2.DrawContours(dst0, contours1, 0, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, maxLevel: -1); - // //Cv2.DrawContours(dst0, contours1, 0, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, maxLevel: int.MaxValue); - // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, - // hierarchy: hierarchy, - // //maxLevel: -1); - // maxLevel: int.MaxValue); - // //colorSeparatedMats.Add(c); - // dst0.SaveImage(@$"F:\GS_TEST_c{contNum}.png"); - - // var boundingRect = Cv2.BoundingRect(contours1[0]); - - // //var dst1 = Mat.Zeros(boundingRect.Size, MatType.CV_8UC1).ToMat(); - // var dst1 = Mat.Zeros(m.Size(), MatType.CV_8UC1).ToMat(); - // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // //Cv2.DrawContours(dst1, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, - // Cv2.DrawContours(dst1, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 255 ? 255 : 20 * contNum), -1, - // hierarchy: hierarchy, - // //maxLevel: -1); - // maxLevel: int.MaxValue); - // //colorSeparatedMats.Add(c); - // dst1.SaveImage(@$"F:\GS_TEST_cc{contNum}.png"); - - // var dst2 = dst1.EmptyClone(); - // //Cv2.DistanceTransform(dst1, dst2, DistanceTypes.L2, DistanceTransformMasks.Precise); - // var labels = dst1.EmptyClone(); - // Cv2.DistanceTransformWithLabels(dst1, dst2, labels, DistanceTypes.L2, DistanceTransformMasks.Precise, - // //Cv2.DistanceTransformWithLabels(dst1, dst2, labelIndexes, DistanceTypes.L2, DistanceTransformMasks.Mask3, - // DistanceTransformLabelTypes.Pixel); - // dst2.SaveImage(@$"F:\GS_TEST_ccc{contNum}.png"); - - // //labels.PushBack(-1); - // var k = 1; - // for (int i = 0; i < dst2.Height; i++) - // //for (int i = boundingRect.Top; i < boundingRect.Height; i++) - // { - // for (int j = 0; j < dst2.Width; j++) - // //for (int j = boundingRect.Left; j < boundingRect.Width; j++) - // { - // var px = dst2.At(i, j); - // if (px == 0) - // { - // //labels.PushBack(new Vec2i(i, j)); - // } - // else { } - // var p = labels.At(i, j); - - // var p_x = p % labels.Width; - // var p_y = p / labels.Width; - - // var p1 = labels.At(i, j); - // var p2 = labels.At(i, j); - // var p3 = labels.At(i, j); - // var p4 = labels.At(i, j); - - // } - // } - - // labels.SaveImage(@$"F:\GS_TEST_cccc{contNum}.png"); - - - - - // //var un = labels.dis - - // Console.ReadLine(); - // //foreach (var contour in contours1) - // //{ - // // //if (hierarchy[contNum].Parent < 0) - // // //{ - // // // var boundingRect = Cv2.BoundingRect(contour); - - // // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); - // // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, - // // // LineTypes.Link8, maxLevel: -1); - // // // //colorSeparatedMats.Add(c); - // // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); - // // //} - - // // var boundingRect = Cv2.BoundingRect(contour); - - // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); - // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // //Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, 128, 20 * contNum), -1, - // // // LineTypes.Link8, maxLevel: -1); - // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, - // // LineTypes.Link8, maxLevel: -1); - // // //colorSeparatedMats.Add(c); - // // dst0.SaveImage(@$"F:\GS_TEST_c{contNum}.png"); - - - - - // // //var mean = Cv2.Mean(c, mask); - // // //var notBlackContour = Cv2.Mean(c, mask)[0] > 0; - - // // //var averageInsideColor = - - - // // //var c1 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // //var dst0 = c1.EmptyClone(); - // // //Cv2.BitwiseAnd(c1, c1, dst0, c.GetRectSubPix(boundingRect.Size, new Point2f(boundingRect.Left + (boundingRect.Width / 2), boundingRect.Top + (boundingRect.Height / 2)))); - - // // //var c1 = c.CvtColor(ColorConversionCodes.GRAY2BGR); - // // //var m1 = c1.InRange(new Scalar(0), new Scalar(255)); - // // //Cv2.FloodFill(c1, m1, new Point(0, 0), new Scalar(0, color, 20 * cNum)); - - // // //dst0.SaveImage(@$"F:\GS_TEST_cc{cNum}_{contNum}.png"); - // // contNum++; - // //} - - // //___ - - // //var colorSeparatedMats = new List(); - // //var cNum = 0; - // //foreach (var color in colors) - // //{ - - - // // //var c = m.InRange(new Scalar(color), new Scalar(color)); - - // // var c = m.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); - - // // var closing = c.EmptyClone(); - - // // //var contours0 = closing.FindContoursAsMat(RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - // // //Cv2.MorphologyEx(c, closing, MorphTypes.Close, contours0[0]); - // // Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); - - // // //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); - // // Point[][] contours1; - // // HierarchyIndex[] hierarchy; - // // //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - // // closing.FindContours(out contours1, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); - - // // var contNum = 0; - - // // var aaaaa = contours1.Count(); - - // // var a01 = hierarchy[0].Previous; - // // var a02 = hierarchy[0].Next; - // // var a03 = hierarchy[1].Previous; - // // var a04 = hierarchy[1].Next; - - // // var aaa = hierarchy.Where(q => q.Parent < 0).ToList(); - - // // foreach (var contour in contours1) - // // { - // // //if (hierarchy[contNum].Parent < 0) - // // //{ - // // // var boundingRect = Cv2.BoundingRect(contour); - - // // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); - // // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, - // // // LineTypes.Link8, maxLevel: -1); - // // // //colorSeparatedMats.Add(c); - // // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); - // // //} - - // // var boundingRect = Cv2.BoundingRect(contour); - - // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); - // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, - // // LineTypes.Link8, maxLevel: -1); - // // //colorSeparatedMats.Add(c); - // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); - - - - - // // //var mean = Cv2.Mean(c, mask); - // // //var notBlackContour = Cv2.Mean(c, mask)[0] > 0; - - // // //var averageInsideColor = - - - // // //var c1 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); - // // //var dst0 = c1.EmptyClone(); - // // //Cv2.BitwiseAnd(c1, c1, dst0, c.GetRectSubPix(boundingRect.Size, new Point2f(boundingRect.Left + (boundingRect.Width / 2), boundingRect.Top + (boundingRect.Height / 2)))); - - // // //var c1 = c.CvtColor(ColorConversionCodes.GRAY2BGR); - // // //var m1 = c1.InRange(new Scalar(0), new Scalar(255)); - // // //Cv2.FloodFill(c1, m1, new Point(0, 0), new Scalar(0, color, 20 * cNum)); - - // // //dst0.SaveImage(@$"F:\GS_TEST_cc{cNum}_{contNum}.png"); - // // contNum++; - // // } - - // // //colorSeparatedMats.Add(c); - // // //c.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); - - // // cNum++; - // //} - - // //foreach (var cMat in colorSeparatedMats) - // //{ - // // cNum++; - // //} - // //c.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); - - // var contours = m.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); - // var a = contours.Count(); - // //var c = contours.Where(q => q.).Count(); - // //var nbl = m.FindNonZero(); - // //var contours1 = nbl.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); - // //var b = contours1.Count(); - // var biggestContourRect = Cv2.BoundingRect(contours[0]); - // Mat dst = new Mat(); - // Cv2.Rectangle(dst, - // new Point(biggestContourRect.X, biggestContourRect.Y), - // new Point(biggestContourRect.X + biggestContourRect.Width, biggestContourRect.Y + biggestContourRect.Height), - // new Scalar(255, 255, 255), 2); - // } - //} - } -} +// using OpenCvSharp; +// +// namespace Make3.Renderer +// { +// internal class TestClass +// { +// private readonly Mat _inputMat; +// private readonly string _outputDirectory; +// +// public TestClass(Stream stream, string outputDirectory) +// { +// _inputMat = Mat.FromStream(stream, ImreadModes.Grayscale); ; +// _outputDirectory = outputDirectory; +// } +// +// public TestClass(Mat inputMat, string outputDirectory) +// { +// _inputMat = inputMat; +// _outputDirectory = outputDirectory; +// } +// +// internal void Execute() +// { +// var colors = GetColors(); +// +// var c = _inputMat.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); +// var closing = c.EmptyClone(); +// Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); +// +// //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); +// Point[][] contours; +// HierarchyIndex[] hierarchy; +// //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// closing.FindContours(out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// +// var contNum = 0; +// +// var allContursColored = GetContoursColored(contours, hierarchy, contNum); +// allContursColored.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST_{contNum}_conturs_colored.png")); +// +// var allConturs = GetContoursBW(contours, hierarchy, contNum); +// allConturs.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contNum}_conturs_BW.png")); +// +// //colorSeparatedMats.Add(c); +// +// DistanceTransform(allConturs, contNum); +// } +// +// private List GetColors() +// { +// var nonZero = _inputMat.FindNonZero(); +// var colors = new List(); +// var total = nonZero.Total(); +// for (int i = 0; i < total; i++) +// { +// var p = nonZero.At(i); +// var color = _inputMat.At(p.X, p.Y); +// if (!colors.Contains(color) && color != 0) colors.Add(color); +// } +// return colors; +// } +// +// private Mat GetContoursColored(Point[][] contours, HierarchyIndex[] hierarchy, int contur) +// { +// var result = Mat.Zeros(_inputMat.Size(), MatType.CV_8UC3).ToMat(); +// Cv2.DrawContours(result, contours, contur, new Scalar(hierarchy[contur].Parent < 0 ? 255 : 0, 128, 20 * contur), -1, +// hierarchy: hierarchy, +// maxLevel: int.MaxValue); +// return result; +// } +// +// /// +// /// sdfsd +// /// +// /// +// /// +// /// +// /// +// private Mat GetContoursBW(Point[][] contours, HierarchyIndex[] hierarchy, int contur) +// { +// var result = Mat.Zeros(_inputMat.Size(), MatType.CV_8UC1).ToMat(); +// Cv2.DrawContours(result, contours, contur, new Scalar(hierarchy[contur].Parent < 255 ? 255 : 20 * contur), -1, +// hierarchy: hierarchy, +// maxLevel: int.MaxValue); +// return result; +// } +// +// /// +// /// +// /// Ссылка на обсуждение нахождения расстояний +// /// вот и на форум из комментариев +// /// ещё вот. Там на C++ +// /// +// /// +// /// Вот ещё что-то с DataTransform. +// /// +// /// +// /// И ещё, но что-то не читал. +// /// +// /// +// /// Это, к сожалению, на Питоне, +// /// и там используется местная библиотека numpy, так что не подходит. +// /// +// /// +// /// Вот тут +// /// примеры функций типа bwdist и прочих. До конца не смотрел, возможно, что-то и подходит.К сожалению, тоже на Питоне, и также используется numpy. +// /// +// /// +// /// Это +// /// просто документация, описание DistanceTransformWithLabels. К сожалению, написано очень скудно, и примеров нет. +// /// +// /// +// /// Вот тут +// /// примеры функций, но похоже, просто скопировано с докементации, примеров, к сожалению, нет (ну они есть, но не те). +// /// +// /// +// /// +// /// +// private void DistanceTransform(Mat input, int contur) +// { +// var distanceTransform = input.EmptyClone(); +// //Cv2.DistanceTransform(dst1, dst2, DistanceTypes.L2, DistanceTransformMasks.Precise); +// var labels = input.EmptyClone(); +// Cv2.DistanceTransformWithLabels(input, distanceTransform, labels, DistanceTypes.L2, DistanceTransformMasks.Precise, +// //Cv2.DistanceTransformWithLabels(dst1, dst2, labelIndexes, DistanceTypes.L2, DistanceTransformMasks.Mask3, +// DistanceTransformLabelTypes.Pixel); +// distanceTransform.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contur}_distance_transform.png")); +// +// //labels.PushBack(-1); +// var k = 1; +// for (int i = 0; i < distanceTransform.Height; i++) +// { +// for (int j = 0; j < distanceTransform.Width; j++) +// { +// var px = distanceTransform.At(i, j); +// if (px == 0) +// { +// //labels.PushBack(new Vec2i(i, j)); +// } +// else { } +// var p = labels.At(i, j); +// +// var p_x = p % labels.Width; +// var p_y = p / labels.Width; +// +// var p1 = labels.At(i, j); +// var p2 = labels.At(i, j); +// var p3 = labels.At(i, j); +// var p4 = labels.At(i, j); +// +// } +// } +// +// labels.SaveImage(Path.Combine(_outputDirectory, $"GS_TEST{contur}_distance_transform_labels.png")); +// } +// +// //private void B() +// //{ +// // using (var s = new FileStream(filePath, FileMode.Open, FileAccess.Read)) +// // { +// +// // //var m = new Mat(); +// // //var m = Mat.FromStream(s, ImreadModes.Color); +// // var m = Mat.FromStream(s, ImreadModes.Grayscale); +// // var result = new List(); +// +// // var nonZero = m.FindNonZero(); +// // var colors = new List(); +// // var total = nonZero.Total(); +// // for (int i = 0; i < total; i++) +// // { +// // var p = nonZero.At(i); +// // var color = m.At(p.X, p.Y); +// // if (!colors.Contains(color) && color != 0) colors.Add(color); +// // } +// +// +// // //___ +// +// // //var c = m.InRange(new Scalar(color), new Scalar(color)); +// +// // var c = m.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); +// +// // var closing = c.EmptyClone(); +// +// // //var contours0 = closing.FindContoursAsMat(RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// // //Cv2.MorphologyEx(c, closing, MorphTypes.Close, contours0[0]); +// // Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); +// +// // //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); +// // Point[][] contours1; +// // HierarchyIndex[] hierarchy; +// // //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// // closing.FindContours(out contours1, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// +// // var contNum = 0; +// +// // var aaaaa = contours1.Count(); +// +// // var aaa = hierarchy.Where(q => q.Parent < 0).ToList(); +// +// // //var boundingRect = Cv2.BoundingRect(contours1[0]); +// +// // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); +// // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // //Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, 128, 20 * contNum), -1, +// // // LineTypes.Link8, maxLevel: -1); +// // //Cv2.DrawContours(dst0, contours1, 0, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, maxLevel: -1); +// // //Cv2.DrawContours(dst0, contours1, 0, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, maxLevel: int.MaxValue); +// // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, +// // hierarchy: hierarchy, +// // //maxLevel: -1); +// // maxLevel: int.MaxValue); +// // //colorSeparatedMats.Add(c); +// // dst0.SaveImage(@$"F:\GS_TEST_c{contNum}.png"); +// +// // var boundingRect = Cv2.BoundingRect(contours1[0]); +// +// // //var dst1 = Mat.Zeros(boundingRect.Size, MatType.CV_8UC1).ToMat(); +// // var dst1 = Mat.Zeros(m.Size(), MatType.CV_8UC1).ToMat(); +// // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // //Cv2.DrawContours(dst1, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, +// // Cv2.DrawContours(dst1, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 255 ? 255 : 20 * contNum), -1, +// // hierarchy: hierarchy, +// // //maxLevel: -1); +// // maxLevel: int.MaxValue); +// // //colorSeparatedMats.Add(c); +// // dst1.SaveImage(@$"F:\GS_TEST_cc{contNum}.png"); +// +// // var dst2 = dst1.EmptyClone(); +// // //Cv2.DistanceTransform(dst1, dst2, DistanceTypes.L2, DistanceTransformMasks.Precise); +// // var labels = dst1.EmptyClone(); +// // Cv2.DistanceTransformWithLabels(dst1, dst2, labels, DistanceTypes.L2, DistanceTransformMasks.Precise, +// // //Cv2.DistanceTransformWithLabels(dst1, dst2, labelIndexes, DistanceTypes.L2, DistanceTransformMasks.Mask3, +// // DistanceTransformLabelTypes.Pixel); +// // dst2.SaveImage(@$"F:\GS_TEST_ccc{contNum}.png"); +// +// // //labels.PushBack(-1); +// // var k = 1; +// // for (int i = 0; i < dst2.Height; i++) +// // //for (int i = boundingRect.Top; i < boundingRect.Height; i++) +// // { +// // for (int j = 0; j < dst2.Width; j++) +// // //for (int j = boundingRect.Left; j < boundingRect.Width; j++) +// // { +// // var px = dst2.At(i, j); +// // if (px == 0) +// // { +// // //labels.PushBack(new Vec2i(i, j)); +// // } +// // else { } +// // var p = labels.At(i, j); +// +// // var p_x = p % labels.Width; +// // var p_y = p / labels.Width; +// +// // var p1 = labels.At(i, j); +// // var p2 = labels.At(i, j); +// // var p3 = labels.At(i, j); +// // var p4 = labels.At(i, j); +// +// // } +// // } +// +// // labels.SaveImage(@$"F:\GS_TEST_cccc{contNum}.png"); +// +// +// +// +// // //var un = labels.dis +// +// // Console.ReadLine(); +// // //foreach (var contour in contours1) +// // //{ +// // // //if (hierarchy[contNum].Parent < 0) +// // // //{ +// // // // var boundingRect = Cv2.BoundingRect(contour); +// +// // // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); +// // // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, +// // // // LineTypes.Link8, maxLevel: -1); +// // // // //colorSeparatedMats.Add(c); +// // // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); +// // // //} +// +// // // var boundingRect = Cv2.BoundingRect(contour); +// +// // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); +// // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // //Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, 128, 20 * contNum), -1, +// // // // LineTypes.Link8, maxLevel: -1); +// // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(hierarchy[contNum].Parent < 0 ? 255 : 0, 128, 20 * contNum), -1, +// // // LineTypes.Link8, maxLevel: -1); +// // // //colorSeparatedMats.Add(c); +// // // dst0.SaveImage(@$"F:\GS_TEST_c{contNum}.png"); +// +// +// +// +// // // //var mean = Cv2.Mean(c, mask); +// // // //var notBlackContour = Cv2.Mean(c, mask)[0] > 0; +// +// // // //var averageInsideColor = +// +// +// // // //var c1 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // //var dst0 = c1.EmptyClone(); +// // // //Cv2.BitwiseAnd(c1, c1, dst0, c.GetRectSubPix(boundingRect.Size, new Point2f(boundingRect.Left + (boundingRect.Width / 2), boundingRect.Top + (boundingRect.Height / 2)))); +// +// // // //var c1 = c.CvtColor(ColorConversionCodes.GRAY2BGR); +// // // //var m1 = c1.InRange(new Scalar(0), new Scalar(255)); +// // // //Cv2.FloodFill(c1, m1, new Point(0, 0), new Scalar(0, color, 20 * cNum)); +// +// // // //dst0.SaveImage(@$"F:\GS_TEST_cc{cNum}_{contNum}.png"); +// // // contNum++; +// // //} +// +// // //___ +// +// // //var colorSeparatedMats = new List(); +// // //var cNum = 0; +// // //foreach (var color in colors) +// // //{ +// +// +// // // //var c = m.InRange(new Scalar(color), new Scalar(color)); +// +// // // var c = m.Threshold(0, 255, ThresholdTypes.Binary & ThresholdTypes.Otsu); +// +// // // var closing = c.EmptyClone(); +// +// // // //var contours0 = closing.FindContoursAsMat(RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// // // //Cv2.MorphologyEx(c, closing, MorphTypes.Close, contours0[0]); +// // // Cv2.MorphologyEx(c, closing, MorphTypes.Close, null); +// +// // // //var contours0 = closing.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); +// // // Point[][] contours1; +// // // HierarchyIndex[] hierarchy; +// // // //closing.FindContours(out contours1, , RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// // // closing.FindContours(out contours1, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone); +// +// // // var contNum = 0; +// +// // // var aaaaa = contours1.Count(); +// +// // // var a01 = hierarchy[0].Previous; +// // // var a02 = hierarchy[0].Next; +// // // var a03 = hierarchy[1].Previous; +// // // var a04 = hierarchy[1].Next; +// +// // // var aaa = hierarchy.Where(q => q.Parent < 0).ToList(); +// +// // // foreach (var contour in contours1) +// // // { +// // // //if (hierarchy[contNum].Parent < 0) +// // // //{ +// // // // var boundingRect = Cv2.BoundingRect(contour); +// +// // // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); +// // // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, +// // // // LineTypes.Link8, maxLevel: -1); +// // // // //colorSeparatedMats.Add(c); +// // // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); +// // // //} +// +// // // var boundingRect = Cv2.BoundingRect(contour); +// +// // // var dst0 = Mat.Zeros(m.Size(), MatType.CV_8UC3).ToMat(); +// // // //var dst0 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // Cv2.DrawContours(dst0, contours1, contNum, new Scalar(0, color, 20 * cNum), -1, +// // // LineTypes.Link8, maxLevel: -1); +// // // //colorSeparatedMats.Add(c); +// // // dst0.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); +// +// +// +// +// // // //var mean = Cv2.Mean(c, mask); +// // // //var notBlackContour = Cv2.Mean(c, mask)[0] > 0; +// +// // // //var averageInsideColor = +// +// +// // // //var c1 = new Mat(boundingRect.Size, MatType.CV_8UC3, new Scalar(0, color, 20 * cNum)); +// // // //var dst0 = c1.EmptyClone(); +// // // //Cv2.BitwiseAnd(c1, c1, dst0, c.GetRectSubPix(boundingRect.Size, new Point2f(boundingRect.Left + (boundingRect.Width / 2), boundingRect.Top + (boundingRect.Height / 2)))); +// +// // // //var c1 = c.CvtColor(ColorConversionCodes.GRAY2BGR); +// // // //var m1 = c1.InRange(new Scalar(0), new Scalar(255)); +// // // //Cv2.FloodFill(c1, m1, new Point(0, 0), new Scalar(0, color, 20 * cNum)); +// +// // // //dst0.SaveImage(@$"F:\GS_TEST_cc{cNum}_{contNum}.png"); +// // // contNum++; +// // // } +// +// // // //colorSeparatedMats.Add(c); +// // // //c.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); +// +// // // cNum++; +// // //} +// +// // //foreach (var cMat in colorSeparatedMats) +// // //{ +// // // cNum++; +// // //} +// // //c.SaveImage(@$"F:\GS_TEST_c{cNum}.png"); +// +// // var contours = m.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); +// // var a = contours.Count(); +// // //var c = contours.Where(q => q.).Count(); +// // //var nbl = m.FindNonZero(); +// // //var contours1 = nbl.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone); +// // //var b = contours1.Count(); +// // var biggestContourRect = Cv2.BoundingRect(contours[0]); +// // Mat dst = new Mat(); +// // Cv2.Rectangle(dst, +// // new Point(biggestContourRect.X, biggestContourRect.Y), +// // new Point(biggestContourRect.X + biggestContourRect.Width, biggestContourRect.Y + biggestContourRect.Height), +// // new Scalar(255, 255, 255), 2); +// // } +// //} +// } +// } diff --git a/Make3.Renderer/test1.txt b/Make3.Renderer/test1.txt index 628ff00..63596b9 100644 --- a/Make3.Renderer/test1.txt +++ b/Make3.Renderer/test1.txt @@ -1,26 +1,30 @@ - - - - - .:^. ::. - .:~77??: !??7!^: - :!7??????: !??????7~. - :7?????????: !?????????~. - .!???????????: !???????????^ - .7????????????: !????????????~ - .7?????????????: !?????????????~ - !??????????????: !??????????????: - .???????????????: !??????????????! - ^???????????????: !??????????????7 - :???????????????~ .7??????????????7 - .????????????????~. .:7???????????????! - ~?????????????????77!7??????????????????. - 7?????????????????????????????????????^ - .7J??????????????????????????????????^ - ~?J?????????????????????????????J7: - .!?JJ???????????????????????JJ7^ - .~7?JJJ???????????????JJJ?!: - .^!7???JJJJJJJJ????7~:. - ..:^^~~~~~^^:. - - +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB +PPPP5YYJ?7!!~^^::::^^~~!77?JJYYYYYJJJ???777777?JY5PGGB###&&&##BB \ No newline at end of file diff --git a/Make3.Renderer/test2-mask.txt b/Make3.Renderer/test2-mask.txt index e69de29..9a768fd 100644 --- a/Make3.Renderer/test2-mask.txt +++ b/Make3.Renderer/test2-mask.txt @@ -0,0 +1,30 @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@55@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Y 5@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@J J@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@7 ?@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@#~ !&@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@B: ^B@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@B. :B@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@#: :&@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@J Y@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@J Y@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@&#####&@@@@@@@@@&^ ^&@@@@@@@@&&#####&@@@@@@@ +@@@@BJ!:.....::~7YG&@@@@#^ ^#@@@@&GY7~:......:!Y#@@@@ +@@#7 :?B@@@&~ !&@@@B?: 7#@@ +@B: 7#@@@7 7@@@B! :#@ +@! .J@@@7 7@@@J. 7@ +@~ 7@@@~ !@@@7 ~@ +@Y :7YPGBBBBG5?^ Y@@#. :#@@J ~?5GBBBBGPY7: Y@ +@@~ ^P&@@@@@@@@@@@@B! .&@@J Y@@#. 7B@@@@@@@@@@@@&P^ !@@ +@@&7 7@@@@@@@@@@@@@@@@@J 5@@B #@@Y Y@@@@@@@@@@@@@@@@&! 7&@@ +@@@@5::&@@@@@@@@@@@@@@@&&B ?&&B..#&&? B&&@@@@@@@@@@@@@@@&.^P@@@@ +@@@@@&B@@@@@@@@@@@@@@@@&&B.G&&B..#&&P.B&&@@@@@@@@@@@@@@@@B&@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@5^@@@# .#@@&:P@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@PJG@@@#:P@@@5 P@@@5^#@@@PYP@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@&~ G@5^5@@@#: :#@@@5^P@P !@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@&^ ^77#@@@@J Y@@@@B77: ~@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@&PY5G#@@@@@@#: ^&@@@@@@#G5YP&@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G .B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PG@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \ No newline at end of file diff --git a/Make3.Renderer/test2.txt b/Make3.Renderer/test2.txt index e69de29..c64be50 100644 --- a/Make3.Renderer/test2.txt +++ b/Make3.Renderer/test2.txt @@ -0,0 +1,30 @@ + 7! JY Y&:::::#@#YJ?!?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +.JJ^~5&^.:P@&&&&&@@B!7JYP@@@@&5YYYYYYYYYYY#@@@@@@@@@@@@@@@@@@@@@ +B@@@@@@PJ5P5YYJJ?P@@@@@@@@@@@# P@@@@@BYYG@&5YP@@@@@@@ +@@@@@@P. ?@@@@@@@@@@# P@@@@5 !: !@@@@@@ +@@@@@5 ^B@@@@@@@@&!~~~~~~~~~~~B@@@@7 .&@@@@@ +@@@@5 .Y@@@@@@@@@@@@@@@@@@@@@@@@@&?: .!B@@@@@@ +@@@5 .#@@@@@@@@@@@@@@@@@@@@@@@@@@&G! :5#@@@@@@@@ +@@@#7 :B@@@@@@@@@#B@@@@@@@@@@@@@@@@@@@G&@@@@@@@@@@ +@@@@@5. :#@@@&YB@@@&^:&@@@BY&@@@@@@@@@@@@@#GGB#@@@@@@ +@@@@@@#~ ^#@@@@@! !B@P 5@#7 ~@@@@@@@@@@@5!. ^?B@@@ +@@@@@@@@YJJYY55PPP&@GG#&@&~ .P5 YP. ^&@&#GG@@@@#^ !PBBGJ. Y@@ +@@@@@@@@@@@@@@@@@@@@G!..~JP! ~ ~ ~PJ~..!G@@@@! 5@@@@@@#: B@ +@@@@@@@@@@@@@@@@@@@@@@#Y~. :. .: .~Y#@@@@@@~ P@@@@@@&: G@ +@@@@@@@@@@@@@@@@@@#5J7!!!~: :~!!!7?5#@@G. ?G##BY^ ?@@ +@@@@@@@@@@@@@@@@@@#5J?77!~: :~!77?J5#@@@#?: .~P@@@ +@@@@@@@@@@@@@@@@@@@@@@#J~. :. .: .~JB@@@@@@@@@&BPYY5G#@@@@@ +@@@@@@&#GGG#&@@@@@@@G!..~JP~ ~ ~ ~PJ~:.~G@@@&#B#@@@@@@@@@@@@@ +@@@@P!: :7G@@@@@BG#&@&^ .P5 YP. ^&@&#GB@#7: .^5@@@@@@@@@@@ +@@&! 7@@@@@@@@@! 7#@P 5@#7 ~@@@@@@! G@@@@@@@@@@ +@@J Y@@@@@@@@5B@@@&^:&@@@#Y&@@@@@5 ^#@@@@@@@@@@ +@@J J@@@@@@@@@@@@@@##@@@@@@@@@@@@@BY77?P&@@@@@@@@@@@ +@@&~ ~&@@@@@@J^^^^^^^~~J@@@@@@#BB#@@@@@@@@@@@@@@@@@@@@ +@@@@Y^. .~5@@@@@@&! !&@@@JYGGPYP@@@@@@@@@@@&BPPB&@ +@@@@@@#G555G#@@@@@@@B^ ^#@@?P@@@@?5@@@@@#GJ!^..^75&@ +@@@@@@@@@@@@@@@@@@@P .G@@5J5PG7~Y5J!^..^7YG&@@@@@ +@@@@@@@@@@@@@@@@@@@B^ :B@@@GJ!!?! ^5#@@@@@@@@@ +@@@@@@@@@@@@@@@@@@@@&! !&@@GJ5B&@77##B5?^..:!YG&@@@@ +@@@@@@@@@@@@@@@@@@@@@@? J@@@&~#@@@B7#@@@@@@&GY7^::~Y#@ +@@@@@@@@@@@@@@@@@@@@@@@57777777!!P@@@@@GYY55P&@@@@@@@@@@@@&BB#@@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \ No newline at end of file